Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/precommit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
name: pre-commit

on:
pull_request:
types: [opened, synchronize, reopened, ready_for_review]

jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v3
- uses: pre-commit/[email protected]
13 changes: 13 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
# Ruff version.
rev: v0.14.9
hooks:
# Run the linter.
- id: ruff-check
files: birdhouse/components/jupyterhub/jupyterhub_custom/jupyterhub_custom/
args: [ --config=birdhouse/components/jupyterhub/jupyterhub_custom/ruff.toml ]
# Run the formatter.
- id: ruff-format
files: birdhouse/components/jupyterhub/jupyterhub_custom/jupyterhub_custom/
args: [ --config=birdhouse/components/jupyterhub/jupyterhub_custom/ruff.toml ]
54 changes: 54 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,60 @@

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

- Refactor Jupyterhub configuration files

Previously the jupyterhub configuration was defined in `components/jupyterhub/jupyterhub_config.py.template`
except for the custom authenticator which was included in the `pavics/jupyterhub` docker image. This was fine,
except that the configuration was split across two projects and as the configuration got more complex,
maintaining these was getting more difficult.

This moves all configuration code including the authenticator to a python package named `jupyterhub_custom`
located at `components/jupyterhub/jupyterhub_custom/`. It moves all configurations for the authenticator
to the `magpie_authenticator.py` file and all the configurations for the spawner to the `custom_spawner.py`
file. All variables that are settable by environment variables are moved to the `constants.py` file. This
makes it much easier to see which variables are configurable using environment variables all in one place.

This change introduces unit tests and style policies (enforced by [ruff](https://docs.astral.sh/ruff/) and
[pre-commit](https://pre-commit.com/)) for this package to encourage better code practices.

This change should be fully backwards compatible with the exception of the change to how settings for
`deprecated-components/catalog` is handled (see below). However, it does deprecate some environment variables in
favour of better configuration solutions:

- using the `JUPYTERHUB_ENABLE_MULTI_NOTEBOOKS` variable to set the `DockerSpawner.allowed_images` variable
is deprecated.
- why?: this variable could be used to insert any python code into the middle of the
`jupyterhub_config.py.template` file. This makes it too easy to accidentally insert code that breaks
the configuration settings later on. We should avoid code insertion like this whenever possible.
- new method: by default `DockerSpawner.allowed_images` will be set based on the values of
`JUPYTERHUB_IMAGE_SELECTION_NAMES` and `JUPYTERHUB_DOCKER_NOTEBOOK_IMAGES`. If more than one image
is specified by those variables then the user will be able to select which one they want. If you
want to specify images other than those in the default those can be set using the `JUPYTERHUB_ALLOWED_IMAGES`
which is a yaml or JSON mapping of image names to jupyterlab docker image tags.
- using the `JUPYTERHUB_ADMIN_USERS` variable to set the `DockerSpawner.admin_users` variable is deprecated.
- why?: this also executes arbitrary code (like above). Also, jupyterhub encourages assigning admin permissions
based on
[roles](https://jupyterhub.readthedocs.io/en/latest/tutorial/getting-started/authenticators-users-basics.html#configure-admins-admin-users),
not by assigning them to the `admin_users` attribute. This makes it possible to easily revoke admin
permissions as well and does not require us to restart Jupyterhub to do so.
- new method: a new Magpie group is created. Its name is determined by the `JUPYTERHUB_ADMIN_GROUP_NAME`
variable (default is "jupyterhub-admin"). Add users to this group in Magpie in order to give them admin permissions on Jupyterhub.
- using lowercase constants in `JUPYTERHUB_CONFIG_OVERRIDE` is deprecated.
- why?: [PEP8 recommends](https://peps.python.org/pep-0008/#constants) constants be written with capitals
with underscores separating them.
- new method: all the constants that were previously named with lowercase have an uppercase equivalent
in `constants.py`. For example: `notebook_dir == constants.NOTEBOOK_DIR`. The uppercase version is
preferred.

Note: if your deployment is still using the `deprecated-components/catalog` component. You may want to manually set the
following in `JUPYTERHUB_CONFIG_OVERRIDE` since the automatic addition of the the `CATALOG_USERNAME` to the `blocked_users`
set is no longer part of the default settings (since the `deprecated-components/catalog` component has been deprecated for
a long time):

```python
c.MagpieAuthenticator.blocked_users.add("${CATALOG_USERNAME}")
```

[2.20.1](https://github.com/bird-house/birdhouse-deploy/tree/2.20.1) (2025-12-16)
------------------------------------------------------------------------------------------------------------------

Expand Down
5 changes: 3 additions & 2 deletions birdhouse/components/jupyterhub/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ custom_templates/login.html
jupyterhub_config.py
config/proxy/conf.extra-service.d/jupyterhub.conf
config/canarie-api/canarie_api_monitoring.py
config/magpie/providers.cfg
config/magpie/config.yml
service-config.json

# Old paths. Keep these so that old config files remain uncommittable after updates.
jupyterhub_canarie_api_monitoring.py
config/proxy/canarie_api_monitoring.py
config/proxy/canarie_api_monitoring.py
config/magpie/providers.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,10 @@ providers:
c4i: false
type: api
sync_type: api

permissions:
- service: jupyterhub
resource: /
permission: read
group: ${JUPYTERHUB_ADMIN_GROUP_NAME}
action: create
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
services:
magpie:
volumes:
- ./components/jupyterhub/config/magpie/providers.cfg:${MAGPIE_PROVIDERS_CONFIG_PATH}/jupyter.cfg:ro
- ./components/jupyterhub/config/magpie/config.yml:${MAGPIE_PROVIDERS_CONFIG_PATH}/jupyter.yml:ro
- ./components/jupyterhub/config/magpie/config.yml:${MAGPIE_PERMISSIONS_CONFIG_PATH}/jupyter.yml:ro
22 changes: 11 additions & 11 deletions birdhouse/components/jupyterhub/default.env
Original file line number Diff line number Diff line change
Expand Up @@ -69,8 +69,13 @@ export JUPYTERHUB_CRYPT_KEY=
# JUPYTERHUB_CRYPT_KEY is set.
export JUPYTERHUB_AUTHENTICATOR_REFRESH_AGE=60

# Usernames that should be given admin access in jupyterhub
export JUPYTERHUB_ADMIN_USERS='{\"${MAGPIE_ADMIN_USERNAME}\"}' # python set syntax
# Usernames that should be given admin access in jupyterhub
# This option is DEPRECATED (assign jupyterhub admin users to magpie group named JUPYTERHUB_ADMIN_GROUP_NAME instead)
#export JUPYTERHUB_ADMIN_USERS='{\"${MAGPIE_ADMIN_USERNAME}\"}' # python set syntax

# Users that belong to this group will have admin permissions when they log in to jupyterhub.
# By setting this variable this group will be created if it doesn't exist
export JUPYTERHUB_ADMIN_GROUP_NAME=jupyterhub-admin

# See description in env.local.example for details
export JUPYTERHUB_RESOURCE_LIMITS=
Expand All @@ -86,13 +91,15 @@ export DELAYED_EVAL="
# add any new variables not already in 'VARS' or 'OPTIONAL_VARS' that must be replaced in templates here
VARS="
$VARS
\$JUPYTERHUB_ADMIN_USERS
\$JUPYTERHUB_ADMIN_GROUP_NAME
\$JUPYTER_DEMO_USER
\$JUPYTERHUB_USER_DATA_DIR
"

OPTIONAL_VARS="
$OPTIONAL_VARS
\$JUPYTERHUB_ADMIN_USERS
\$JUPYTERHUB_ENABLE_MULTI_NOTEBOOKS
\$JUPYTER_DEMO_USER
\$JUPYTER_LOGIN_BANNER_TOP_SECTION
\$JUPYTER_LOGIN_BANNER_BOTTOM_SECTION
\$JUPYTER_LOGIN_TERMS_URL
Expand All @@ -101,13 +108,6 @@ OPTIONAL_VARS="
\$JUPYTERHUB_VERSION
\$JUPYTERHUB_IMAGE
\$JUPYTERHUB_IMAGE_URI
\$JUPYTERHUB_AUTHENTICATOR_AUTHORIZATION_URL
\$JUPYTERHUB_AUTHENTICATOR_REFRESH_AGE
\$JUPYTER_IDLE_SERVER_CULL_TIMEOUT
\$JUPYTER_IDLE_KERNEL_CULL_TIMEOUT
\$JUPYTER_IDLE_KERNEL_CULL_INTERVAL
\$JUPYTERHUB_USER_DATA_DIR
\$JUPYTERHUB_RESOURCE_LIMITS
"

# add any component that this component requires to run
Expand Down
14 changes: 13 additions & 1 deletion birdhouse/components/jupyterhub/docker-compose-extra.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ services:
DOCKER_NETWORK_NAME: jupyterhub_network
JUPYTERHUB_USER_DATA_DIR: ${JUPYTERHUB_USER_DATA_DIR}
WORKSPACE_DIR: ${JUPYTERHUB_USER_DATA_DIR}
JUPYTERHUB_ADMIN_USERS: ${JUPYTERHUB_ADMIN_USERS}
JUPYTERHUB_ADMIN_USERS: ${JUPYTERHUB_ADMIN_USERS} # DEPRECATED: see default.env for details
JUPYTER_DEMO_USER: ${JUPYTER_DEMO_USER}
JUPYTER_DEMO_USER_MEM_LIMIT: ${JUPYTER_DEMO_USER_MEM_LIMIT}
JUPYTER_DEMO_USER_CPU_LIMIT: ${JUPYTER_DEMO_USER_CPU_LIMIT}
Expand All @@ -28,7 +28,19 @@ services:
USER_WORKSPACE_GID: ${USER_WORKSPACE_GID}
JUPYTERHUB_CRYPT_KEY: ${JUPYTERHUB_CRYPT_KEY}
JUPYTERHUB_DOCKER_EXTRA_HOSTS: ${JUPYTERHUB_DOCKER_EXTRA_HOSTS:-}
JUPYTERHUB_AUTHENTICATOR_AUTHORIZATION_URL: ${JUPYTERHUB_AUTHENTICATOR_AUTHORIZATION_URL:-}
JUPYTERHUB_AUTHENTICATOR_REFRESH_AGE: ${JUPYTERHUB_AUTHENTICATOR_REFRESH_AGE:-}
JUPYTERHUB_ADMIN_GROUP_NAME: ${JUPYTERHUB_ADMIN_GROUP_NAME}
BIRDHOUSE_FQDN_PUBLIC: ${BIRDHOUSE_FQDN_PUBLIC}
BIRDHOUSE_PROXY_SCHEME: ${BIRDHOUSE_PROXY_SCHEME}
JUPYTER_IDLE_SERVER_CULL_TIMEOUT: ${JUPYTER_IDLE_SERVER_CULL_TIMEOUT:-}
JUPYTER_IDLE_KERNEL_CULL_TIMEOUT: ${JUPYTER_IDLE_KERNEL_CULL_TIMEOUT:-}
JUPYTER_IDLE_KERNEL_CULL_INTERVAL: ${JUPYTER_IDLE_KERNEL_CULL_INTERVAL:-}
JUPYTERHUB_ALLOWED_IMAGES: ${JUPYTERHUB_ALLOWED_IMAGES:-}
JUPYTERHUB_RESOURCE_LIMITS: ${JUPYTERHUB_RESOURCE_LIMITS:-}
PYTHONPATH: /srv/jupyterhub/jupyterhub_custom
volumes:
- ./components/jupyterhub/jupyterhub_custom:/srv/jupyterhub/jupyterhub_custom:ro
- ./components/jupyterhub/jupyterhub_config.py:/srv/jupyterhub/jupyterhub_config.py:ro
- ./components/jupyterhub/custom_templates:/custom_templates:ro
- ${JUPYTERHUB_USER_DATA_DIR}:${JUPYTERHUB_USER_DATA_DIR}
Expand Down
Loading
Loading