jupyterhub-1219 Enhancement: automatically create a directory for the user after successful login

This commit is contained in:
CRegenschein
2017-07-20 14:02:13 +02:00
committed by Min RK
parent 0fd1a95405
commit 72550725da
5 changed files with 242 additions and 2 deletions

View File

@@ -0,0 +1,130 @@
# Bootstrapping your users
Before spawning a notebook to the user, it could be useful to
do some preparation work in a bootstrapping process.
Common use cases are:
*Providing writeable storage for LDAP users*
Your Jupyterhub is configured to use the LDAPAuthenticator and DockerSpawer.
* The user has no file directory on the host since your are using LDAP.
* When a user has no directory and DockerSpawner wants to mount a volume,
the spawner will use docker to create a directory.
Since the docker daemon is running as root, the generated directory for the volume
mount will not be writeable by the `jovyan` user inside of the container.
For the directory to be useful to the user, the permissions on the directory
need to be modified for the user to have write access.
*Prepopulating Content*
Another use would be to copy initial content, such as tutorial files or reference
material, into the user's space when a notebook server is newly spawned.
You can define your own bootstrap process by implementing a `pre_spawn_hook` on any spawner.
The Spawner itself is passed as parameter to your hook and you can easily get the contextual information out of the spawning process.
If you implement a hook, make sure that it is *idempotent*. It will be executed every time
a notebook server is spawned to the user. That means you should somehow
ensure that things which should run only once are not running again and again.
For example, before you create a directory, check if it exists.
Bootstrapping examples:
### Example #1 - Create a user directory
Create a directory for the user, if none exists
```python
# in jupyterhub_config.py
import os
def create_dir_hook(spawner):
username = spawner.user.name # get the username
volume_path = os.path.join('/volumes/jupyterhub', username)
if not os.path.exists(volume_path):
# create a directory with umask 0755
# hub and container user must have the same UID to be writeable
# still readable by other users on the system
os.mkdir(volume_path, 0o755)
# now do whatever you think your user needs
# ...
pass
# attach the hook function to the spawner
c.Spawner.pre_spawn_hook = create_dir_hook
```
### Example #2 - Run a shell script
You can specify a plain ole' shell script (or any other executable) to be run
by the bootstrap process.
For example, you can execute a shell script and as first parameter pass the name
of the user:
```python
# in jupyterhub_config.py
from subprocess import check_call
import os
def my_script_hook(spawner):
username = spawner.user.name # get the username
script = os.path.join(os.path.dirname(__file__), 'bootstrap.sh')
check_call([script, username])
# attach the hook function to the spawner
c.Spawner.pre_spawn_hook = my_script_hook
```
Here's an example on what you could do in your shell script. See also
`/examples/bootstrap-script/`
```bash
#!/bin/bash
# Bootstrap example script
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
# - The first parameter for the Bootstrap Script is the USER.
USER=$1
if ["$USER" == ""]; then
exit 1
fi
# ----------------------------------------------------------------------------
# This example script will do the following:
# - create one directory for the user $USER in a BASE_DIRECTORY (see below)
# - create a "tutorials" directory within and download and unzip
# the PythonDataScienceHandbook from GitHub
# Start the Bootstrap Process
echo "bootstrap process running for user $USER ..."
# Base Directory: All Directories for the user will be below this point
BASE_DIRECTORY=/volumes/jupyterhub/
# User Directory: That's the private directory for the user to be created, if none exists
USER_DIRECTORY=$BASE_DIRECTORY/$USER
if [ -d "$USER_DIRECTORY" ]; then
echo "...directory for user already exists. skipped"
exit 0 # all good. nothing to do.
else
echo "...creating a directory for the user: $USER_DIRECTORY"
mkdir $USER_DIRECTORY
echo "...initial content loading for user ..."
mkdir $USER_DIRECTORY/tutorials
cd $USER_DIRECTORY/tutorials
wget https://github.com/jakevdp/PythonDataScienceHandbook/archive/master.zip
unzip -o master.zip
rm master.zip
fi
exit 0
```

View File

@@ -0,0 +1,48 @@
#!/bin/bash
# Bootstrap example script
# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
# - The first parameter for the Bootstrap Script is the USER.
USER=$1
if ["$USER" == ""]; then
exit 1
fi
# ----------------------------------------------------------------------------
# This example script will do the following:
# - create one directory for the user $USER in a BASE_DIRECTORY (see below)
# - create a "tutorials" directory within and download and unzip the PythonDataScienceHandbook from GitHub
# Start the Bootstrap Process
echo "bootstrap process running for user $USER ..."
# Base Directory: All Directories for the user will be below this point
BASE_DIRECTORY=/volumes/jupyterhub
# User Directory: That's the private directory for the user to be created, if none exists
USER_DIRECTORY=$BASE_DIRECTORY/$USER
if [ -d "$USER_DIRECTORY" ]; then
echo "...directory for user already exists. skipped"
exit 0 # all good. nothing to do.
else
echo "...creating a directory for the user: $USER_DIRECTORY"
mkdir $USER_DIRECTORY
# mkdir did not succeed?
if [ $? -ne 0 ] ; then
exit 1
fi
echo "...initial content loading for user ..."
mkdir $USER_DIRECTORY/tutorials
cd $USER_DIRECTORY/tutorials
wget https://github.com/jakevdp/PythonDataScienceHandbook/archive/master.zip
unzip -o master.zip
rm master.zip
fi
exit 0

View File

@@ -0,0 +1,26 @@
# Example for a Spawner.pre_spawn_hook
# create a directory for the user before the spawner starts
import os
def create_dir_hook(spawner):
username = spawner.user.name # get the username
volume_path = os.path.join('/volumes/jupyterhub', username)
if not os.path.exists(volume_path):
os.mkdir(volume_path, 0o755)
# now do whatever you think your user needs
# ...
# attach the hook function to the spawner
c.Spawner.pre_spawn_hook = create_dir_hook
# Use the DockerSpawner to serve your users' notebooks
c.JupyterHub.spawner_class = 'dockerspawner.DockerSpawner'
from jupyter_client.localinterfaces import public_ips
c.JupyterHub.hub_ip = public_ips()[0]
c.DockerSpawner.hub_ip_connect = public_ips()[0]
c.DockerSpawner.container_ip = "0.0.0.0"
# You can now mount the volume to the docker container as we've
# made sure the directory exists
c.DockerSpawner.volumes = { '/volumes/jupyterhub/{username}/': '/home/jovyan/work' }