mirror of
https://github.com/jupyterhub/the-littlest-jupyterhub.git
synced 2025-12-18 21:54:05 +08:00
Load user lists & auth config from a YAML file
- Load config only once at startup. A lot of jupyterhub config (like user lists) take effect only at startup, so live reload is not super useful. It will make the software more complex, so let's not do it. - Add pyyaml as a dependency of tljh. - Remove escapism dependency since it is not actually used
This commit is contained in:
2
setup.py
2
setup.py
@@ -11,6 +11,6 @@ setup(
|
|||||||
packages=find_packages(),
|
packages=find_packages(),
|
||||||
include_package_data=True,
|
include_package_data=True,
|
||||||
install_requires=[
|
install_requires=[
|
||||||
'escapism==1.*'
|
'pyyaml==4.*'
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|||||||
93
tljh/configurer.py
Normal file
93
tljh/configurer.py
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
"""
|
||||||
|
Parse YAML config file & update JupyterHub config.
|
||||||
|
|
||||||
|
Config should never append or mutate, only set. Functions here could
|
||||||
|
be called many times per lifetime of a jupyterhub.
|
||||||
|
|
||||||
|
Traitlets that modify the startup of JupyterHub should not be here.
|
||||||
|
FIXME: A strong feeling that JSON Schema should be involved somehow.
|
||||||
|
"""
|
||||||
|
import copy
|
||||||
|
import os
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
# Default configuration for tljh
|
||||||
|
# User provided config is merged into this
|
||||||
|
default = {
|
||||||
|
'auth': {
|
||||||
|
'type': 'dummy',
|
||||||
|
'dummy': {}
|
||||||
|
},
|
||||||
|
'users': {
|
||||||
|
'allowed': [],
|
||||||
|
'banned': [],
|
||||||
|
'admin': []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def apply_yaml_config(path, c):
|
||||||
|
if not os.path.exists(path):
|
||||||
|
user_config = copy.deepcopy(default)
|
||||||
|
|
||||||
|
with open(path) as f:
|
||||||
|
user_config = _merge_dictionaries(yaml.safe_load(f), default)
|
||||||
|
|
||||||
|
update_auth(c, user_config)
|
||||||
|
update_userlists(c, user_config)
|
||||||
|
|
||||||
|
|
||||||
|
def update_auth(c, config):
|
||||||
|
"""
|
||||||
|
Set auth related configuration from YAML config file
|
||||||
|
"""
|
||||||
|
auth = config.get('auth')
|
||||||
|
|
||||||
|
if auth['type'] == 'dummy':
|
||||||
|
c.JupyterHub.authenticator_class = 'dummyauthenticator.DummyAuthenticator'
|
||||||
|
password = auth['dummy'].get('password')
|
||||||
|
if password is not None:
|
||||||
|
c.DummyAuthenticator.password = password
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
|
def update_userlists(c, config):
|
||||||
|
"""
|
||||||
|
Set user whitelists & admin lists
|
||||||
|
"""
|
||||||
|
users = config['users']
|
||||||
|
|
||||||
|
c.Authenticator.whitelist = set(users['allowed'])
|
||||||
|
c.Authenticator.blacklist = set(users['banned'])
|
||||||
|
c.Authenticator.admin_users = set(users['admin'])
|
||||||
|
|
||||||
|
|
||||||
|
def _merge_dictionaries(a, b, path=None, update=True):
|
||||||
|
"""
|
||||||
|
Merge two dictionaries recursively.
|
||||||
|
|
||||||
|
From https://stackoverflow.com/a/25270947
|
||||||
|
"""
|
||||||
|
if path is None:
|
||||||
|
path = []
|
||||||
|
for key in b:
|
||||||
|
if key in a:
|
||||||
|
if isinstance(a[key], dict) and isinstance(b[key], dict):
|
||||||
|
_merge_dictionaries(a[key], b[key], path + [str(key)])
|
||||||
|
elif a[key] == b[key]:
|
||||||
|
pass # same leaf value
|
||||||
|
elif isinstance(a[key], list) and isinstance(b[key], list):
|
||||||
|
for idx, val in enumerate(b[key]):
|
||||||
|
a[key][idx] = _merge_dictionaries(
|
||||||
|
a[key][idx],
|
||||||
|
b[key][idx],
|
||||||
|
path + [str(key), str(idx)],
|
||||||
|
update=update
|
||||||
|
)
|
||||||
|
elif update:
|
||||||
|
a[key] = b[key]
|
||||||
|
else:
|
||||||
|
raise Exception('Conflict at %s' % '.'.join(path + [str(key)]))
|
||||||
|
else:
|
||||||
|
a[key] = b[key]
|
||||||
|
return a
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
"""
|
"""
|
||||||
JupyterHub config for the littlest jupyterhub.
|
JupyterHub config for the littlest jupyterhub.
|
||||||
"""
|
"""
|
||||||
from escapism import escape
|
|
||||||
import os
|
import os
|
||||||
from systemdspawner import SystemdSpawner
|
from systemdspawner import SystemdSpawner
|
||||||
from tljh import user
|
from tljh import user, configurer
|
||||||
|
|
||||||
INSTALL_PREFIX = os.environ.get('TLJH_INSTALL_PREFIX')
|
INSTALL_PREFIX = os.environ.get('TLJH_INSTALL_PREFIX')
|
||||||
USER_ENV_PREFIX = os.path.join(INSTALL_PREFIX, 'user')
|
USER_ENV_PREFIX = os.path.join(INSTALL_PREFIX, 'user')
|
||||||
@@ -23,9 +22,9 @@ class CustomSpawner(SystemdSpawner):
|
|||||||
user.remove_user_group(self.user.name, 'jupyterhub-admins')
|
user.remove_user_group(self.user.name, 'jupyterhub-admins')
|
||||||
return super().start()
|
return super().start()
|
||||||
|
|
||||||
|
|
||||||
c.JupyterHub.spawner_class = CustomSpawner
|
c.JupyterHub.spawner_class = CustomSpawner
|
||||||
c.JupyterHub.authenticator_class = 'dummyauthenticator.DummyAuthenticator'
|
|
||||||
|
|
||||||
c.SystemdSpawner.extra_paths = [os.path.join(USER_ENV_PREFIX, 'bin')]
|
c.SystemdSpawner.extra_paths = [os.path.join(USER_ENV_PREFIX, 'bin')]
|
||||||
c.SystemdSpawner.use_sudo = True
|
c.SystemdSpawner.use_sudo = True
|
||||||
|
|
||||||
|
configurer.apply_yaml_config('/etc/jupyterhub/jupyterhub.yaml', c)
|
||||||
|
|||||||
Reference in New Issue
Block a user