5.1 KiB
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.
Similarly, there may be cases where you would like to clean up after a spawner stops.
You may implement a post_stop_hook
that is always executed after the spawner stops.
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
# 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 mkhomedir_helper
Many Linux distributions provide a script that is responsible for user homedir bootstrapping: /sbin/mkhomedir_helper
. To make use of it, you can use
def create_dir_hook(spawner):
username = spawner.user.name
if not os.path.exists(os.path.join('/volumes/jupyterhub', username)):
subprocess.call(["sudo", "/sbin/mkhomedir_helper", spawner.user.name])
# attach the hook function to the spawner
c.Spawner.pre_spawn_hook = create_dir_hook
and make sure to add
jupyterhub ALL = (root) NOPASSWD: /sbin/mkhomedir_helper
in a new file in /etc/sudoers.d
, or simply in /etc/sudoers
.
All new home directories will be created from /etc/skel
, so make sure to place any custom homedir-contents in there.
Example #3 - 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:
# 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/
#!/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