mirror of
https://github.com/jupyterhub/jupyterhub.git
synced 2025-10-07 18:14:10 +00:00
158 lines
5.1 KiB
Markdown
158 lines
5.1 KiB
Markdown
# 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 you 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 a 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
|
|
|
|
```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 `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
|
|
|
|
```python
|
|
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:
|
|
|
|
```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/HEAD.zip
|
|
unzip -o HEAD.zip
|
|
rm HEAD.zip
|
|
fi
|
|
|
|
exit 0
|
|
```
|