# 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
```
