From f54f34e6352f0a89206f235492a492d5503df7d2 Mon Sep 17 00:00:00 2001 From: Chris Ostrouchov Date: Tue, 5 Jul 2022 09:48:02 -0400 Subject: [PATCH] Initial attempt to prove functions work --- flake.nix | 2 + .../image/jupyterhub/environment.yaml | 2 +- .../jupyterhub/files/jupyterhub/02-spawner.py | 56 +++++++++++++++---- .../kubernetes/services/jupyterhub/main.tf | 12 ++++ 4 files changed, 59 insertions(+), 13 deletions(-) diff --git a/flake.nix b/flake.nix index 94ca6310d1..6969666182 100644 --- a/flake.nix +++ b/flake.nix @@ -22,6 +22,7 @@ pythonPackages.bcrypt pythonPackages.kubernetes pythonPackages.packaging + pythonPackages.rich keycloak # cloud packages @@ -40,6 +41,7 @@ pythonPackages.dask-gateway pythonPackages.paramiko pythonPackages.escapism + pythonPackages.isort # additional pkgs.minikube diff --git a/qhub/template/image/jupyterhub/environment.yaml b/qhub/template/image/jupyterhub/environment.yaml index 9c9a1b2756..28bfff0d8d 100644 --- a/qhub/template/image/jupyterhub/environment.yaml +++ b/qhub/template/image/jupyterhub/environment.yaml @@ -7,7 +7,7 @@ dependencies: - jupyterhub-kubespawner==1.1.0 - oauthenticator==14.1.0 - escapism==1.0.1 - - cdsdashboards==0.6.1 + - cdsdashboards==0.6.2 - jupyterhub-idle-culler==1.0 - pip: - qhub-jupyterhub-theme==0.3.5 diff --git a/qhub/template/stages/07-kubernetes-services/modules/kubernetes/services/jupyterhub/files/jupyterhub/02-spawner.py b/qhub/template/stages/07-kubernetes-services/modules/kubernetes/services/jupyterhub/files/jupyterhub/02-spawner.py index 92c4db59b0..1f0c3b8be7 100644 --- a/qhub/template/stages/07-kubernetes-services/modules/kubernetes/services/jupyterhub/files/jupyterhub/02-spawner.py +++ b/qhub/template/stages/07-kubernetes-services/modules/kubernetes/services/jupyterhub/files/jupyterhub/02-spawner.py @@ -1,17 +1,11 @@ -# remove after next kubespawner release past 1/20/2022 -# /~https://github.com/jupyterhub/kubespawner/pull/558 +import json import os -import kubernetes.client.models -from tornado import gen - -kubernetes.client.models.V1EndpointPort = kubernetes.client.models.CoreV1EndpointPort - import z2jh # noqa: E402 from kubespawner import KubeSpawner # noqa: E402 +from tornado import gen cdsdashboards = z2jh.get_config("custom.cdsdashboards") -conda_store_environments = z2jh.get_config("custom.environments") @gen.coroutine @@ -32,7 +26,8 @@ def get_username_hook(spawner): from cdsdashboards.app import CDS_TEMPLATE_PATHS from cdsdashboards.builder.kubebuilder import KubeBuilder from cdsdashboards.hubextension import cds_extra_handlers - from cdsdashboards.hubextension.spawners.variablekube import VariableKubeSpawner + from cdsdashboards.hubextension.spawners.variablekube import \ + VariableKubeSpawner c.JupyterHub.allow_named_servers = True c.JupyterHub.extra_handlers.extend(cds_extra_handlers) @@ -49,9 +44,46 @@ def get_username_hook(spawner): # Force dashboard creator to select an instance size c.CDSDashboardsConfig.spawn_default_options = False - c.CDSDashboardsConfig.conda_envs = [ - environment["name"] for _, environment in conda_store_environments.items() - ] + def get_packages(conda_prefix): + try: + packages = set() + for filename in os.listdir(os.path.join(conda_prefix, "conda-meta")): + if filename.endswith(".json"): + with open(os.path.join(conda_prefix, "conda-meta", filename)) as f: + packages.add(json.load(f).get("name")) + return packages + except OSError as e: + import logging + + logger = logging.getLogger() + logger.error(f"An issue with a conda environment was encountered.\n{e}") + + def get_conda_prefixes(conda_store_mount): + for namespace in os.listdir(conda_store_mount): + if os.path.isdir(os.path.join(conda_store_mount, namespace, "envs")): + for name in os.listdir( + os.path.join(conda_store_mount, namespace, "envs") + ): + yield namespace, name, os.path.join( + conda_store_mount, namespace, "envs", name + ) + + def list_dashboard_environments(conda_store_mount): + for namespace, name, conda_prefix in get_conda_prefixes(conda_store_mount): + packages = get_packages(conda_prefix) + if packages and {"cdsdashboards-singleuser"} <= packages: + yield namespace, name, conda_prefix + + def conda_environments(): + conda_store_mount = z2jh.get_config("custom.conda-store-mount") + return [ + name + for namespace, name, conda_prefix in list_dashboard_environments( + conda_store_mount + ) + ] + + c.CDSDashboardsConfig.conda_envs = conda_environments # TODO: make timeouts configurable c.VariableMixin.proxy_ready_timeout = 600 diff --git a/qhub/template/stages/07-kubernetes-services/modules/kubernetes/services/jupyterhub/main.tf b/qhub/template/stages/07-kubernetes-services/modules/kubernetes/services/jupyterhub/main.tf index 7505e79002..2e10df8ff9 100644 --- a/qhub/template/stages/07-kubernetes-services/modules/kubernetes/services/jupyterhub/main.tf +++ b/qhub/template/stages/07-kubernetes-services/modules/kubernetes/services/jupyterhub/main.tf @@ -72,6 +72,18 @@ resource "helm_release" "jupyterhub" { "${var.general-node-group.key}" = var.general-node-group.value } + extraVolumes = [{ + name = "conda-store-shared" + persistentVolumeClaim = { + claimName = var.conda-store-pvc + } + }] + + extraVolumeMounts = [{ + mountPath = var.conda-store-mount + name = "conda-store-shared" + }] + extraConfig = { "01-theme.py" = file("${path.module}/files/jupyterhub/01-theme.py") "02-spawner.py" = file("${path.module}/files/jupyterhub/02-spawner.py")