Skip to content

Commit 7a9c432

Browse files
committed
refactor jupyterhub customizations
1 parent 6032b92 commit 7a9c432

File tree

19 files changed

+1692
-333
lines changed

19 files changed

+1692
-333
lines changed

.github/workflows/precommit.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
name: pre-commit
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened, ready_for_review]
6+
7+
jobs:
8+
pre-commit:
9+
runs-on: ubuntu-latest
10+
steps:
11+
- uses: actions/checkout@v4
12+
- uses: actions/setup-python@v3
13+
- uses: pre-commit/[email protected]

.pre-commit-config.yaml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
repos:
2+
- repo: https://github.com/astral-sh/ruff-pre-commit
3+
# Ruff version.
4+
rev: v0.14.9
5+
hooks:
6+
# Run the linter.
7+
- id: ruff-check
8+
files: birdhouse/components/jupyterhub/jupyterhub_custom/jupyterhub_custom/
9+
args: [ --config=birdhouse/components/jupyterhub/jupyterhub_custom/ruff.toml ]
10+
# Run the formatter.
11+
- id: ruff-format
12+
files: birdhouse/components/jupyterhub/jupyterhub_custom/jupyterhub_custom/
13+
args: [ --config=birdhouse/components/jupyterhub/jupyterhub_custom/ruff.toml ]

CHANGES.md

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,60 @@
3434

3535
Also changes the format for `JUPYTERHUB_RESOURCE_LIMITS` to a yaml or JSON string.
3636

37+
- Refactor Jupyterhub configuration files
38+
39+
Previously the jupyterhub configuration was defined in `components/jupyterhub/jupyterhub_config.py.template`
40+
except for the custom authenticator which was included in the `pavics/jupyterhub` docker image. This was fine,
41+
except that the configuration was split across two projects and as the configuration got more complex,
42+
maintaining these was getting more difficult.
43+
44+
This moves all configuration code including the authenticator to a python package named `jupyterhub_custom`
45+
located at `components/jupyterhub/jupyterhub_custom/`. It moves all configurations for the authenticator
46+
to the `magpie_authenticator.py` file and all the configurations for the spawner to the `custom_spawner.py`
47+
file. All variables that are settable by environment variables are moved to the `constants.py` file. This
48+
makes it much easier to see which variables are configurable using environment variables all in one place.
49+
50+
This change introduces unit tests and style policies (enforced by [ruff](https://docs.astral.sh/ruff/) and
51+
[pre-commit](https://pre-commit.com/)) for this package to encourage better code practices.
52+
53+
This change should be fully backwards compatible with the exception of the change to how settings for
54+
`deprecated-components/catalog` is handled (see below). However, it does deprecate some environment variables in
55+
favour of better configuration solutions:
56+
57+
- using the `JUPYTERHUB_ENABLE_MULTI_NOTEBOOKS` variable to set the `DockerSpawner.allowed_images` variable
58+
is deprecated.
59+
- why?: this variable could be used to insert any python code into the middle of the
60+
`jupyterhub_config.py.template` file. This makes it too easy to accidentally insert code that breaks
61+
the configuration settings later on. We should avoid code insertion like this whenever possible.
62+
- new method: by default `DockerSpawner.allowed_images` will be set based on the values of
63+
`JUPYTERHUB_IMAGE_SELECTION_NAMES` and `JUPYTERHUB_DOCKER_NOTEBOOK_IMAGES`. If more than one image
64+
is specified by those variables then the user will be able to select which one they want. If you
65+
want to specify images other than those in the default those can be set using the `JUPYTERHUB_ALLOWED_IMAGES`
66+
which is a yaml or JSON mapping of image names to jupyterlab docker image tags.
67+
- using the `JUPYTERHUB_ADMIN_USERS` variable to set the `DockerSpawner.admin_users` variable is deprecated.
68+
- why?: this also executes arbitrary code (like above). Also, jupyterhub encourages assigning admin permissions
69+
based on
70+
[roles](https://jupyterhub.readthedocs.io/en/latest/tutorial/getting-started/authenticators-users-basics.html#configure-admins-admin-users),
71+
not by assigning them to the `admin_users` attribute. This makes it possible to easily revoke admin
72+
permissions as well and does not require us to restart Jupyterhub to do so.
73+
- new method: a new Magpie group is created. Its name is determined by the `JUPYTERHUB_ADMIN_GROUP_NAME`
74+
variable (default is "jupyterhub-admin"). Add users to this group in Magpie in order to give them admin permissions on Jupyterhub.
75+
- using lowercase constants in `JUPYTERHUB_CONFIG_OVERRIDE` is deprecated.
76+
- why?: [PEP8 recommends](https://peps.python.org/pep-0008/#constants) constants be written with capitals
77+
with underscores separating them.
78+
- new method: all the constants that were previously named with lowercase have an uppercase equivalent
79+
in `constants.py`. For example: `notebook_dir == constants.NOTEBOOK_DIR`. The uppercase version is
80+
preferred.
81+
82+
Note: if your deployment is still using the `deprecated-components/catalog` component. You may want to manually set the
83+
following in `JUPYTERHUB_CONFIG_OVERRIDE` since the automatic addition of the the `CATALOG_USERNAME` to the `blocked_users`
84+
set is no longer part of the default settings (since the `deprecated-components/catalog` component has been deprecated for
85+
a long time):
86+
87+
```python
88+
c.MagpieAuthenticator.blocked_users.add("${CATALOG_USERNAME}")
89+
```
90+
3791
[2.20.1](https://github.com/bird-house/birdhouse-deploy/tree/2.20.1) (2025-12-16)
3892
------------------------------------------------------------------------------------------------------------------
3993

birdhouse/components/jupyterhub/.gitignore

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ custom_templates/login.html
22
jupyterhub_config.py
33
config/proxy/conf.extra-service.d/jupyterhub.conf
44
config/canarie-api/canarie_api_monitoring.py
5-
config/magpie/providers.cfg
5+
config/magpie/config.yml
66
service-config.json
77

88
# Old paths. Keep these so that old config files remain uncommittable after updates.
99
jupyterhub_canarie_api_monitoring.py
10-
config/proxy/canarie_api_monitoring.py
10+
config/proxy/canarie_api_monitoring.py
11+
config/magpie/providers.cfg

birdhouse/components/jupyterhub/config/magpie/providers.cfg.template renamed to birdhouse/components/jupyterhub/config/magpie/config.yml.template

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,10 @@ providers:
88
c4i: false
99
type: api
1010
sync_type: api
11+
12+
permissions:
13+
- service: jupyterhub
14+
resource: /
15+
permission: read
16+
group: ${JUPYTERHUB_ADMIN_GROUP_NAME}
17+
action: create
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
services:
22
magpie:
33
volumes:
4-
- ./components/jupyterhub/config/magpie/providers.cfg:${MAGPIE_PROVIDERS_CONFIG_PATH}/jupyter.cfg:ro
4+
- ./components/jupyterhub/config/magpie/config.yml:${MAGPIE_PROVIDERS_CONFIG_PATH}/jupyter.yml:ro
5+
- ./components/jupyterhub/config/magpie/config.yml:${MAGPIE_PERMISSIONS_CONFIG_PATH}/jupyter.yml:ro

birdhouse/components/jupyterhub/default.env

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,13 @@ export JUPYTERHUB_CRYPT_KEY=
6969
# JUPYTERHUB_CRYPT_KEY is set.
7070
export JUPYTERHUB_AUTHENTICATOR_REFRESH_AGE=60
7171

72-
# Usernames that should be given admin access in jupyterhub
73-
export JUPYTERHUB_ADMIN_USERS='{\"${MAGPIE_ADMIN_USERNAME}\"}' # python set syntax
72+
# Usernames that should be given admin access in jupyterhub
73+
# This option is DEPRECATED (assign jupyterhub admin users to magpie group named JUPYTERHUB_ADMIN_GROUP_NAME instead)
74+
#export JUPYTERHUB_ADMIN_USERS='{\"${MAGPIE_ADMIN_USERNAME}\"}' # python set syntax
75+
76+
# Users that belong to this group will have admin permissions when they log in to jupyterhub.
77+
# By setting this variable this group will be created if it doesn't exist
78+
export JUPYTERHUB_ADMIN_GROUP_NAME=jupyterhub-admin
7479

7580
# See description in env.local.example for details
7681
export JUPYTERHUB_RESOURCE_LIMITS=
@@ -86,13 +91,15 @@ export DELAYED_EVAL="
8691
# add any new variables not already in 'VARS' or 'OPTIONAL_VARS' that must be replaced in templates here
8792
VARS="
8893
$VARS
89-
\$JUPYTERHUB_ADMIN_USERS
94+
\$JUPYTERHUB_ADMIN_GROUP_NAME
95+
\$JUPYTER_DEMO_USER
96+
\$JUPYTERHUB_USER_DATA_DIR
9097
"
9198

9299
OPTIONAL_VARS="
93100
$OPTIONAL_VARS
101+
\$JUPYTERHUB_ADMIN_USERS
94102
\$JUPYTERHUB_ENABLE_MULTI_NOTEBOOKS
95-
\$JUPYTER_DEMO_USER
96103
\$JUPYTER_LOGIN_BANNER_TOP_SECTION
97104
\$JUPYTER_LOGIN_BANNER_BOTTOM_SECTION
98105
\$JUPYTER_LOGIN_TERMS_URL
@@ -101,13 +108,6 @@ OPTIONAL_VARS="
101108
\$JUPYTERHUB_VERSION
102109
\$JUPYTERHUB_IMAGE
103110
\$JUPYTERHUB_IMAGE_URI
104-
\$JUPYTERHUB_AUTHENTICATOR_AUTHORIZATION_URL
105-
\$JUPYTERHUB_AUTHENTICATOR_REFRESH_AGE
106-
\$JUPYTER_IDLE_SERVER_CULL_TIMEOUT
107-
\$JUPYTER_IDLE_KERNEL_CULL_TIMEOUT
108-
\$JUPYTER_IDLE_KERNEL_CULL_INTERVAL
109-
\$JUPYTERHUB_USER_DATA_DIR
110-
\$JUPYTERHUB_RESOURCE_LIMITS
111111
"
112112

113113
# add any component that this component requires to run

birdhouse/components/jupyterhub/docker-compose-extra.yml

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ services:
1717
DOCKER_NETWORK_NAME: jupyterhub_network
1818
JUPYTERHUB_USER_DATA_DIR: ${JUPYTERHUB_USER_DATA_DIR}
1919
WORKSPACE_DIR: ${JUPYTERHUB_USER_DATA_DIR}
20-
JUPYTERHUB_ADMIN_USERS: ${JUPYTERHUB_ADMIN_USERS}
20+
JUPYTERHUB_ADMIN_USERS: ${JUPYTERHUB_ADMIN_USERS} # DEPRECATED: see default.env for details
2121
JUPYTER_DEMO_USER: ${JUPYTER_DEMO_USER}
2222
JUPYTER_DEMO_USER_MEM_LIMIT: ${JUPYTER_DEMO_USER_MEM_LIMIT}
2323
JUPYTER_DEMO_USER_CPU_LIMIT: ${JUPYTER_DEMO_USER_CPU_LIMIT}
@@ -28,7 +28,19 @@ services:
2828
USER_WORKSPACE_GID: ${USER_WORKSPACE_GID}
2929
JUPYTERHUB_CRYPT_KEY: ${JUPYTERHUB_CRYPT_KEY}
3030
JUPYTERHUB_DOCKER_EXTRA_HOSTS: ${JUPYTERHUB_DOCKER_EXTRA_HOSTS:-}
31+
JUPYTERHUB_AUTHENTICATOR_AUTHORIZATION_URL: ${JUPYTERHUB_AUTHENTICATOR_AUTHORIZATION_URL:-}
32+
JUPYTERHUB_AUTHENTICATOR_REFRESH_AGE: ${JUPYTERHUB_AUTHENTICATOR_REFRESH_AGE:-}
33+
JUPYTERHUB_ADMIN_GROUP_NAME: ${JUPYTERHUB_ADMIN_GROUP_NAME}
34+
BIRDHOUSE_FQDN_PUBLIC: ${BIRDHOUSE_FQDN_PUBLIC}
35+
BIRDHOUSE_PROXY_SCHEME: ${BIRDHOUSE_PROXY_SCHEME}
36+
JUPYTER_IDLE_SERVER_CULL_TIMEOUT: ${JUPYTER_IDLE_SERVER_CULL_TIMEOUT:-}
37+
JUPYTER_IDLE_KERNEL_CULL_TIMEOUT: ${JUPYTER_IDLE_KERNEL_CULL_TIMEOUT:-}
38+
JUPYTER_IDLE_KERNEL_CULL_INTERVAL: ${JUPYTER_IDLE_KERNEL_CULL_INTERVAL:-}
39+
JUPYTERHUB_ALLOWED_IMAGES: ${JUPYTERHUB_ALLOWED_IMAGES:-}
40+
JUPYTERHUB_RESOURCE_LIMITS: ${JUPYTERHUB_RESOURCE_LIMITS:-}
41+
PYTHONPATH: /srv/jupyterhub/jupyterhub_custom
3142
volumes:
43+
- ./components/jupyterhub/jupyterhub_custom:/srv/jupyterhub/jupyterhub_custom:ro
3244
- ./components/jupyterhub/jupyterhub_config.py:/srv/jupyterhub/jupyterhub_config.py:ro
3345
- ./components/jupyterhub/custom_templates:/custom_templates:ro
3446
- ${JUPYTERHUB_USER_DATA_DIR}:${JUPYTERHUB_USER_DATA_DIR}

0 commit comments

Comments
 (0)