jupyterhub-deploy-docker/jupyterhub_config.py
Michael Pilosov fd5d5111db
Carme (#5)
* changed settings for personal hub.

* htop added to singleuser.

* better singleuser.

* config updated.

* bad merge.

* standard updates.

* updates to builds.

* updated.

* updated path.

* loc

* stable build!

* lab enabled.

* dockerspawner bug fix.

* stable upgrade.

* remove env ending.

* minimal installation script with python 2.

* git enabled.

* env.

* upgraded hub for hartke.

* setting cull idle servers.

* default upgrade now working.

* options.

* tweaks for personal settings.

* personalization.

* update.

* attempting full latex build.

* lab build.

* typo

* separated layers.

* got a fancy environment working

* allspark.

* updated personal hub settings.

* allspark done.

* updates. tflow.

* octave deps.

* rstudio and redundancy cleanup.

* WIP

* everything WORKS!!!

* stable state with updates.

* stuff to improve the experience.

* merge.

* working state.

* settings.

* settings for carme, successful build

* settings tweaked for lab, sudo, debugger

* working on carme

* upgrade to python 3.9 and remove conda install steps

* updates

* major cleanup (#6)

* update for slalom

* disable options and grant sudo to all

* vscode launcher

* default url

* pkgs

* config

* updates

* typo

* update

* dont mount extras

* update info in config

* adding install scripts, cleaning up dockerfile

* refactor class

* clean up logic

* carme

* upgrades

* update

* swap order

* spacing

* updates

* paths

* rearranging

* cleanup

* lsp

* cleanup

* culler service update

* unecessary option

* more unecessary args

* Update .env

* monitoring

Co-authored-by: mathematicalmichael <mpilosov@gmail.com>
2021-11-27 19:01:30 -07:00

248 lines
9.4 KiB
Python
Executable File

# Copyright (c) Jupyter Development Team.
# Distributed under the terms of the Modified BSD License.
# Major edits by MathematicalMichael(.com) 02-2019
# Configuration file for JupyterHub
import os
import sys
from subprocess import check_call
pwd = os.path.dirname(__file__)
c = get_config()
### Helper scripts
def create_group_map(filename='userlist') -> 'Dict[str, List[str]]':
"""
Parses text file to assign users to groups and creates
a dictionary where keys are usernames and values are the list
of group names to which they belong.
You can use group membership to define policies for shared volumes
or use an `admin` group to determine which users get root permissions.
Note that updates to the userlist require restarts for the jupyerhub to
take effect. This can be inconvenient as an interruption to existing users.
For this reason, we suggest not using `userlist` to manage
shared volumes but rather setting up an external filesystem on the network
and managing access through that (give users instructions on how to mount them
as folders inside their containerized environments), or perhaps opt for
object storage like s3 and distribute user-keys / credentials and rely on
CLI or programmatic file access.
"""
group_map = {}
# TODO: check if file exists and return empty dictionary if it does not.
with open(os.path.join(pwd, filename)) as f:
for line in f:
if not line:
continue
# each line of file is user: group_1 group_2 ...
parts = line.split()
# in case of newline at the end of userlist file
if len(parts) == 0:
continue
user_name = parts[0]
group_map[user_name] = []
for i in range(1,len(parts)):
group_name = parts.pop()
group_map[user_name].append(group_name)
return group_map
def create_volume_mount(group_id='group', mode='ro', nb_user='jovyan') -> 'Dict[str, Dict[str, str]]':
volumes = {}
volume_name = f'shared-{group_id}'
volume_config = {
'bind': f'/home/{nb_user}/{volume_name}',
'mode': mode,
}
volumes[volume_name] = volume_config
return volumes
# We rely on environment variables to configure JupyterHub so that we
# avoid having to rebuild the JupyterHub container every time we change a
# configuration parameter.
HUB_NAME = os.environ['HUB_NAME']
DEFAULT_IMAGE = f'{HUB_NAME}-user'
GROUP_MAP = create_group_map('userlist')
# Allow admin users to log into other single-user servers (e.g. for debugging, testing)? As a courtesy, you should make sure your users know if admin_access is enabled.
c.JupyterHub.admin_access = True
## Allow named single-user servers per user
c.JupyterHub.allow_named_servers = True
# Allow admin to access other users' containers
c.NotebookApp.allow_remote_access = True
# Optional list of images
ENABLE_DROPDOWN = True
IMAGE_WHITELIST= {
'default': f"{HUB_NAME}-user",
'scipy-notebook': "jupyter/scipy-notebook",
'tensorflow-notebook': "jupyter/tensorflow-notebook",
'r-notebook': 'jupyter/r-notebook',
'base-notebook': "jupyter/base-notebook",
}
# Spawn single-user servers as Docker containers
from dockerspawner import DockerSpawner
class MyDockerSpawner(DockerSpawner):
def start(self):
group_list = GROUP_MAP.get(self.user.name, [])
self.update_volumes(group_list)
# if 'admin' in group_list:
# self.mount_config_files()
# self.grant_sudo()
# self.limit_resources()
self.enable_lab()
self.grant_sudo() # grants sudo to all users!!!
return super().start()
def grant_sudo(self):
"""
Grants sudo permission to current user being spawned.
"""
self.environment['GRANT_SUDO'] = "1"
self.extra_create_kwargs = {'user': 'root'}
def enable_lab(self):
"""
Sets Jupyterlab as the default environment which users see.
"""
self.environment['JUPYTER_ENABLE_LAB'] = 'yes'
self.default_url = '/lab'
def update_volumes(self, group_list):
for group_id in group_list:
mode = 'rw' if 'admin' in group_list else 'ro'
volume = create_volume_mount(group_id, mode, 'jovyan')
self.volumes.update(volume)
def limit_resources(self, mem_limit='8G', cpu_limit=1.0):
self.mem_limit = mem_limit
self.cpu_limit = cpu_limit
def mount_config_files(self, username='jovyan'):
"""
Allows editing of `jupyterhub_config.py` + `userlist` from
within the container but relies on using `Shut Down` from
the admin panel + docker automatically restarting the hub
in order for changes to take effect. If you make a mistake,
your hub will become unavailable and you will need to edit
it by logging into the server hosting the jupyterhub app.
"""
self.volumes['%s/userlist'%(os.environ['HUB_LOC'])] = \
{ 'bind': f'/home/{username}/userlist', 'mode': 'rw' }
self.volumes['%s/jupyterhub_config.py'%(os.environ['HUB_LOC'])] = \
{ 'bind': f'/home/{username}/jupyterhub_config.py', 'mode': 'rw' }
c.JupyterHub.spawner_class = MyDockerSpawner
c.DockerSpawner.image = '%s-user'%HUB_NAME
c.DockerSpawner.name_template = '%s-{username}-{servername}-{imagename}'%HUB_NAME
if ENABLE_DROPDOWN:
c.DockerSpawner.allowed_images = IMAGE_WHITELIST
# JupyterHub requires a single-user instance of the Notebook server, so we
# default to using the `start-singleuser.sh` script included in the
# jupyter/docker-stacks *-notebook images as the Docker run command when
# spawning containers. Optionally, you can override the Docker run command
# using the DOCKER_SPAWN_CMD environment variable.
spawn_cmd = os.environ.get('DOCKER_SPAWN_CMD', "start-singleuser.sh")
c.DockerSpawner.extra_create_kwargs.update({ 'command': spawn_cmd })
# Connect containers to this Docker network
network_name = '%s-network'%HUB_NAME
c.DockerSpawner.use_internal_ip = True
c.DockerSpawner.network_name = network_name
# Pass the network name as argument to spawned containers
c.DockerSpawner.extra_host_config = { 'network_mode': network_name }
# Explicitly set notebook directory because we'll be mounting a host volume to
# it. Most jupyter/docker-stacks *-notebook images run the Notebook server as
# user `jovyan`, and set the notebook directory to `/home/jovyan/work`.
# We follow the same convention.
notebook_dir = os.environ.get('DOCKER_NOTEBOOK_DIR') or '/home/jovyan/work'
#c.DockerSpawner.notebook_dir = notebook_dir
# Mount the real user's Docker volume on the host to the notebook user's
# notebook directory in the container
c.DockerSpawner.volumes = { 'hub-user-{username}': notebook_dir }
# volume_driver is no longer a keyword argument to create_container()
# c.DockerSpawner.extra_create_kwargs.update({ 'volume_driver': 'local' })
# Remove containers once they are stopped
c.DockerSpawner.remove = True
# For debugging arguments passed to spawned containers
c.DockerSpawner.debug = True
# User containers will access hub by container name on the Docker network
c.JupyterHub.hub_ip = HUB_NAME
# The hub will be hosted at example.com/HUB_NAME/
c.JupyterHub.base_url = u'/%s/'%HUB_NAME
#c.JupyterHub.hub_port = 8001
## Authentication
# Whitlelist users and admins
c.Authenticator.allowed_users = whitelist = set()
c.Authenticator.admin_users = admin = set()
# add default user so that first-time log in is easy.
admin.add('hub-admin')
for name in GROUP_MAP:
if 'admin' in GROUP_MAP[name]:
admin.add(name)
else:
whitelist.add(name)
# Authenticate users with GitHub OAuth
# c.JupyterHub.authenticator_class = 'oauthenticator.GitHubOAuthenticator'
# c.GitHubOAuthenticator.oauth_callback_url = os.environ['OAUTH_CALLBACK_URL']
# Authenticate with thedataincubator/jupyterhub-hashauthenticator
c.JupyterHub.authenticator_class = 'hashauthenticator.HashAuthenticator'
# You can generate a good "secret key" by running `openssl rand -hex 32` in terminal.
# it is recommended to do this from time-to-time to change passwords (including changing their length)
c.HashAuthenticator.secret_key = os.environ['HASH_SECRET_KEY'] # Defaults to ''
c.HashAuthenticator.password_length = int(os.environ['PASSWORD_LENGTH']) # Defaults to 6
# Can find your password by looking at `hashauthpw --length 10 [username] [key]`
# If the `show_logins` option is set to `True`, a CSV file containing
#login names and passwords will be served (to admins only) at `/hub/login_list`.
c.HashAuthenticator.show_logins = True # Optional, defaults to False
# TLS config
#c.JupyterHub.port = 8000
#c.JupyterHub.ssl_key = os.environ['SSL_KEY']
#c.JupyterHub.ssl_cert = os.environ['SSL_CERT']
### Database Interaction - cookies, db for jupyterhub
# Persist hub data on volume mounted inside container
data_dir = '/data' # DATA_VOLUME_CONTAINER
c.JupyterHub.cookie_secret_file = os.path.join(data_dir,
'jupyterhub_cookie_secret')
c.JupyterHub.db_url = 'postgresql://postgres:{password}@{host}/{db}'.format(
host=os.environ['POSTGRES_HOST'],
password=os.environ['POSTGRES_PASSWORD'],
db=HUB_NAME,
)
# https://github.com/jupyterhub/jupyterhub-idle-culler
c.JupyterHub.services = [
{
"name": "jupyterhub-idle-culler-service",
"command": [
sys.executable,
"-m", "jupyterhub_idle_culler",
"--timeout=3600",
],
"admin": True,
}
]