From f667f1ce4379649b6ba19f7b7a6ea616347e4aec Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Fri, 20 Sep 2024 11:03:15 +0200 Subject: [PATCH 01/65] additions for eoepca+ release beta01 --- .dockerignore | 1 + .gitignore | 1 - .pre-commit-config.yaml | 2 +- application_hub_context/app_hub_context.py | 161 ++++- application_hub_context/models.py | 16 +- application_hub_context/parser.py | 7 + config-generator/config-generator.ipynb | 546 +++++++++++++++ config-generator/config-maps/bash-login | 1 + config-generator/config-maps/bash-rc | 22 + config-generator/config-maps/conda-rc.yml | 5 + config-generator/config-maps/init.sh | 14 + config-generator/models.py | 148 ++++ jupyterhub/.helmignore | 31 + jupyterhub/Chart.yaml | 41 ++ jupyterhub/README.md | 18 + jupyterhub/files/hub/config.yml | 303 ++++++++ jupyterhub/files/hub/jupyterhub_config.py | 179 +++++ jupyterhub/files/hub/z2jh.py | 121 ++++ jupyterhub/files/theme/page.html | 5 + jupyterhub/files/theme/spawn.html | 7 + jupyterhub/files/theme/spawn_pending.html | 94 +++ jupyterhub/requirements.txt | 0 jupyterhub/templates/NOTES.txt | 153 ++++ jupyterhub/templates/_helpers-names.tpl | 306 ++++++++ jupyterhub/templates/_helpers-netpol.tpl | 86 +++ jupyterhub/templates/_helpers.tpl | 402 +++++++++++ .../templates/hub/_helpers-passwords.tpl | 92 +++ jupyterhub/templates/hub/configmap-theme.yaml | 22 + jupyterhub/templates/hub/configmap.yaml | 30 + jupyterhub/templates/hub/deployment.yaml | 252 +++++++ jupyterhub/templates/hub/netpol.yaml | 84 +++ jupyterhub/templates/hub/pdb.yaml | 23 + jupyterhub/templates/hub/pvc.yaml | 25 + jupyterhub/templates/hub/rbac.yaml | 30 + jupyterhub/templates/hub/secret.yaml | 50 ++ jupyterhub/templates/hub/service.yaml | 37 + jupyterhub/templates/hub/serviceaccount.yaml | 12 + jupyterhub/templates/image-pull-secret.yaml | 15 + .../image-puller/_helpers-daemonset.tpl | 251 +++++++ .../image-puller/daemonset-continuous.yaml | 8 + .../image-puller/daemonset-hook.yaml | 9 + jupyterhub/templates/image-puller/job.yaml | 76 ++ .../templates/image-puller/priorityclass.yaml | 18 + jupyterhub/templates/image-puller/rbac.yaml | 45 ++ .../image-puller/serviceaccount.yaml | 21 + jupyterhub/templates/ingress.yaml | 35 + .../templates/proxy/autohttps/_README.txt | 9 + .../proxy/autohttps/_configmap-dynamic.yaml | 109 +++ .../proxy/autohttps/_configmap-traefik.yaml | 68 ++ .../templates/proxy/autohttps/configmap.yaml | 28 + .../templates/proxy/autohttps/deployment.yaml | 154 ++++ .../templates/proxy/autohttps/netpol.yaml | 78 +++ jupyterhub/templates/proxy/autohttps/pdb.yaml | 23 + .../templates/proxy/autohttps/rbac.yaml | 35 + .../templates/proxy/autohttps/service.yaml | 25 + .../proxy/autohttps/serviceaccount.yaml | 12 + jupyterhub/templates/proxy/deployment.yaml | 178 +++++ jupyterhub/templates/proxy/netpol.yaml | 108 +++ jupyterhub/templates/proxy/pdb.yaml | 23 + jupyterhub/templates/proxy/secret.yaml | 13 + jupyterhub/templates/proxy/service.yaml | 80 +++ .../scheduling/_scheduling-helpers.tpl | 138 ++++ .../templates/scheduling/priorityclass.yaml | 15 + .../scheduling/user-placeholder/pdb.yaml | 22 + .../user-placeholder/priorityclass.yaml | 16 + .../user-placeholder/statefulset.yaml | 80 +++ .../scheduling/user-scheduler/configmap.yaml | 95 +++ .../scheduling/user-scheduler/deployment.yaml | 109 +++ .../scheduling/user-scheduler/pdb.yaml | 23 + .../scheduling/user-scheduler/rbac.yaml | 233 ++++++ .../user-scheduler/serviceaccount.yaml | 14 + jupyterhub/templates/singleuser/netpol.yaml | 99 +++ jupyterhub/templates/singleuser/secret.yaml | 17 + jupyterhub/values-minikube.yaml | 663 ++++++++++++++++++ jupyterhub/values.schema.json | 1 + sk-k8s/cluster-role-binding.yaml | 12 + sk-k8s/job.yaml | 26 + sk-k8s/script.yaml | 22 + skaffold.yaml | 32 + validate_config.py | 9 - 80 files changed, 6337 insertions(+), 37 deletions(-) create mode 100644 config-generator/config-generator.ipynb create mode 100644 config-generator/config-maps/bash-login create mode 100644 config-generator/config-maps/bash-rc create mode 100644 config-generator/config-maps/conda-rc.yml create mode 100644 config-generator/config-maps/init.sh create mode 100644 config-generator/models.py create mode 100644 jupyterhub/.helmignore create mode 100644 jupyterhub/Chart.yaml create mode 100644 jupyterhub/README.md create mode 100644 jupyterhub/files/hub/config.yml create mode 100644 jupyterhub/files/hub/jupyterhub_config.py create mode 100644 jupyterhub/files/hub/z2jh.py create mode 100644 jupyterhub/files/theme/page.html create mode 100644 jupyterhub/files/theme/spawn.html create mode 100644 jupyterhub/files/theme/spawn_pending.html create mode 100644 jupyterhub/requirements.txt create mode 100644 jupyterhub/templates/NOTES.txt create mode 100644 jupyterhub/templates/_helpers-names.tpl create mode 100644 jupyterhub/templates/_helpers-netpol.tpl create mode 100644 jupyterhub/templates/_helpers.tpl create mode 100644 jupyterhub/templates/hub/_helpers-passwords.tpl create mode 100644 jupyterhub/templates/hub/configmap-theme.yaml create mode 100644 jupyterhub/templates/hub/configmap.yaml create mode 100644 jupyterhub/templates/hub/deployment.yaml create mode 100644 jupyterhub/templates/hub/netpol.yaml create mode 100644 jupyterhub/templates/hub/pdb.yaml create mode 100644 jupyterhub/templates/hub/pvc.yaml create mode 100644 jupyterhub/templates/hub/rbac.yaml create mode 100644 jupyterhub/templates/hub/secret.yaml create mode 100644 jupyterhub/templates/hub/service.yaml create mode 100644 jupyterhub/templates/hub/serviceaccount.yaml create mode 100644 jupyterhub/templates/image-pull-secret.yaml create mode 100644 jupyterhub/templates/image-puller/_helpers-daemonset.tpl create mode 100644 jupyterhub/templates/image-puller/daemonset-continuous.yaml create mode 100644 jupyterhub/templates/image-puller/daemonset-hook.yaml create mode 100644 jupyterhub/templates/image-puller/job.yaml create mode 100644 jupyterhub/templates/image-puller/priorityclass.yaml create mode 100644 jupyterhub/templates/image-puller/rbac.yaml create mode 100644 jupyterhub/templates/image-puller/serviceaccount.yaml create mode 100644 jupyterhub/templates/ingress.yaml create mode 100644 jupyterhub/templates/proxy/autohttps/_README.txt create mode 100644 jupyterhub/templates/proxy/autohttps/_configmap-dynamic.yaml create mode 100644 jupyterhub/templates/proxy/autohttps/_configmap-traefik.yaml create mode 100644 jupyterhub/templates/proxy/autohttps/configmap.yaml create mode 100644 jupyterhub/templates/proxy/autohttps/deployment.yaml create mode 100644 jupyterhub/templates/proxy/autohttps/netpol.yaml create mode 100644 jupyterhub/templates/proxy/autohttps/pdb.yaml create mode 100644 jupyterhub/templates/proxy/autohttps/rbac.yaml create mode 100644 jupyterhub/templates/proxy/autohttps/service.yaml create mode 100644 jupyterhub/templates/proxy/autohttps/serviceaccount.yaml create mode 100644 jupyterhub/templates/proxy/deployment.yaml create mode 100644 jupyterhub/templates/proxy/netpol.yaml create mode 100644 jupyterhub/templates/proxy/pdb.yaml create mode 100644 jupyterhub/templates/proxy/secret.yaml create mode 100644 jupyterhub/templates/proxy/service.yaml create mode 100644 jupyterhub/templates/scheduling/_scheduling-helpers.tpl create mode 100644 jupyterhub/templates/scheduling/priorityclass.yaml create mode 100644 jupyterhub/templates/scheduling/user-placeholder/pdb.yaml create mode 100644 jupyterhub/templates/scheduling/user-placeholder/priorityclass.yaml create mode 100644 jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml create mode 100644 jupyterhub/templates/scheduling/user-scheduler/configmap.yaml create mode 100644 jupyterhub/templates/scheduling/user-scheduler/deployment.yaml create mode 100644 jupyterhub/templates/scheduling/user-scheduler/pdb.yaml create mode 100644 jupyterhub/templates/scheduling/user-scheduler/rbac.yaml create mode 100644 jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml create mode 100644 jupyterhub/templates/singleuser/netpol.yaml create mode 100644 jupyterhub/templates/singleuser/secret.yaml create mode 100644 jupyterhub/values-minikube.yaml create mode 100644 jupyterhub/values.schema.json create mode 100644 sk-k8s/cluster-role-binding.yaml create mode 100644 sk-k8s/job.yaml create mode 100644 sk-k8s/script.yaml create mode 100644 skaffold.yaml delete mode 100644 validate_config.py diff --git a/.dockerignore b/.dockerignore index b20cfb8..4f72fd2 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,3 +1,4 @@ jupyterhub values.yaml config.yml +config-generator diff --git a/.gitignore b/.gitignore index 5c29fa9..e239487 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ *.pyc __pycache__ -jupyterhub values.yaml *.egg-info build diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 2172a56..f1de4c9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -27,7 +27,7 @@ repos: args: - --max-line-length=88 - --max-doc-length=90 - - --ignore=E203,W503,W505 + - --ignore=E203,W503,W505,F821,E302,E402 - repo: https://github.com/psf/black rev: 22.3.0 hooks: diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index 3ebff7b..f805fec 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -1,10 +1,12 @@ import os import time +import yaml from abc import ABC from http import HTTPStatus from typing import Dict, TextIO from kubernetes import client, config +from kubernetes.utils import create_from_dict from kubernetes.client import Configuration from kubernetes.client.rest import ApiException from kubernetes.config.config_exception import ConfigException @@ -33,9 +35,9 @@ def __init__( self.api_client = self._get_api_client(self.kubeconfig_file) self.core_v1_api = self._get_core_v1_api() self.batch_v1_api = self._get_batch_v1_api() + self.apps_v1_api = self._get_apps_v1_api() self.rbac_authorization_v1_api = self._get_rbac_authorization_v1_api() self.namespace = namespace - self.spawner = spawner # get the groups the user belongs to self.user_groups = [group.name for group in self.spawner.user.groups] @@ -122,6 +124,9 @@ def _get_batch_v1_api(self) -> client.BatchV1Api: def _get_rbac_authorization_v1_api(self) -> client.RbacAuthorizationApi: return client.RbacAuthorizationV1Api(self.api_client) + def _get_apps_v1_api(self) -> client.AppsV1Api: + return client.AppsV1Api(self.api_client) + def is_object_created(self, read_method, **kwargs): read_methods = {} @@ -195,6 +200,35 @@ def initialise(self): def dispose(self): pass + def create_namespace( + self, labels: dict = None, annotations: dict = None + ) -> client.V1Namespace: + + if self.is_namespace_created(): + self.spawner.log.info( + f"namespace {self.namespace} exists, skipping creation" + ) + return self.core_v1_api.read_namespace(name=self.namespace) + + self.spawner.log.info(f"creating namespace {self.namespace}") + try: + body = client.V1Namespace( + metadata=client.V1ObjectMeta( + name=self.namespace, labels=labels, annotations=annotations + ) # noqa: E501 + ) + response = self.core_v1_api.create_namespace( + body=body, async_req=False + ) # noqa: E501 + + if not self.retry(self.is_namespace_created): + raise ApiException(http_resp=response) + self.spawner.log.info(f"namespace {self.namespace} created") + return response + except ApiException as e: + self.spawner.log.error(f"namespace {self.namespace} creation failed, {e}\n") + raise e + @staticmethod def retry(fun, max_tries=10, interval=1, **kwargs): for i in range(max_tries): @@ -390,7 +424,6 @@ def create_role( resources: list[str] = [""], api_groups: list[str] = ["*"], ): - if self.is_role_created(name=name): return self.rbac_authorization_v1_api.read_namespaced_role( name=name, namespace=self.namespace @@ -424,9 +457,7 @@ def create_role( raise e def create_image_pull_secret(self, name: str, data): - if self.is_image_pull_secret_created(name=name): - return self.core_v1_api.read_namespaced_secret( namespace=self.namespace, name=name ) # noqa: E501 @@ -500,6 +531,48 @@ def patch_service_account(self, secret_name: str): except ApiException as e: raise e + # new function to apply a set of manifests like kubectl apply -f + # def apply_manifests(self, manifest_file): + def apply_manifest(self, manifest): + + create_from_dict( + k8s_client=self.api_client, + data=manifest, + verbose=True, + namespace=self.namespace, + ) + + def unapply_manifests(self, manifest_content): + + manifests = yaml.safe_load_all(manifest_content) + + for k8_object in manifests: + kind = k8_object.get("kind") + self.spawner.log.info( + f"Deleting {kind} {k8_object.get('metadata', {}).get('name')}" + ) + metadata = k8_object.get("metadata", {}) + namespace = metadata.get("namespace", self.namespace) + name = metadata.get("name") + + if not kind or not name: + continue + + try: + if kind == "Deployment": + self.apps_v1_api.delete_namespaced_deployment(name, namespace) + elif kind == "Service": + self.core_v1_api.delete_namespaced_service(name, namespace) + elif kind == "Job": + self.batch_v1_api.delete_namespaced_job(name, namespace) + elif kind == "Pod": + self.core_v1_api.delete_namespaced_pod(name, namespace) + # Add other kinds as needed + else: + self.spawner.log.error(f"Unsupported kind: {kind}") + except client.exceptions.ApiException as e: + self.spawner.log.error(f"An error occurred: {e}") + class DefaultApplicationHubContext(ApplicationHubContext): def get_profile_list(self): @@ -576,6 +649,11 @@ def initialise(self): # process the config maps config_maps = self.config_parser.get_profile_config_maps(profile_id=profile_id) + # check the namespace + if not self.is_namespace_created(): + self.spawner.log.info(f"Creating namespace {self.namespace}") + self.create_namespace() + if config_maps: for config_map in config_maps: try: @@ -588,25 +666,34 @@ def initialise(self): annotations=None, labels=None, ) - self.spawner.log.info(f"Mounting configmap {config_map.name}") - self.spawner.volume_mounts.extend( - [ - { - "name": config_map.name, - "mountPath": config_map.mount_path, - "subPath": config_map.key, - }, - ] - ) + if config_map.mount_path is not None: - self.spawner.volumes.extend( - [ - { - "name": config_map.name, - "configMap": {"name": config_map.key}, - } - ] - ) + self.spawner.log.info(f"Mounting configmap {config_map.name}") + self.spawner.volume_mounts.extend( + [ + { + "name": config_map.name, + "mountPath": config_map.mount_path, + "subPath": config_map.key, + }, + ] + ) + self.spawner.volumes.extend( + [ + { + "name": config_map.name, + "configMap": { + "name": config_map.key, + "defaultMode": int(config_map.default_mode, 8) + if config_map.default_mode + else 0o644, # noqa: E501 + }, + } + ] + ) + self.spawner.log.info( + f"Mounted configmap {config_map.name} (key {config_map.key}) mode {int(config_map.default_mode, 8)}" # noqa: E501 + ) except Exception as err: self.spawner.log.error(f"Unexpected {err=}, {type(err)=}") self.spawner.log.error( @@ -668,7 +755,6 @@ def initialise(self): try: # checking if role binding is already created if not self.is_role_binding_created(name=role_binding.name): - # checking if role is already created if not self.is_role_created(name=role_binding.role.name): self.spawner.log.info( @@ -755,6 +841,26 @@ def initialise(self): f"Skipping creation of init container {init_container.name}" ) + # process the manifests + manifests = self.config_parser.get_profile_manifests(profile_id=profile_id) + + if manifests: + for manifest in manifests: + self.spawner.log.info(f"Apply manifest {manifest.name}") + + try: + ms = yaml.safe_load_all(manifest.content) + for k8_object in ms: + self.spawner.log.info( + f"Apply manifest kind {k8_object['kind']}" + ) + self.apply_manifest(k8_object) + except Exception as err: + self.spawner.log.error(f"Unexpected {err}, {type(err)}") + self.spawner.log.error( + f"Skipping creation of manifest {manifest.name}" + ) + def dispose(self): profile_id = self.config_parser.get_profile_by_slug(slug=self.profile_slug).id @@ -794,6 +900,14 @@ def dispose(self): self.spawner.log.info(f"Dispose role binding {role_binding.name}") self.delete_role_binding(role_binding=role_binding) + # process the manifests + manifests = self.config_parser.get_profile_manifests(profile_id=profile_id) + self.spawner.log.info(f"Delete manifest {manifests}") + if manifests: + for manifest in manifests: + self.spawner.log.info(f"Un-apply manifest {manifest.name}") + self.unapply_manifests(manifest_content=manifest.content) + # deal with the image pull secrets image_pull_secrets = self.config_parser.get_profile_image_pull_secrets( profile_id=profile_id @@ -814,7 +928,6 @@ def dispose(self): ) for elem in service_account_body.image_pull_secrets: if elem.name == image_pull_secret.name: - service_account_body.image_pull_secrets.remove( {"name": elem.name} ) diff --git a/application_hub_context/models.py b/application_hub_context/models.py index ec03106..51fa401 100644 --- a/application_hub_context/models.py +++ b/application_hub_context/models.py @@ -37,6 +37,13 @@ class defining a volume object: persist: bool +class Manifest(BaseModel): + name: str + key: str + content: Optional[str] = None + persist: Optional[bool] = True + + class ConfigMap(BaseModel): """ @@ -50,8 +57,8 @@ class ConfigMap(BaseModel): name: str key: str - mount_path: str - default_mode: Optional[str] + mount_path: Optional[str] = None + default_mode: Optional[str] = None readonly: bool content: Optional[str] = None persist: Optional[bool] = True @@ -67,6 +74,10 @@ class KubespawnerOverride(BaseModel): extra_resource_guarantees: Optional[dict] = {} +class InitContainerVolumeMount(VolumeMount): + sub_path: str + + class InitContainer(BaseModel): name: str image: str @@ -165,6 +176,7 @@ class Profile(BaseModel): role_bindings: Optional[List[RoleBinding]] = None image_pull_secrets: Optional[List[ImagePullSecret]] = None init_containers: Optional[List[InitContainer]] = None + manifests: Optional[List[Manifest]] = None class Config(BaseModel): diff --git a/application_hub_context/parser.py b/application_hub_context/parser.py index 8ed1e44..6364a6b 100644 --- a/application_hub_context/parser.py +++ b/application_hub_context/parser.py @@ -124,3 +124,10 @@ def get_profile_image_pull_secrets(self, profile_id): def get_profile_init_containers(self, profile_id): """returns the image pull secrets""" return self.get_profile_by_id(profile_id=profile_id).init_containers + + def get_profile_manifests(self, profile_id): + """returns the profile manifests""" + try: + return self.get_profile_by_id(profile_id=profile_id).manifests + except AttributeError: + pass diff --git a/config-generator/config-generator.ipynb b/config-generator/config-generator.ipynb new file mode 100644 index 0000000..a6d71d7 --- /dev/null +++ b/config-generator/config-generator.ipynb @@ -0,0 +1,546 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import yaml\n", + "from models import *\n", + "import os" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Configuration\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "storage_class_rwo = \"standard\"\n", + "storage_class_rwx = \"standard\"\n", + "\n", + "workspace_volume_size = \"50Gi\"\n", + "calrissian_volume_size = \"50Gi\"\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Volumes\n", + "\n", + "Create the Volumes" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Workspace Volume\n", + "\n", + "The workspace volume is persisted." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "workspace_volume = Volume(\n", + " name=\"workspace-volume\",\n", + " size=workspace_volume_size,\n", + " claim_name=\"workspace-claim\",\n", + " mount_path=\"/workspace\",\n", + " storage_class=storage_class_rwo,\n", + " access_modes=[\"ReadWriteOnce\"],\n", + " volume_mount=VolumeMount(name=\"workspace-volume\", mount_path=\"/workspace\"),\n", + " persist=True,\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Calrissian Volume\n", + "\n", + "This is a RWX volume, not persisted" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "calrissian_volume = Volume(\n", + " name=\"calrissian-volume\",\n", + " claim_name=\"calrissian-claim\",\n", + " size=calrissian_volume_size,\n", + " storage_class=storage_class_rwx,\n", + " access_modes=[\"ReadWriteMany\"],\n", + " volume_mount=VolumeMount(name=\"calrissian-volume\", mount_path=\"/calrissian\"),\n", + " persist=False,\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## ConfigMaps\n", + "\n", + "These configmaps are mounted as files on the pod." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### bash login\n", + "\n", + "This file is used for the JupyterLab Terminal configuration" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"config-maps/bash-login\", \"r\") as f:\n", + " content = f.read()\n", + "\n", + "bash_login_cm = ConfigMap(\n", + " name=\"bash-login\",\n", + " key=\"bash-login\",\n", + " content=content,\n", + " readonly=True,\n", + " persist=True,\n", + " mount_path=\"/workspace/.bash_login\",\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### bash.rc\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"config-maps/bash-rc\", \"r\") as f:\n", + " content = f.read()\n", + "bash_rc_cm = ConfigMap(\n", + " name=\"bash-rc\",\n", + " key=\"bash-rc\",\n", + " content=content,\n", + " readonly=True,\n", + " persist=True,\n", + " mount_path=\"/workspace/.bashrc\",\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Profiles" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "profiles = []" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Coder" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "coders = {\n", + " \"coder1\": {\n", + " \"display_name\": \"Code Server Small\",\n", + " \"slug\": \"ellip_studio_coder_slug_s\",\n", + " \"cpu_limit\": 2,\n", + " \"mem_limit\": \"8G\",\n", + " },\n", + " \"coder2\": {\n", + " \"display_name\": \"Code Server Medium\",\n", + " \"slug\": \"ellip_studio_coder_slug_m\",\n", + " \"cpu_limit\": 4,\n", + " \"mem_limit\": \"12G\",\n", + " },\n", + "}\n", + "\n", + "for key, value in coders.items():\n", + " coder_definition = ProfileDefinition(\n", + " display_name=value[\"display_name\"],\n", + " slug=value[\"slug\"],\n", + " default=False,\n", + " kubespawner_override=KubespawnerOverride(\n", + " cpu_limit=value[\"cpu_limit\"],\n", + " mem_limit=value[\"mem_limit\"],\n", + " image=\"eoepca/pde-code-server:develop\",\n", + " ),\n", + " )\n", + "\n", + " coder_profile = Profile(\n", + " id=f\"profile_studio_{key}\",\n", + " groups=[\"group-a\", \"group-b\"],\n", + " definition=coder_definition,\n", + " node_selector={},\n", + " volumes=[calrissian_volume, workspace_volume],\n", + " config_maps=[\n", + " bash_rc_cm,\n", + " ],\n", + " pod_env_vars={\n", + " \"HOME\": \"/workspace\",\n", + " \"CONDA_ENVS_PATH\": \" /workspace/.envs\",\n", + " },\n", + " )\n", + "\n", + " profiles.append(coder_profile)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## init.sh script" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"./config-maps/init.sh\", \"r\") as f:\n", + " content = f.read()\n", + "\n", + "init_cm = ConfigMap(\n", + " name=\"init\",\n", + " key=\"init\",\n", + " content=content,\n", + " readonly=True,\n", + " persist=False,\n", + " mount_path=\"/opt/init/.init.sh\",\n", + " default_mode=\"0660\",\n", + ")\n", + "\n", + "\n", + "init_context_volume_mount = InitContainerVolumeMount(\n", + " mount_path=\"/opt/init/.init.sh\", name=\"init\", sub_path=\"init\"\n", + ")\n", + "init_container = InitContainer(\n", + " name=\"init-file-on-volume\",\n", + " image=\"eoepca/pde-code-server:develop\",\n", + " command=[\"sh\", \"-c\", \"sh /opt/init/.init.sh\"],\n", + " volume_mounts=[\n", + " VolumeMount(name=\"workspace-volume\", mount_path=\"/workspace\"),\n", + " init_context_volume_mount,\n", + " ],\n", + ")\n", + "\n", + "eoepca_demo_init_script_profile = Profile(\n", + " id=f\"profile_demo_init_script\",\n", + " groups=[\"group-a\", \"group-b\"],\n", + " definition=ProfileDefinition(\n", + " display_name=\"Coder demo init script\",\n", + " description=\"This profile is used to demonstrate the use of an init script\",\n", + " slug=\"eoepca_demo_init_script\",\n", + " default=False,\n", + " kubespawner_override=KubespawnerOverride(\n", + " cpu_guarantee=1,\n", + " cpu_limit=2,\n", + " mem_guarantee=\"4G\",\n", + " mem_limit=\"6G\",\n", + " image=\"eoepca/pde-code-server:develop\",\n", + " ),\n", + " ),\n", + " node_selector={},\n", + " volumes=[calrissian_volume, workspace_volume],\n", + " config_maps=[init_cm],\n", + " pod_env_vars={\n", + " \"HOME\": \"/workspace\",\n", + " \"CONDA_ENVS_PATH\": \"/workspace/.envs\",\n", + " \"CONDARC\": \"/workspace/.condarc\",\n", + " \"XDG_RUNTIME_DIR\": \"/workspace/.local\",\n", + " \"CODE_SERVER_WS\": \"/workspace/mastering-app-package\",\n", + " },\n", + " init_containers=[init_container],\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "profiles.append(eoepca_demo_init_script_profile)\n", + "\n", + "eoepca_demo_init_script_profile = Profile(\n", + " id=f\"profile_demo_init_script\",\n", + " groups=[\"group-a\", \"group-b\"],\n", + " definition=ProfileDefinition(\n", + " display_name=\"Coder demo init script\",\n", + " description=\"This profile is used to demonstrate the use of an init script\",\n", + " slug=\"eoepca_demo_init_script\",\n", + " default=False,\n", + " kubespawner_override=KubespawnerOverride(\n", + " cpu_guarantee=1,\n", + " cpu_limit=2,\n", + " mem_guarantee=\"4G\",\n", + " mem_limit=\"6G\",\n", + " image=\"eoepca/pde-code-server:develop\",\n", + " ),\n", + " ),\n", + " node_selector={},\n", + " volumes=[calrissian_volume, workspace_volume],\n", + " config_maps=[init_cm],\n", + " pod_env_vars={\n", + " \"HOME\": \"/workspace\",\n", + " \"CONDA_ENVS_PATH\": \"/workspace/.envs\",\n", + " \"CONDARC\": \"/workspace/.condarc\",\n", + " \"XDG_RUNTIME_DIR\": \"/workspace/.local\",\n", + " \"CODE_SERVER_WS\": \"/workspace/mastering-app-package\",\n", + " },\n", + " init_containers=[init_container],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## JupyterLab" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "image = \"jupyter/scipy-notebook\"\n", + "\n", + "\n", + "eoepca_jupyter_lab_profile = Profile(\n", + " id=\"profile_jupyter_lab\",\n", + " groups=[\"group-c\"],\n", + " definition=ProfileDefinition(\n", + " display_name=\"Jupyter Lab\",\n", + " description=\"Jupyter Lab with Python 3.11\",\n", + " slug=\"eoepca_jupyter_lab\",\n", + " default=False,\n", + " kubespawner_override=KubespawnerOverride(\n", + " cpu_guarantee=1,\n", + " cpu_limit=2,\n", + " mem_guarantee=\"4G\",\n", + " mem_limit=\"6G\",\n", + " image=image,\n", + " ),\n", + " ),\n", + " node_selector={},\n", + " volumes=[workspace_volume],\n", + " config_maps=[],\n", + " pod_env_vars={\n", + " \"HOME\": \"/workspace\",\n", + " \"XDG_RUNTIME_DIR\": \"/workspace/.local\",\n", + " \"XDG_CONFIG_HOME\": \"/workspace/.config\",\n", + " },\n", + ")\n", + "\n", + "profiles.append(eoepca_jupyter_lab_profile)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Image pull secret\n" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "image_pull_secret = ImagePullSecret(\n", + " name=\"cr-config\",\n", + " persist=False,\n", + " data=\"\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "image = \"jupyter/scipy-notebook\"\n", + "\n", + "\n", + "eoepca_jupyter_lab_profile_2 = Profile(\n", + " id=\"profile_jupyter_lab_2\",\n", + " groups=[\"group-c\"],\n", + " definition=ProfileDefinition(\n", + " display_name=\"Jupyter Lab - profile 2\",\n", + " description=\"Jupyter Lab with Python 3.11 - demoes the use of an image pull secret\",\n", + " slug=\"eoepca_jupyter_lab_2\",\n", + " default=False,\n", + " kubespawner_override=KubespawnerOverride(\n", + " cpu_guarantee=1,\n", + " cpu_limit=2,\n", + " mem_guarantee=\"4G\",\n", + " mem_limit=\"6G\",\n", + " image=image,\n", + " ),\n", + " ),\n", + " node_selector={},\n", + " volumes=[workspace_volume],\n", + " config_maps=[],\n", + " pod_env_vars={\n", + " \"HOME\": \"/workspace\",\n", + " \"XDG_RUNTIME_DIR\": \"/workspace/.local\",\n", + " \"XDG_CONFIG_HOME\": \"/workspace/.config\",\n", + " },\n", + " image_pull_secrets=[image_pull_secret],\n", + ")\n", + "\n", + "profiles.append(eoepca_jupyter_lab_profile_2)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Configuration" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "config = Config(\n", + " profiles=profiles\n", + ")\n", + "\n", + "with open(\n", + " \"../jupyterhub/files/hub/config.yml\", \"w\"\n", + ") as file:\n", + " yaml.dump(config.dict(), file)" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[Profile(id='profile_studio_coder1', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='Code Server Small', description=None, slug='ellip_studio_coder_slug_s', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=None, mem_limit='8G', mem_guarantee=None, image='eoepca/pde-code-server:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[ConfigMap(name='bash-rc', key='bash-rc', mount_path='/workspace/.bashrc', default_mode=None, readonly=True, content='alias ll=\"ls -l\"\\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\\nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\\n. /home/jovyan/.bashrc\\n# >>> conda initialize >>>\\n# !! Contents within this block are managed by \\'conda init\\' !!\\n__conda_setup=\"$(\\'/opt/conda/bin/conda\\' \\'shell.bash\\' \\'hook\\' 2> /dev/null)\"\\nif [ $? -eq 0 ]; then\\n eval \"$__conda_setup\"\\nelse\\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/conda.sh\"\\n else\\n export PATH=\"/srv/conda/bin:$PATH\"\\n fi\\nfi\\nunset __conda_setup\\n\\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/mamba.sh\"\\nfi\\n# <<< conda initialize <<<\\n', persist=True)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteMany'], volume_mount=VolumeMount(name='calrissian-volume', mount_path='/calrissian'), persist=False), Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': ' /workspace/.envs'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[], manifests=None),\n", + " Profile(id='profile_studio_coder2', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='Code Server Medium', description=None, slug='ellip_studio_coder_slug_m', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=4, cpu_guarantee=None, mem_limit='12G', mem_guarantee=None, image='eoepca/pde-code-server:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[ConfigMap(name='bash-rc', key='bash-rc', mount_path='/workspace/.bashrc', default_mode=None, readonly=True, content='alias ll=\"ls -l\"\\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\\nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\\n. /home/jovyan/.bashrc\\n# >>> conda initialize >>>\\n# !! Contents within this block are managed by \\'conda init\\' !!\\n__conda_setup=\"$(\\'/opt/conda/bin/conda\\' \\'shell.bash\\' \\'hook\\' 2> /dev/null)\"\\nif [ $? -eq 0 ]; then\\n eval \"$__conda_setup\"\\nelse\\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/conda.sh\"\\n else\\n export PATH=\"/srv/conda/bin:$PATH\"\\n fi\\nfi\\nunset __conda_setup\\n\\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/mamba.sh\"\\nfi\\n# <<< conda initialize <<<\\n', persist=True)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteMany'], volume_mount=VolumeMount(name='calrissian-volume', mount_path='/calrissian'), persist=False), Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': ' /workspace/.envs'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[], manifests=None),\n", + " Profile(id='profile_demo_init_script', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='Coder demo init script', description='This profile is used to demonstrate the use of an init script', slug='eoepca_demo_init_script', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='eoepca/pde-code-server:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[ConfigMap(name='init', key='init', mount_path='/opt/init/.init.sh', default_mode='0660', readonly=True, content=\"set -x\\n\\ncd /workspace\\n\\ngit clone 'https://github.com/eoap/mastering-app-package.git'\\n\\ncode-server --install-extension ms-python.python\\ncode-server --install-extension redhat.vscode-yaml\\ncode-server --install-extension sbg-rabix.benten-cwl\\ncode-server --install-extension ms-toolsai.jupyter\\n\\nln -s /workspace/.local/share/code-server/extensions /workspace/extensions\\n\\nexit 0\\n\", persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteMany'], volume_mount=VolumeMount(name='calrissian-volume', mount_path='/calrissian'), persist=False), Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': '/workspace/.envs', 'CONDARC': '/workspace/.condarc', 'XDG_RUNTIME_DIR': '/workspace/.local', 'CODE_SERVER_WS': '/workspace/mastering-app-package'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[InitContainer(name='init-file-on-volume', image='eoepca/pde-code-server:develop', command=['sh', '-c', 'sh /opt/init/.init.sh'], volume_mounts=[VolumeMount(name='workspace-volume', mount_path='/workspace'), InitContainerVolumeMount(name='init', mount_path='/opt/init/.init.sh', sub_path='init')])], manifests=None),\n", + " Profile(id='profile_jupyter_lab', groups=['group-c'], definition=ProfileDefinition(display_name='Jupyter Lab', description='Jupyter Lab with Python 3.11', slug='eoepca_jupyter_lab', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='jupyter/scipy-notebook', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'XDG_RUNTIME_DIR': '/workspace/.local', 'XDG_CONFIG_HOME': '/workspace/.config'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[], manifests=None),\n", + " Profile(id='profile_jupyter_lab_2', groups=['group-c'], definition=ProfileDefinition(display_name='Jupyter Lab - profile 2', description='Jupyter Lab with Python 3.11 - demoes the use of an image pull secret', slug='eoepca_jupyter_lab_2', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='jupyter/scipy-notebook', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'XDG_RUNTIME_DIR': '/workspace/.local', 'XDG_CONFIG_HOME': '/workspace/.config'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[ImagePullSecret(name='cr-config', persist=False, data='')], init_containers=[], manifests=None)]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "profiles" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.8" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/config-generator/config-maps/bash-login b/config-generator/config-maps/bash-login new file mode 100644 index 0000000..743cb42 --- /dev/null +++ b/config-generator/config-maps/bash-login @@ -0,0 +1 @@ +source /workspace/.bashrc diff --git a/config-generator/config-maps/bash-rc b/config-generator/config-maps/bash-rc new file mode 100644 index 0000000..6623894 --- /dev/null +++ b/config-generator/config-maps/bash-rc @@ -0,0 +1,22 @@ +alias ll="ls -l" +alias calrissian="/opt/conda/bin/calrissian --pod-nodeselectors /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram 16G --max-cores "8" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/" +alias cwltool="/opt/conda/bin/cwltool --podman" +. /home/jovyan/.bashrc +# >>> conda initialize >>> +# !! Contents within this block are managed by 'conda init' !! +__conda_setup="$('/opt/conda/bin/conda' 'shell.bash' 'hook' 2> /dev/null)" +if [ $? -eq 0 ]; then + eval "$__conda_setup" +else + if [ -f "/opt/conda/etc/profile.d/conda.sh" ]; then + . "/opt/conda/etc/profile.d/conda.sh" + else + export PATH="/srv/conda/bin:$PATH" + fi +fi +unset __conda_setup + +if [ -f "/opt/conda/etc/profile.d/mamba.sh" ]; then + . "/opt/conda/etc/profile.d/mamba.sh" +fi +# <<< conda initialize <<< diff --git a/config-generator/config-maps/conda-rc.yml b/config-generator/config-maps/conda-rc.yml new file mode 100644 index 0000000..f8a34eb --- /dev/null +++ b/config-generator/config-maps/conda-rc.yml @@ -0,0 +1,5 @@ +auto_update_conda: false +show_channel_urls: true +channels: + - conda-forge + - terradue diff --git a/config-generator/config-maps/init.sh b/config-generator/config-maps/init.sh new file mode 100644 index 0000000..014fdf3 --- /dev/null +++ b/config-generator/config-maps/init.sh @@ -0,0 +1,14 @@ +set -x + +cd /workspace + +git clone 'https://github.com/eoap/mastering-app-package.git' + +code-server --install-extension ms-python.python +code-server --install-extension redhat.vscode-yaml +code-server --install-extension sbg-rabix.benten-cwl +code-server --install-extension ms-toolsai.jupyter + +ln -s /workspace/.local/share/code-server/extensions /workspace/extensions + +exit 0 diff --git a/config-generator/models.py b/config-generator/models.py new file mode 100644 index 0000000..aee37fa --- /dev/null +++ b/config-generator/models.py @@ -0,0 +1,148 @@ +from enum import Enum +from typing import Dict, List, Optional, Union + +from pydantic import BaseModel + + +class ConfigMapKeyRef(BaseModel): + name: str + key: str + + +class ConfigMapEnvVarReference(BaseModel): + from_config_map: ConfigMapKeyRef + + +class SubjectKind(str, Enum): + service_account = "ServiceAccount" + user = "User" + + +class Verb(str, Enum): + get = "get" + list = "list" + watch = "watch" + create = "create" + update = "update" + patch = "patch" + delete = "delete" + deletecollection = "deletecollection" + + +class Subject(BaseModel): + name: str + kind: SubjectKind + + +class Role(BaseModel): + name: str + resources: List[str] + verbs: List[Verb] + api_groups: Optional[List[str]] = [""] + + +class RoleBinding(BaseModel): + name: str + subjects: List[Subject] + role: Role + persist: bool = True + + +class VolumeMount(BaseModel): + """volume mount object""" + + name: str + mount_path: str + + +class InitContainerVolumeMount(VolumeMount): + sub_path: str + + +class Volume(BaseModel): + """volume object""" + + name: str + claim_name: str + size: str + storage_class: str + access_modes: List[str] + volume_mount: VolumeMount + persist: bool + + +class Manifest(BaseModel): + name: str + key: str + content: Optional[str] = None + persist: Optional[bool] = True + + +class ConfigMap(BaseModel): + """config map object""" + + name: str + key: str + mount_path: Optional[str] = None + default_mode: Optional[str] = None + readonly: bool + content: Optional[str] = None + persist: Optional[bool] = True + + +class KubespawnerOverride(BaseModel): + """kubespawner override object""" + + cpu_limit: int + cpu_guarantee: Optional[int] = None + mem_limit: str + mem_guarantee: Optional[str] = None + image: str + extra_resource_limits: Optional[dict] = {} + extra_resource_guarantees: Optional[dict] = {} + + +class InitContainer(BaseModel): + name: str + image: str + command: List[str] + volume_mounts: list[VolumeMount | InitContainerVolumeMount] + + +class ProfileDefinition(BaseModel): + """profile definition object""" + + display_name: str + description: Optional[str] = None + slug: str + default: bool + kubespawner_override: KubespawnerOverride + + +class ImagePullSecret(BaseModel): + name: str + persist: bool = True + data: Optional[str] = None + + +class Profile(BaseModel): + """profile object""" + + id: str + groups: List[str] + definition: ProfileDefinition + config_maps: Optional[List[ConfigMap]] = None + volumes: Optional[List[Volume]] = None + pod_env_vars: Optional[Dict[str, Union[str, ConfigMapEnvVarReference]]] = None + default_url: Optional[str] = None + node_selector: dict + role_bindings: Optional[List[RoleBinding]] = None + image_pull_secrets: Optional[List[ImagePullSecret]] = [] + init_containers: Optional[List[InitContainer]] = [] + manifests: Optional[List[Manifest]] = None + + +class Config(BaseModel): + """config object""" + + profiles: List[Profile] diff --git a/jupyterhub/.helmignore b/jupyterhub/.helmignore new file mode 100644 index 0000000..05f3c98 --- /dev/null +++ b/jupyterhub/.helmignore @@ -0,0 +1,31 @@ +# Anything within the root folder of the Helm chart, where Chart.yaml resides, +# will be embedded into the packaged Helm chart. This is reasonable since only +# when the templates render after the chart has been packaged and distributed, +# will the templates logic evaluate that determines if other files were +# referenced, such as our our files/hub/jupyterhub_config.py. +# +# Here are files that we intentionally ignore to avoid them being packaged, +# because we don't want to reference them from our templates anyhow. +schema.yaml + +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*~ +# Various IDEs +.project +.idea/ +*.tmproj diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml new file mode 100644 index 0000000..a6e5198 --- /dev/null +++ b/jupyterhub/Chart.yaml @@ -0,0 +1,41 @@ +annotations: + artifacthub.io/images: | + - image: jupyterhub/configurable-http-proxy:4.5.3 + name: configurable-http-proxy + - image: jupyterhub/k8s-hub:2.0.0 + name: k8s-hub + - image: jupyterhub/k8s-image-awaiter:2.0.0 + name: k8s-image-awaiter + - image: jupyterhub/k8s-network-tools:2.0.0 + name: k8s-network-tools + - image: jupyterhub/k8s-secret-sync:2.0.0 + name: k8s-secret-sync + - image: jupyterhub/k8s-singleuser-sample:2.0.0 + name: k8s-singleuser-sample + - image: k8s.gcr.io/kube-scheduler:v1.23.10 + name: kube-scheduler + - image: k8s.gcr.io/pause:3.8 + name: pause + - image: k8s.gcr.io/pause:3.8 + name: pause + - image: traefik:v2.8.4 + name: traefik +apiVersion: v2 +appVersion: 3.0.0 +description: Multi-user Jupyter installation +home: https://z2jh.jupyter.org +icon: https://jupyterhub.github.io/helm-chart/images/hublogo.svg +keywords: +- jupyter +- jupyterhub +- z2jh +kubeVersion: '>=1.20.0-0' +maintainers: +- email: erik@sundellopensource.se + name: Erik Sundell +- name: Simon Li + url: https://github.com/manics/ +name: jupyterhub +sources: +- https://github.com/jupyterhub/zero-to-jupyterhub-k8s +version: 2.0.0 diff --git a/jupyterhub/README.md b/jupyterhub/README.md new file mode 100644 index 0000000..bb85ebb --- /dev/null +++ b/jupyterhub/README.md @@ -0,0 +1,18 @@ +# JupyterHub Helm chart + +[![Documentation](https://img.shields.io/badge/Documentation-z2jh.jupyter.org-blue?logo=read-the-docs&logoColor=white)](https://z2jh.jupyter.org) +[![GitHub](https://img.shields.io/badge/Source_code-github-blue?logo=github&logoColor=white)](https://github.com/jupyterhub/zero-to-jupyterhub-k8s) +[![Discourse](https://img.shields.io/badge/Help_forum-discourse-blue?logo=discourse&logoColor=white)](https://discourse.jupyter.org/c/jupyterhub/z2jh-k8s) +[![Gitter](https://img.shields.io/badge/Social_chat-gitter-blue?logo=gitter&logoColor=white)](https://gitter.im/jupyterhub/jupyterhub) +
+[![Latest stable release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=Latest%20stable%20release&url=https://jupyterhub.github.io/helm-chart/info.json&query=$.jupyterhub.stable&logo=helm&logoColor=white)](https://jupyterhub.github.io/helm-chart#jupyterhub) +[![Latest pre-release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=Latest%20pre-release&url=https://jupyterhub.github.io/helm-chart/info.json&query=$.jupyterhub.pre&logo=helm&logoColor=white)](https://jupyterhub.github.io/helm-chart#development-releases-jupyterhub) +[![Latest development release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=Latest%20dev%20release&url=https://jupyterhub.github.io/helm-chart/info.json&query=$.jupyterhub.latest&logo=helm&logoColor=white)](https://jupyterhub.github.io/helm-chart#development-releases-jupyterhub) + +The JupyterHub Helm chart is accompanied with an installation guide at [z2jh.jupyter.org](https://z2jh.jupyter.org). Together they enable you to deploy [JupyterHub](https://jupyterhub.readthedocs.io) in a Kubernetes cluster that can make Jupyter environments available to several thousands of simultaneous users. + +## History + +Much of the initial groundwork for this documentation is information learned from the successful use of JupyterHub and Kubernetes at UC Berkeley in their [Data 8](http://data8.org/) program. + +![](https://raw.githubusercontent.com/jupyterhub/zero-to-jupyterhub-k8s/HEAD/docs/source/_static/images/data8_massive_audience.jpg) diff --git a/jupyterhub/files/hub/config.yml b/jupyterhub/files/hub/config.yml new file mode 100644 index 0000000..f2f679b --- /dev/null +++ b/jupyterhub/files/hub/config.yml @@ -0,0 +1,303 @@ +profiles: +- config_maps: + - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors\ + \ /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram\ + \ 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ + \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\ + # >>> conda initialize >>>\n# !! Contents within this block are managed by 'conda\ + \ init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' 2>\ + \ /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n \ + \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ + \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset\ + \ __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ + \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n" + default_mode: null + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: true + readonly: true + default_url: null + definition: + default: false + description: null + display_name: Code Server Small + kubespawner_override: + cpu_guarantee: null + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: eoepca/pde-code-server:develop + mem_guarantee: null + mem_limit: 8G + slug: ellip_studio_coder_slug_s + groups: + - group-a + - group-b + id: profile_studio_coder1 + image_pull_secrets: [] + init_containers: [] + manifests: null + node_selector: {} + pod_env_vars: + CONDA_ENVS_PATH: ' /workspace/.envs' + HOME: /workspace + role_bindings: null + volumes: + - access_modes: + - ReadWriteMany + claim_name: calrissian-claim + name: calrissian-volume + persist: false + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /calrissian + name: calrissian-volume + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: true + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: + - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors\ + \ /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram\ + \ 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ + \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\ + # >>> conda initialize >>>\n# !! Contents within this block are managed by 'conda\ + \ init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' 2>\ + \ /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n \ + \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ + \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset\ + \ __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ + \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n" + default_mode: null + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: true + readonly: true + default_url: null + definition: + default: false + description: null + display_name: Code Server Medium + kubespawner_override: + cpu_guarantee: null + cpu_limit: 4 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: eoepca/pde-code-server:develop + mem_guarantee: null + mem_limit: 12G + slug: ellip_studio_coder_slug_m + groups: + - group-a + - group-b + id: profile_studio_coder2 + image_pull_secrets: [] + init_containers: [] + manifests: null + node_selector: {} + pod_env_vars: + CONDA_ENVS_PATH: ' /workspace/.envs' + HOME: /workspace + role_bindings: null + volumes: + - access_modes: + - ReadWriteMany + claim_name: calrissian-claim + name: calrissian-volume + persist: false + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /calrissian + name: calrissian-volume + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: true + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: + - content: 'set -x + + + cd /workspace + + + git clone ''https://github.com/eoap/mastering-app-package.git'' + + + code-server --install-extension ms-python.python + + code-server --install-extension redhat.vscode-yaml + + code-server --install-extension sbg-rabix.benten-cwl + + code-server --install-extension ms-toolsai.jupyter + + + ln -s /workspace/.local/share/code-server/extensions /workspace/extensions + + + exit 0 + + ' + default_mode: '0660' + key: init + mount_path: /opt/init/.init.sh + name: init + persist: false + readonly: true + default_url: null + definition: + default: false + description: This profile is used to demonstrate the use of an init script + display_name: Coder demo init script + kubespawner_override: + cpu_guarantee: 1 + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: eoepca/pde-code-server:develop + mem_guarantee: 4G + mem_limit: 6G + slug: eoepca_demo_init_script + groups: + - group-a + - group-b + id: profile_demo_init_script + image_pull_secrets: [] + init_containers: + - command: + - sh + - -c + - sh /opt/init/.init.sh + image: eoepca/pde-code-server:develop + name: init-file-on-volume + volume_mounts: + - mount_path: /workspace + name: workspace-volume + - mount_path: /opt/init/.init.sh + name: init + sub_path: init + manifests: null + node_selector: {} + pod_env_vars: + CODE_SERVER_WS: /workspace/mastering-app-package + CONDARC: /workspace/.condarc + CONDA_ENVS_PATH: /workspace/.envs + HOME: /workspace + XDG_RUNTIME_DIR: /workspace/.local + role_bindings: null + volumes: + - access_modes: + - ReadWriteMany + claim_name: calrissian-claim + name: calrissian-volume + persist: false + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /calrissian + name: calrissian-volume + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: true + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: [] + default_url: null + definition: + default: false + description: Jupyter Lab with Python 3.11 + display_name: Jupyter Lab + kubespawner_override: + cpu_guarantee: 1 + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: jupyter/scipy-notebook + mem_guarantee: 4G + mem_limit: 6G + slug: eoepca_jupyter_lab + groups: + - group-c + id: profile_jupyter_lab + image_pull_secrets: [] + init_containers: [] + manifests: null + node_selector: {} + pod_env_vars: + HOME: /workspace + XDG_CONFIG_HOME: /workspace/.config + XDG_RUNTIME_DIR: /workspace/.local + role_bindings: null + volumes: + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: true + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: [] + default_url: null + definition: + default: false + description: Jupyter Lab with Python 3.11 - demoes the use of an image pull secret + display_name: Jupyter Lab - profile 2 + kubespawner_override: + cpu_guarantee: 1 + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: jupyter/scipy-notebook + mem_guarantee: 4G + mem_limit: 6G + slug: eoepca_jupyter_lab_2 + groups: + - group-c + id: profile_jupyter_lab_2 + image_pull_secrets: + - data: '' + name: cr-config + persist: false + init_containers: [] + manifests: null + node_selector: {} + pod_env_vars: + HOME: /workspace + XDG_CONFIG_HOME: /workspace/.config + XDG_RUNTIME_DIR: /workspace/.local + role_bindings: null + volumes: + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: true + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /workspace + name: workspace-volume diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py new file mode 100644 index 0000000..5e6fb9e --- /dev/null +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -0,0 +1,179 @@ +import os +import sys + +from tornado.httpclient import AsyncHTTPClient + + +from application_hub_context.app_hub_context import DefaultApplicationHubContext + + +configuration_directory = os.path.dirname(os.path.realpath(__file__)) +sys.path.insert(0, configuration_directory) + +from z2jh import ( + get_config, + get_name, + get_name_env, +) + +config_path = "/usr/local/etc/jupyterhub/config.yml" + +namespace_prefix = "jupyter" + + +def custom_options_form(spawner): + + spawner.log.info("Configure profile list") + + namespace = f"{namespace_prefix}-{spawner.user.name}" + + workspace = DefaultApplicationHubContext( + namespace=namespace, + spawner=spawner, + config_path=config_path, + ) + + spawner.profile_list = workspace.get_profile_list() + + return spawner._options_form_default() + + +def pre_spawn_hook(spawner): + + profile_slug = spawner.user_options.get("profile", None) + + env = os.environ["JUPYTERHUB_ENV"].lower() + + spawner.environment["CALRISSIAN_POD_NAME"] = f"jupyter-{spawner.user.name}-{env}" + + spawner.log.info(f"Using profile slug {profile_slug}") + + namespace = f"{namespace_prefix}-{spawner.user.name}" + + workspace = DefaultApplicationHubContext( + namespace=namespace, spawner=spawner, config_path=config_path + ) + + workspace.initialise() + + profile_id = workspace.config_parser.get_profile_by_slug(slug=profile_slug).id + + default_url = workspace.config_parser.get_profile_default_url(profile_id=profile_id) + + if default_url: + spawner.log.info(f"Setting default url to {default_url}") + spawner.default_url = default_url + + +def post_stop_hook(spawner): + + namespace = f"jupyter-{spawner.user.name}" + + workspace = DefaultApplicationHubContext( + namespace=namespace, spawner=spawner, config_path=config_path + ) + spawner.log.info("Dispose in post stop hook") + workspace.dispose() + + +c.JupyterHub.default_url = "spawn" + + +# Configure JupyterHub to use the curl backend for making HTTP requests, +# rather than the pure-python implementations. The default one starts +# being too slow to make a large number of requests to the proxy API +# at the rate required. +AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient") + +c.ConfigurableHTTPProxy.api_url = ( + f'http://{get_name("proxy-api")}:{get_name_env("proxy-api", "_SERVICE_PORT")}' +) +# c.ConfigurableHTTPProxy.should_start = False + +# Don't wait at all before redirecting a spawning user to the progress page +c.JupyterHub.tornado_settings = { + "slow_spawn_timeout": 0, +} + +jupyterhub_env = os.environ["JUPYTERHUB_ENV"].upper() +jupyterhub_hub_host = "hub.jupyter" +jupyterhub_single_user_image = os.environ["JUPYTERHUB_SINGLE_USER_IMAGE_NOTEBOOKS"] + +# Authentication +c.LocalAuthenticator.create_system_users = True +c.Authenticator.admin_users = {"jovyan"} +# Deprecated +c.Authenticator.allowed_users = {"jovyan"} +c.JupyterHub.authenticator_class = "dummy" + +# HTTP Proxy auth token +c.ConfigurableHTTPProxy.auth_token = get_config("proxy.secretToken") +c.JupyterHub.cookie_secret_file = "/srv/jupyterhub/cookie_secret" +# Proxy config +c.JupyterHub.cleanup_servers = False +# Network +c.JupyterHub.allow_named_servers = True +c.JupyterHub.ip = "0.0.0.0" +c.JupyterHub.hub_ip = "0.0.0.0" +c.JupyterHub.hub_connect_ip = jupyterhub_hub_host +# Misc +c.JupyterHub.cleanup_servers = False + +# Culling +c.JupyterHub.services = [ + { + "name": "idle-culler", + "admin": True, + "command": [sys.executable, "-m", "jupyterhub_idle_culler", "--timeout=3600"], + } +] + +# Logs +c.JupyterHub.log_level = "DEBUG" + +# Spawner +c.JupyterHub.spawner_class = "kubespawner.KubeSpawner" +c.KubeSpawner.environment = { + "JUPYTER_ENABLE_LAB": "true", +} + +c.KubeSpawner.uid = 1001 +c.KubeSpawner.fs_gid = 100 +c.KubeSpawner.hub_connect_ip = jupyterhub_hub_host + +# SecurityContext +c.KubeSpawner.privileged = True +c.KubeSpawner.allow_privilege_escalation = True + +# ServiceAccount +c.KubeSpawner.service_account = "default" +c.KubeSpawner.start_timeout = 60 * 15 +c.KubeSpawner.image = jupyterhub_single_user_image +c.KubernetesSpawner.verify_ssl = True +c.KubeSpawner.pod_name_template = ( + "jupyter-{username}-{servername}-" + os.environ["JUPYTERHUB_ENV"].lower() +) + +# Namespace +c.KubeSpawner.namespace = "jupyter" + +# User namespace +c.KubeSpawner.enable_user_namespaces = True + +# Volumes +# volumes are managed by the pre_spawn_hook/post_stop_hook + +# TODO - move this value to the values.yaml file +c.KubeSpawner.image_pull_secrets = ["cr-config"] + +# custom options form +c.KubeSpawner.options_form = custom_options_form + +# hooks +c.KubeSpawner.pre_spawn_hook = pre_spawn_hook +c.KubeSpawner.post_stop_hook = post_stop_hook + +c.JupyterHub.template_paths = [ + "/opt/jupyterhub/template", + "/usr/local/share/jupyterhub/templates", +] diff --git a/jupyterhub/files/hub/z2jh.py b/jupyterhub/files/hub/z2jh.py new file mode 100644 index 0000000..57e463f --- /dev/null +++ b/jupyterhub/files/hub/z2jh.py @@ -0,0 +1,121 @@ +""" +Utility methods for use in jupyterhub_config.py and dynamic subconfigs. + +Methods here can be imported by extraConfig in values.yaml +""" +# from collections import Mapping +from collections.abc import Mapping +from functools import lru_cache +import os + +import yaml + +# memoize so we only load config once +@lru_cache() +def _load_config(): + """Load the Helm chart configuration used to render the Helm templates of + the chart from a mounted k8s Secret, and merge in values from an optionally + mounted secret (hub.existingSecret).""" + + cfg = {} + for source in ("secret/values.yaml", "existing-secret/values.yaml"): + path = f"/usr/local/etc/jupyterhub/{source}" + if os.path.exists(path): + print(f"Loading {path}") + with open(path) as f: + values = yaml.safe_load(f) + cfg = _merge_dictionaries(cfg, values) + else: + print(f"No config at {path}") + return cfg + + +@lru_cache() +def _get_config_value(key): + """Load value from the k8s ConfigMap given a key.""" + + path = f"/usr/local/etc/jupyterhub/config/{key}" + if os.path.exists(path): + with open(path) as f: + return f.read() + else: + raise Exception(f"{path} not found!") + + +@lru_cache() +def get_secret_value(key, default="never-explicitly-set"): + """Load value from the user managed k8s Secret or the default k8s Secret + given a key.""" + + for source in ("existing-secret", "secret"): + path = f"/usr/local/etc/jupyterhub/{source}/{key}" + if os.path.exists(path): + with open(path) as f: + return f.read() + if default != "never-explicitly-set": + return default + raise Exception(f"{key} not found in either k8s Secret!") + + +def get_name(name): + """Returns the fullname of a resource given its short name""" + return _get_config_value(name) + + +def get_name_env(name, suffix=""): + """Returns the fullname of a resource given its short name along with a + suffix, converted to uppercase with dashes replaced with underscores. This + is useful to reference named services associated environment variables, such + as PROXY_PUBLIC_SERVICE_PORT.""" + env_key = _get_config_value(name) + suffix + env_key = env_key.upper().replace("-", "_") + return os.environ[env_key] + + +def _merge_dictionaries(a, b): + """Merge two dictionaries recursively. + + Simplified From https://stackoverflow.com/a/7205107 + """ + merged = a.copy() + for key in b: + if key in a: + if isinstance(a[key], Mapping) and isinstance(b[key], Mapping): + merged[key] = _merge_dictionaries(a[key], b[key]) + else: + merged[key] = b[key] + else: + merged[key] = b[key] + return merged + + +def get_config(key, default=None): + """ + Find a config item of a given name & return it + + Parses everything as YAML, so lists and dicts are available too + + get_config("a.b.c") returns config['a']['b']['c'] + """ + value = _load_config() + # resolve path in yaml + for level in key.split("."): + if not isinstance(value, dict): + # a parent is a scalar or null, + # can't resolve full path + return default + if level not in value: + return default + else: + value = value[level] + return value + + +def set_config_if_not_none(cparent, name, key): + """ + Find a config item of a given name, set the corresponding Jupyter + configuration item if not None + """ + data = get_config(key) + if data is not None: + setattr(cparent, name, data) diff --git a/jupyterhub/files/theme/page.html b/jupyterhub/files/theme/page.html new file mode 100644 index 0000000..6ee147a --- /dev/null +++ b/jupyterhub/files/theme/page.html @@ -0,0 +1,5 @@ +{% extends "templates/page.html" %} + +{% set announcement = 'EOEPCA+ ApplicationHub demonstration instance' %} + +{% block title %}ApplicationHub{% endblock %} diff --git a/jupyterhub/files/theme/spawn.html b/jupyterhub/files/theme/spawn.html new file mode 100644 index 0000000..639636b --- /dev/null +++ b/jupyterhub/files/theme/spawn.html @@ -0,0 +1,7 @@ +{% extends "templates/spawn.html" %} + +{% block heading %} +
+

Available demonstration applications

+
+{% endblock %} diff --git a/jupyterhub/files/theme/spawn_pending.html b/jupyterhub/files/theme/spawn_pending.html new file mode 100644 index 0000000..f2b2e7c --- /dev/null +++ b/jupyterhub/files/theme/spawn_pending.html @@ -0,0 +1,94 @@ +{% extends "page.html" %} + +{% block main %} + +
+
+
+ {% block message %} +

Your pod is starting up.

+

You will be redirected automatically when it's ready for you.

+ {% endblock %} +
+
+ 0% Complete +
+
+

+
+
+
+
+
+ Event log +
+
+
+
+
+ +{% endblock %} + +{% block script %} +{{ super() }} + +{% endblock %} diff --git a/jupyterhub/requirements.txt b/jupyterhub/requirements.txt new file mode 100644 index 0000000..e69de29 diff --git a/jupyterhub/templates/NOTES.txt b/jupyterhub/templates/NOTES.txt new file mode 100644 index 0000000..e9a4edf --- /dev/null +++ b/jupyterhub/templates/NOTES.txt @@ -0,0 +1,153 @@ +{{- $proxy_service := include "jupyterhub.proxy-public.fullname" . -}} + +{{- /* Generated with https://patorjk.com/software/taag/#p=display&h=0&f=Slant&t=JupyterHub */}} +. __ __ __ __ __ + / / __ __ ____ __ __ / /_ ___ _____ / / / / __ __ / /_ + __ / / / / / / / __ \ / / / / / __/ / _ \ / ___/ / /_/ / / / / / / __ \ +/ /_/ / / /_/ / / /_/ / / /_/ / / /_ / __/ / / / __ / / /_/ / / /_/ / +\____/ \__,_/ / .___/ \__, / \__/ \___/ /_/ /_/ /_/ \__,_/ /_.___/ + /_/ /____/ + + You have successfully installed the official JupyterHub Helm chart! + +### Installation info + + - Kubernetes namespace: {{ .Release.Namespace }} + - Helm release name: {{ .Release.Name }} + - Helm chart version: {{ .Chart.Version }} + - JupyterHub version: {{ .Chart.AppVersion }} + - Hub pod packages: See https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/{{ include "jupyterhub.chart-version-to-git-ref" .Chart.Version }}/images/hub/requirements.txt + +### Followup links + + - Documentation: https://z2jh.jupyter.org + - Help forum: https://discourse.jupyter.org + - Social chat: https://gitter.im/jupyterhub/jupyterhub + - Issue tracking: https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues + +### Post-installation checklist + + - Verify that created Pods enter a Running state: + + kubectl --namespace={{ .Release.Namespace }} get pod + + If a pod is stuck with a Pending or ContainerCreating status, diagnose with: + + kubectl --namespace={{ .Release.Namespace }} describe pod + + If a pod keeps restarting, diagnose with: + + kubectl --namespace={{ .Release.Namespace }} logs --previous + {{- println }} + + {{- if eq .Values.proxy.service.type "LoadBalancer" }} + - Verify an external IP is provided for the k8s Service {{ $proxy_service }}. + + kubectl --namespace={{ .Release.Namespace }} get service {{ $proxy_service }} + + If the external ip remains , diagnose with: + + kubectl --namespace={{ .Release.Namespace }} describe service {{ $proxy_service }} + {{- end }} + + - Verify web based access: + {{- println }} + {{- if .Values.ingress.enabled }} + {{- range $host := .Values.ingress.hosts }} + Try insecure HTTP access: http://{{ $host }}{{ $.Values.hub.baseUrl | trimSuffix "/" }}/ + {{- end }} + + {{- range $tls := .Values.ingress.tls }} + {{- range $host := $tls.hosts }} + Try secure HTTPS access: https://{{ $host }}{{ $.Values.hub.baseUrl | trimSuffix "/" }}/ + {{- end }} + {{- end }} + {{- else }} + You have not configured a k8s Ingress resource so you need to access the k8s + Service {{ $proxy_service }} directly. + {{- println }} + + {{- if eq .Values.proxy.service.type "NodePort" }} + The k8s Service {{ $proxy_service }} is exposed via NodePorts. That means + that all the k8s cluster's nodes are exposing the k8s Service via those + ports. + + Try insecure HTTP access: http://:{{ .Values.proxy.service.nodePorts.http | default "no-http-nodeport-set"}} + Try secure HTTPS access: https://:{{ .Values.proxy.service.nodePorts.https | default "no-https-nodeport-set" }} + + {{- else }} + If your computer is outside the k8s cluster, you can port-forward traffic to + the k8s Service {{ $proxy_service }} with kubectl to access it from your + computer. + + kubectl --namespace={{ .Release.Namespace }} port-forward service/{{ $proxy_service }} 8080:http + + Try insecure HTTP access: http://localhost:8080 + {{- end }} + {{- end }} + {{- println }} + + + + + +{{- /* + Warnings for likely misconfiguration +*/}} + +{{- if and (not .Values.scheduling.podPriority.enabled) (and .Values.scheduling.userPlaceholder.enabled .Values.scheduling.userPlaceholder.replicas) }} +################################################################################# +###### WARNING: You are using user placeholders without pod priority ##### +###### enabled*, either enable pod priority or stop using the ##### +###### user placeholders** to avoid having placeholders that ##### +###### refuse to make room for a real user. ##### +###### ##### +###### *scheduling.podPriority.enabled ##### +###### **scheduling.userPlaceholder.enabled ##### +###### **scheduling.userPlaceholder.replicas ##### +################################################################################# +{{- println }} +{{- end }} + + + + + +{{- /* + Breaking changes. +*/}} + +{{- $breaking := "" }} +{{- $breaking_title := "\n" }} +{{- $breaking_title = print $breaking_title "\n#################################################################################" }} +{{- $breaking_title = print $breaking_title "\n###### BREAKING: The config values passed contained no longer accepted #####" }} +{{- $breaking_title = print $breaking_title "\n###### options. See the messages below for more details. #####" }} +{{- $breaking_title = print $breaking_title "\n###### #####" }} +{{- $breaking_title = print $breaking_title "\n###### To verify your updated config is accepted, you can use #####" }} +{{- $breaking_title = print $breaking_title "\n###### the `helm template` command. #####" }} +{{- $breaking_title = print $breaking_title "\n#################################################################################" }} + + +{{- /* + This is an example (in a helm template comment) on how to detect and + communicate with regards to a breaking chart config change. + + {{- if hasKey .Values.singleuser.cloudMetadata "enabled" }} + {{- $breaking = print $breaking "\n\nCHANGED: singleuser.cloudMetadata.enabled must as of 1.0.0 be configured using singleuser.cloudMetadata.blockWithIptables with the opposite value." }} + {{- end }} +*/}} + + +{{- if hasKey .Values.rbac "enabled" }} +{{- $breaking = print $breaking "\n\nCHANGED: rbac.enabled must as of version 2.0.0 be configured via rbac.create and .serviceAccount.create." }} +{{- end }} + + +{{- if hasKey .Values.hub "fsGid" }} +{{- $breaking = print $breaking "\n\nCHANGED: hub.fsGid must as of version 2.0.0 be configured via hub.podSecurityContext.fsGroup." }} +{{- end }} + + +{{- if $breaking }} +{{- fail (print $breaking_title $breaking "\n\n") }} +{{- end }} diff --git a/jupyterhub/templates/_helpers-names.tpl b/jupyterhub/templates/_helpers-names.tpl new file mode 100644 index 0000000..bbb5864 --- /dev/null +++ b/jupyterhub/templates/_helpers-names.tpl @@ -0,0 +1,306 @@ +{{- /* + These helpers encapsulates logic on how we name resources. They also enable + parent charts to reference these dynamic resource names. + + To avoid duplicating documentation, for more information, please see the the + fullnameOverride entry in schema.yaml or the configuration reference that + schema.yaml renders to. + + https://z2jh.jupyter.org/en/latest/resources/reference.html#fullnameOverride +*/}} + + + +{{- /* + Utility templates +*/}} + +{{- /* + Renders to a prefix for the chart's resource names. This prefix is assumed to + make the resource name cluster unique. +*/}} +{{- define "jupyterhub.fullname" -}} + {{- /* + We have implemented a trick to allow a parent chart depending on this + chart to call these named templates. + + Caveats and notes: + + 1. While parent charts can reference these, grandparent charts can't. + 2. Parent charts must not use an alias for this chart. + 3. There is no failsafe workaround to above due to + https://github.com/helm/helm/issues/9214. + 4. .Chart is of its own type (*chart.Metadata) and needs to be casted + using "toYaml | fromYaml" in order to be able to use normal helm + template functions on it. + */}} + {{- $fullname_override := .Values.fullnameOverride }} + {{- $name_override := .Values.nameOverride }} + {{- if ne .Chart.Name "jupyterhub" }} + {{- if .Values.jupyterhub }} + {{- $fullname_override = .Values.jupyterhub.fullnameOverride }} + {{- $name_override = .Values.jupyterhub.nameOverride }} + {{- end }} + {{- end }} + + {{- if eq (typeOf $fullname_override) "string" }} + {{- $fullname_override }} + {{- else }} + {{- $name := $name_override | default .Chart.Name }} + {{- if contains $name .Release.Name }} + {{- .Release.Name }} + {{- else }} + {{- .Release.Name }}-{{ $name }} + {{- end }} + {{- end }} +{{- end }} + +{{- /* + Renders to a blank string or if the fullname template is truthy renders to it + with an appended dash. +*/}} +{{- define "jupyterhub.fullname.dash" -}} + {{- if (include "jupyterhub.fullname" .) }} + {{- include "jupyterhub.fullname" . }}- + {{- end }} +{{- end }} + + + +{{- /* + Namespaced resources +*/}} + +{{- /* hub Deployment */}} +{{- define "jupyterhub.hub.fullname" -}} + {{- include "jupyterhub.fullname.dash" . }}hub +{{- end }} + +{{- /* hub-serviceaccount ServiceAccount */}} +{{- define "jupyterhub.hub-serviceaccount.fullname" -}} + {{- if .Values.hub.serviceAccount.create }} + {{- .Values.hub.serviceAccount.name | default (include "jupyterhub.hub.fullname" .) }} + {{- else }} + {{- .Values.hub.serviceAccount.name | default "default" }} + {{- end }} +{{- end }} + +{{- /* hub-existing-secret Secret */}} +{{- define "jupyterhub.hub-existing-secret.fullname" -}} + {{- /* A hack to avoid issues from invoking this from a parent Helm chart. */}} + {{- $existing_secret := .Values.hub.existingSecret }} + {{- if ne .Chart.Name "jupyterhub" }} + {{- $existing_secret = .Values.jupyterhub.hub.existingSecret }} + {{- end }} + {{- if $existing_secret }} + {{- $existing_secret }} + {{- end }} +{{- end }} + +{{- /* hub-existing-secret-or-default Secret */}} +{{- define "jupyterhub.hub-existing-secret-or-default.fullname" -}} + {{- include "jupyterhub.hub-existing-secret.fullname" . | default (include "jupyterhub.hub.fullname" .) }} +{{- end }} + +{{- /* hub PVC */}} +{{- define "jupyterhub.hub-pvc.fullname" -}} + {{- include "jupyterhub.hub.fullname" . }}-db-dir +{{- end }} + +{{- /* proxy Deployment */}} +{{- define "jupyterhub.proxy.fullname" -}} + {{- include "jupyterhub.fullname.dash" . }}proxy +{{- end }} + +{{- /* proxy-api Service */}} +{{- define "jupyterhub.proxy-api.fullname" -}} + {{- include "jupyterhub.proxy.fullname" . }}-api +{{- end }} + +{{- /* proxy-http Service */}} +{{- define "jupyterhub.proxy-http.fullname" -}} + {{- include "jupyterhub.proxy.fullname" . }}-http +{{- end }} + +{{- /* proxy-public Service */}} +{{- define "jupyterhub.proxy-public.fullname" -}} + {{- include "jupyterhub.proxy.fullname" . }}-public +{{- end }} + +{{- /* proxy-public-tls Secret */}} +{{- define "jupyterhub.proxy-public-tls.fullname" -}} + {{- include "jupyterhub.proxy-public.fullname" . }}-tls-acme +{{- end }} + +{{- /* proxy-public-manual-tls Secret */}} +{{- define "jupyterhub.proxy-public-manual-tls.fullname" -}} + {{- include "jupyterhub.proxy-public.fullname" . }}-manual-tls +{{- end }} + +{{- /* autohttps Deployment */}} +{{- define "jupyterhub.autohttps.fullname" -}} + {{- include "jupyterhub.fullname.dash" . }}autohttps +{{- end }} + +{{- /* autohttps-serviceaccount ServiceAccount */}} +{{- define "jupyterhub.autohttps-serviceaccount.fullname" -}} + {{- if .Values.proxy.traefik.serviceAccount.create }} + {{- .Values.proxy.traefik.serviceAccount.name | default (include "jupyterhub.autohttps.fullname" .) }} + {{- else }} + {{- .Values.proxy.traefik.serviceAccount.name | default "default" }} + {{- end }} +{{- end }} + +{{- /* user-scheduler Deployment */}} +{{- define "jupyterhub.user-scheduler-deploy.fullname" -}} + {{- include "jupyterhub.fullname.dash" . }}user-scheduler +{{- end }} + +{{- /* user-scheduler-serviceaccount ServiceAccount */}} +{{- define "jupyterhub.user-scheduler-serviceaccount.fullname" -}} + {{- if .Values.scheduling.userScheduler.serviceAccount.create }} + {{- .Values.scheduling.userScheduler.serviceAccount.name | default (include "jupyterhub.user-scheduler-deploy.fullname" .) }} + {{- else }} + {{- .Values.scheduling.userScheduler.serviceAccount.name | default "default" }} + {{- end }} +{{- end }} + +{{- /* user-scheduler leader election lock resource */}} +{{- define "jupyterhub.user-scheduler-lock.fullname" -}} + {{- include "jupyterhub.user-scheduler-deploy.fullname" . }}-lock +{{- end }} + +{{- /* user-placeholder StatefulSet */}} +{{- define "jupyterhub.user-placeholder.fullname" -}} + {{- include "jupyterhub.fullname.dash" . }}user-placeholder +{{- end }} + +{{- /* image-awaiter Job */}} +{{- define "jupyterhub.hook-image-awaiter.fullname" -}} + {{- include "jupyterhub.fullname.dash" . }}hook-image-awaiter +{{- end }} + +{{- /* image-awaiter-serviceaccount ServiceAccount */}} +{{- define "jupyterhub.hook-image-awaiter-serviceaccount.fullname" -}} + {{- if .Values.prePuller.hook.serviceAccount.create }} + {{- .Values.prePuller.hook.serviceAccount.name | default (include "jupyterhub.hook-image-awaiter.fullname" .) }} + {{- else }} + {{- .Values.prePuller.hook.serviceAccount.name | default "default" }} + {{- end }} +{{- end }} + +{{- /* hook-image-puller DaemonSet */}} +{{- define "jupyterhub.hook-image-puller.fullname" -}} + {{- include "jupyterhub.fullname.dash" . }}hook-image-puller +{{- end }} + +{{- /* continuous-image-puller DaemonSet */}} +{{- define "jupyterhub.continuous-image-puller.fullname" -}} + {{- include "jupyterhub.fullname.dash" . }}continuous-image-puller +{{- end }} + +{{- /* singleuser NetworkPolicy */}} +{{- define "jupyterhub.singleuser.fullname" -}} + {{- include "jupyterhub.fullname.dash" . }}singleuser +{{- end }} + +{{- /* image-pull-secret Secret */}} +{{- define "jupyterhub.image-pull-secret.fullname" -}} + {{- include "jupyterhub.fullname.dash" . }}image-pull-secret +{{- end }} + +{{- /* Ingress */}} +{{- define "jupyterhub.ingress.fullname" -}} + {{- if (include "jupyterhub.fullname" .) }} + {{- include "jupyterhub.fullname" . }} + {{- else -}} + jupyterhub + {{- end }} +{{- end }} + + + +{{- /* + Cluster wide resources + + We enforce uniqueness of names for our cluster wide resources. We assume that + the prefix from setting fullnameOverride to null or a string will be cluster + unique. +*/}} + +{{- /* Priority */}} +{{- define "jupyterhub.priority.fullname" -}} + {{- if (include "jupyterhub.fullname" .) }} + {{- include "jupyterhub.fullname" . }} + {{- else }} + {{- .Release.Name }}-default-priority + {{- end }} +{{- end }} + +{{- /* user-placeholder Priority */}} +{{- define "jupyterhub.user-placeholder-priority.fullname" -}} + {{- if (include "jupyterhub.fullname" .) }} + {{- include "jupyterhub.user-placeholder.fullname" . }} + {{- else }} + {{- .Release.Name }}-user-placeholder-priority + {{- end }} +{{- end }} + +{{- /* image-puller Priority */}} +{{- define "jupyterhub.image-puller-priority.fullname" -}} + {{- if (include "jupyterhub.fullname" .) }} + {{- include "jupyterhub.fullname.dash" . }}image-puller + {{- else }} + {{- .Release.Name }}-image-puller-priority + {{- end }} +{{- end }} + +{{- /* user-scheduler's registered name */}} +{{- define "jupyterhub.user-scheduler.fullname" -}} + {{- if (include "jupyterhub.fullname" .) }} + {{- include "jupyterhub.user-scheduler-deploy.fullname" . }} + {{- else }} + {{- .Release.Name }}-user-scheduler + {{- end }} +{{- end }} + + + +{{- /* + A template to render all the named templates in this file for use in the + hub's ConfigMap. + + It is important we keep this in sync with the available templates. +*/}} +{{- define "jupyterhub.name-templates" -}} +fullname: {{ include "jupyterhub.fullname" . | quote }} +fullname-dash: {{ include "jupyterhub.fullname.dash" . | quote }} +hub: {{ include "jupyterhub.hub.fullname" . | quote }} +hub-serviceaccount: {{ include "jupyterhub.hub-serviceaccount.fullname" . | quote }} +hub-existing-secret: {{ include "jupyterhub.hub-existing-secret.fullname" . | quote }} +hub-existing-secret-or-default: {{ include "jupyterhub.hub-existing-secret-or-default.fullname" . | quote }} +hub-pvc: {{ include "jupyterhub.hub-pvc.fullname" . | quote }} +proxy: {{ include "jupyterhub.proxy.fullname" . | quote }} +proxy-api: {{ include "jupyterhub.proxy-api.fullname" . | quote }} +proxy-http: {{ include "jupyterhub.proxy-http.fullname" . | quote }} +proxy-public: {{ include "jupyterhub.proxy-public.fullname" . | quote }} +proxy-public-tls: {{ include "jupyterhub.proxy-public-tls.fullname" . | quote }} +proxy-public-manual-tls: {{ include "jupyterhub.proxy-public-manual-tls.fullname" . | quote }} +autohttps: {{ include "jupyterhub.autohttps.fullname" . | quote }} +autohttps-serviceaccount: {{ include "jupyterhub.autohttps-serviceaccount.fullname" . | quote }} +user-scheduler-deploy: {{ include "jupyterhub.user-scheduler-deploy.fullname" . | quote }} +user-scheduler-serviceaccount: {{ include "jupyterhub.user-scheduler-serviceaccount.fullname" . | quote }} +user-scheduler-lock: {{ include "jupyterhub.user-scheduler-lock.fullname" . | quote }} +user-placeholder: {{ include "jupyterhub.user-placeholder.fullname" . | quote }} +image-puller-priority: {{ include "jupyterhub.image-puller-priority.fullname" . | quote }} +hook-image-awaiter: {{ include "jupyterhub.hook-image-awaiter.fullname" . | quote }} +hook-image-awaiter-serviceaccount: {{ include "jupyterhub.hook-image-awaiter-serviceaccount.fullname" . | quote }} +hook-image-puller: {{ include "jupyterhub.hook-image-puller.fullname" . | quote }} +continuous-image-puller: {{ include "jupyterhub.continuous-image-puller.fullname" . | quote }} +singleuser: {{ include "jupyterhub.singleuser.fullname" . | quote }} +image-pull-secret: {{ include "jupyterhub.image-pull-secret.fullname" . | quote }} +ingress: {{ include "jupyterhub.ingress.fullname" . | quote }} +priority: {{ include "jupyterhub.priority.fullname" . | quote }} +user-placeholder-priority: {{ include "jupyterhub.user-placeholder-priority.fullname" . | quote }} +user-scheduler: {{ include "jupyterhub.user-scheduler.fullname" . | quote }} +{{- end }} diff --git a/jupyterhub/templates/_helpers-netpol.tpl b/jupyterhub/templates/_helpers-netpol.tpl new file mode 100644 index 0000000..5adbd3d --- /dev/null +++ b/jupyterhub/templates/_helpers-netpol.tpl @@ -0,0 +1,86 @@ +{{- /* + This named template renders egress rules for NetworkPolicy resources based on + common configuration. + + It is rendering based on the `egressAllowRules` and `egress` keys of the + passed networkPolicy config object. Each flag set to true under + `egressAllowRules` is rendered to a egress rule that next to any custom user + defined rules from the `egress` config. + + This named template needs to render based on a specific networkPolicy + resource, but also needs access to the root context. Due to that, it + accepts a list as its scope, where the first element is supposed to be the + root context and the second element is supposed to be the networkPolicy + configuration object. + + As an example, this is how you would render this named template from a + NetworkPolicy resource under its egress: + + egress: + # other rules here... + + {{- with (include "jupyterhub.networkPolicy.renderEgressRules" (list . .Values.hub.networkPolicy)) }} + {{- . | nindent 4 }} + {{- end }} + + Note that the reference to privateIPs and nonPrivateIPs relate to + https://en.wikipedia.org/wiki/Private_network#Private_IPv4_addresses. +*/}} + +{{- define "jupyterhub.networkPolicy.renderEgressRules" -}} +{{- $root := index . 0 }} +{{- $netpol := index . 1 }} +{{- if $netpol.egressAllowRules.dnsPortsPrivateIPs }} +# Allow outbound connections to the DNS port in the private IP ranges +- ports: + - protocol: UDP + port: 53 + - protocol: TCP + port: 53 + to: + - ipBlock: + cidr: 10.0.0.0/8 + - ipBlock: + cidr: 172.16.0.0/12 + - ipBlock: + cidr: 192.168.0.0/16 +{{- end }} + +{{- if $netpol.egressAllowRules.nonPrivateIPs }} +# Allow outbound connections to non-private IP ranges +- to: + - ipBlock: + cidr: 0.0.0.0/0 + except: + # As part of this rule, don't: + # - allow outbound connections to private IP + - 10.0.0.0/8 + - 172.16.0.0/12 + - 192.168.0.0/16 + # - allow outbound connections to the cloud metadata server + - {{ $root.Values.singleuser.cloudMetadata.ip }}/32 +{{- end }} + +{{- if $netpol.egressAllowRules.privateIPs }} +# Allow outbound connections to private IP ranges +- to: + - ipBlock: + cidr: 10.0.0.0/8 + - ipBlock: + cidr: 172.16.0.0/12 + - ipBlock: + cidr: 192.168.0.0/16 +{{- end }} + +{{- if $netpol.egressAllowRules.cloudMetadataServer }} +# Allow outbound connections to the cloud metadata server +- to: + - ipBlock: + cidr: {{ $root.Values.singleuser.cloudMetadata.ip }}/32 +{{- end }} + +{{- with $netpol.egress }} +# Allow outbound connections based on user specified rules +{{ . | toYaml }} +{{- end }} +{{- end }} diff --git a/jupyterhub/templates/_helpers.tpl b/jupyterhub/templates/_helpers.tpl new file mode 100644 index 0000000..5cc5e6d --- /dev/null +++ b/jupyterhub/templates/_helpers.tpl @@ -0,0 +1,402 @@ +{{- /* + ## About + This file contains helpers to systematically name, label and select Kubernetes + objects we define in the .yaml template files. + + + ## How helpers work + Helm helper functions is a good way to avoid repeating something. They will + generate some output based on one single dictionary of input that we call the + helpers scope. When you are in helm, you access your current scope with a + single a single punctuation (.). + + When you ask a helper to render its content, one often forward the current + scope to the helper in order to allow it to access .Release.Name, + .Values.rbac.create and similar values. + + #### Example - Passing the current scope + {{ include "jupyterhub.commonLabels" . }} + + It would be possible to pass something specific instead of the current scope + (.), but that would make .Release.Name etc. inaccessible by the helper which + is something we aim to avoid. + + #### Example - Passing a new scope + {{ include "demo.bananaPancakes" (dict "pancakes" 5 "bananas" 3) }} + + To let a helper access the current scope along with additional values we have + opted to create dictionary containing additional values that is then populated + with additional values from the current scope through a the merge function. + + #### Example - Passing a new scope augmented with the old + {{- $_ := merge (dict "appLabel" "kube-lego") . }} + {{- include "jupyterhub.matchLabels" $_ | nindent 6 }} + + In this way, the code within the definition of `jupyterhub.matchLabels` will + be able to access .Release.Name and .appLabel. + + NOTE: + The ordering of merge is crucial, the latter argument is merged into the + former. So if you would swap the order you would influence the current scope + risking unintentional behavior. Therefore, always put the fresh unreferenced + dictionary (dict "key1" "value1") first and the current scope (.) last. + + + ## Declared helpers + - appLabel | + - componentLabel | + - commonLabels | uses appLabel + - labels | uses commonLabels + - matchLabels | uses labels + - podCullerSelector | uses matchLabels + + + ## Example usage + ```yaml + # Excerpt from proxy/autohttps/deployment.yaml + apiVersion: apps/v1 + kind: Deployment + metadata: + name: {{ include "jupyterhub.autohttps.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} + spec: + selector: + matchLabels: + {{- include "jupyterhub.matchLabels" $_ | nindent 6 }} + template: + metadata: + labels: + {{- include "jupyterhub.labels" $_ | nindent 8 }} + hub.jupyter.org/network-access-proxy-http: "true" + ``` + + NOTE: + The "jupyterhub.matchLabels" and "jupyterhub.labels" is passed an augmented + scope that will influence the helpers' behavior. It get the current scope + "." but merged with a dictionary containing extra key/value pairs. In this + case the "." scope was merged with a small dictionary containing only one + key/value pair "appLabel: kube-lego". It is required for kube-lego to + function properly. It is a way to override the default app label's value. +*/}} + + +{{- /* + jupyterhub.appLabel: + Used by "jupyterhub.labels". +*/}} +{{- define "jupyterhub.appLabel" -}} +{{ .Values.nameOverride | default .Chart.Name | trunc 63 | trimSuffix "-" }} +{{- end }} + + +{{- /* + jupyterhub.componentLabel: + Used by "jupyterhub.labels". + + NOTE: The component label is determined by either... + - 1: The provided scope's .componentLabel + - 2: The template's filename if living in the root folder + - 3: The template parent folder's name + - : ...and is combined with .componentPrefix and .componentSuffix +*/}} +{{- define "jupyterhub.componentLabel" -}} +{{- $file := .Template.Name | base | trimSuffix ".yaml" -}} +{{- $parent := .Template.Name | dir | base | trimPrefix "templates" -}} +{{- $component := .componentLabel | default $parent | default $file -}} +{{- $component := print (.componentPrefix | default "") $component (.componentSuffix | default "") -}} +{{ $component }} +{{- end }} + + +{{- /* + jupyterhub.commonLabels: + Foundation for "jupyterhub.labels". + Provides labels: app, release, (chart and heritage). +*/}} +{{- define "jupyterhub.commonLabels" -}} +app: {{ .appLabel | default (include "jupyterhub.appLabel" .) }} +release: {{ .Release.Name }} +{{- if not .matchLabels }} +chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} +heritage: {{ .heritageLabel | default .Release.Service }} +{{- end }} +{{- end }} + + +{{- /* + jupyterhub.labels: + Provides labels: component, app, release, (chart and heritage). +*/}} +{{- define "jupyterhub.labels" -}} +component: {{ include "jupyterhub.componentLabel" . }} +{{ include "jupyterhub.commonLabels" . }} +{{- end }} + + +{{- /* + jupyterhub.matchLabels: + Used to provide pod selection labels: component, app, release. +*/}} +{{- define "jupyterhub.matchLabels" -}} +{{- $_ := merge (dict "matchLabels" true) . -}} +{{ include "jupyterhub.labels" $_ }} +{{- end }} + + +{{- /* + jupyterhub.dockerconfigjson: + Creates a base64 encoded docker registry json blob for use in a image pull + secret, just like the `kubectl create secret docker-registry` command does + for the generated secrets data.dockerconfigjson field. The output is + verified to be exactly the same even if you have a password spanning + multiple lines as you may need to use a private GCR registry. + + - https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod +*/}} +{{- define "jupyterhub.dockerconfigjson" -}} +{{ include "jupyterhub.dockerconfigjson.yaml" . | b64enc }} +{{- end }} + +{{- define "jupyterhub.dockerconfigjson.yaml" -}} +{{- with .Values.imagePullSecret -}} +{ + "auths": { + {{ .registry | default "https://index.docker.io/v1/" | quote }}: { + "username": {{ .username | quote }}, + "password": {{ .password | quote }}, + {{- if .email }} + "email": {{ .email | quote }}, + {{- end }} + "auth": {{ (print .username ":" .password) | b64enc | quote }} + } + } +} +{{- end }} +{{- end }} + +{{- /* + jupyterhub.imagePullSecrets + Augments passed .pullSecrets with $.Values.imagePullSecrets +*/}} +{{- define "jupyterhub.imagePullSecrets" -}} + {{- /* + We have implemented a trick to allow a parent chart depending on this + chart to call this named templates. + + Caveats and notes: + + 1. While parent charts can reference these, grandparent charts can't. + 2. Parent charts must not use an alias for this chart. + 3. There is no failsafe workaround to above due to + https://github.com/helm/helm/issues/9214. + 4. .Chart is of its own type (*chart.Metadata) and needs to be casted + using "toYaml | fromYaml" in order to be able to use normal helm + template functions on it. + */}} + {{- $jupyterhub_values := .root.Values }} + {{- if ne .root.Chart.Name "jupyterhub" }} + {{- if .root.Values.jupyterhub }} + {{- $jupyterhub_values = .root.Values.jupyterhub }} + {{- end }} + {{- end }} + + {{- /* Populate $_.list with all relevant entries */}} + {{- $_ := dict "list" (concat .image.pullSecrets $jupyterhub_values.imagePullSecrets | uniq) }} + {{- if and $jupyterhub_values.imagePullSecret.create $jupyterhub_values.imagePullSecret.automaticReferenceInjection }} + {{- $__ := set $_ "list" (append $_.list (include "jupyterhub.image-pull-secret.fullname" .root) | uniq) }} + {{- end }} + + {{- /* Decide if something should be written */}} + {{- if not (eq ($_.list | toJson) "[]") }} + + {{- /* Process the $_.list where strings become dicts with a name key and the + strings become the name keys' values into $_.res */}} + {{- $_ := set $_ "res" list }} + {{- range $_.list }} + {{- if eq (typeOf .) "string" }} + {{- $__ := set $_ "res" (append $_.res (dict "name" .)) }} + {{- else }} + {{- $__ := set $_ "res" (append $_.res .) }} + {{- end }} + {{- end }} + + {{- /* Write the results */}} + {{- $_.res | toJson }} + + {{- end }} +{{- end }} + +{{- /* + jupyterhub.singleuser.resources: + The resource request of a singleuser. +*/}} +{{- define "jupyterhub.singleuser.resources" -}} +{{- $r1 := .Values.singleuser.cpu.guarantee -}} +{{- $r2 := .Values.singleuser.memory.guarantee -}} +{{- $r3 := .Values.singleuser.extraResource.guarantees -}} +{{- $r := or $r1 $r2 $r3 -}} +{{- $l1 := .Values.singleuser.cpu.limit -}} +{{- $l2 := .Values.singleuser.memory.limit -}} +{{- $l3 := .Values.singleuser.extraResource.limits -}} +{{- $l := or $l1 $l2 $l3 -}} +{{- if $r -}} +requests: + {{- if $r1 }} + cpu: {{ .Values.singleuser.cpu.guarantee }} + {{- end }} + {{- if $r2 }} + memory: {{ .Values.singleuser.memory.guarantee }} + {{- end }} + {{- if $r3 }} + {{- range $key, $value := .Values.singleuser.extraResource.guarantees }} + {{ $key | quote }}: {{ $value | quote }} + {{- end }} + {{- end }} +{{- end }} + +{{- if $l }} +limits: + {{- if $l1 }} + cpu: {{ .Values.singleuser.cpu.limit }} + {{- end }} + {{- if $l2 }} + memory: {{ .Values.singleuser.memory.limit }} + {{- end }} + {{- if $l3 }} + {{- range $key, $value := .Values.singleuser.extraResource.limits }} + {{ $key | quote }}: {{ $value | quote }} + {{- end }} + {{- end }} +{{- end }} +{{- end }} + +{{- /* + jupyterhub.extraEnv: + Output YAML formatted EnvVar entries for use in a containers env field. +*/}} +{{- define "jupyterhub.extraEnv" -}} +{{- include "jupyterhub.extraEnv.withTrailingNewLine" . | trimSuffix "\n" }} +{{- end }} + +{{- define "jupyterhub.extraEnv.withTrailingNewLine" -}} +{{- if . }} +{{- /* If extraEnv is a list, we inject it as it is. */}} +{{- if eq (typeOf .) "[]interface {}" }} +{{- . | toYaml }} + +{{- /* If extraEnv is a map, we differentiate two cases: */}} +{{- else if eq (typeOf .) "map[string]interface {}" }} +{{- range $key, $value := . }} +{{- /* + - If extraEnv.someKey has a map value, then we add the value as a YAML + parsed list element and use the key as the name value unless its + explicitly set. +*/}} +{{- if eq (typeOf $value) "map[string]interface {}" }} +{{- merge (dict) $value (dict "name" $key) | list | toYaml | println }} +{{- /* + - If extraEnv.someKey has a string value, then we use the key as the + environment variable name for the value. +*/}} +{{- else if eq (typeOf $value) "string" -}} +- name: {{ $key | quote }} + value: {{ $value | quote | println }} +{{- else }} +{{- printf "?.extraEnv.%s had an unexpected type (%s)" $key (typeOf $value) | fail }} +{{- end }} +{{- end }} {{- /* end of range */}} +{{- end }} +{{- end }} {{- /* end of: if . */}} +{{- end }} {{- /* end of definition */}} + +{{- /* + jupyterhub.extraFiles.data: + Renders content for a k8s Secret's data field, coming from extraFiles with + binaryData entries. +*/}} +{{- define "jupyterhub.extraFiles.data.withNewLineSuffix" -}} + {{- range $file_key, $file_details := . }} + {{- include "jupyterhub.extraFiles.validate-file" (list $file_key $file_details) }} + {{- if $file_details.binaryData }} + {{- $file_key | quote }}: {{ $file_details.binaryData | nospace | quote }}{{ println }} + {{- end }} + {{- end }} +{{- end }} +{{- define "jupyterhub.extraFiles.data" -}} + {{- include "jupyterhub.extraFiles.data.withNewLineSuffix" . | trimSuffix "\n" }} +{{- end }} + +{{- /* + jupyterhub.extraFiles.stringData: + Renders content for a k8s Secret's stringData field, coming from extraFiles + with either data or stringData entries. +*/}} +{{- define "jupyterhub.extraFiles.stringData.withNewLineSuffix" -}} + {{- range $file_key, $file_details := . }} + {{- include "jupyterhub.extraFiles.validate-file" (list $file_key $file_details) }} + {{- $file_name := $file_details.mountPath | base }} + {{- if $file_details.stringData }} + {{- $file_key | quote }}: | + {{- $file_details.stringData | trimSuffix "\n" | nindent 2 }}{{ println }} + {{- end }} + {{- if $file_details.data }} + {{- $file_key | quote }}: | + {{- if or (eq (ext $file_name) ".yaml") (eq (ext $file_name) ".yml") }} + {{- $file_details.data | toYaml | nindent 2 }}{{ println }} + {{- else if eq (ext $file_name) ".json" }} + {{- $file_details.data | toJson | nindent 2 }}{{ println }} + {{- else if eq (ext $file_name) ".toml" }} + {{- $file_details.data | toToml | trimSuffix "\n" | nindent 2 }}{{ println }} + {{- else }} + {{- print "\n\nextraFiles entries with 'data' (" $file_key " > " $file_details.mountPath ") needs to have a filename extension of .yaml, .yml, .json, or .toml!" | fail }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} +{{- define "jupyterhub.extraFiles.stringData" -}} + {{- include "jupyterhub.extraFiles.stringData.withNewLineSuffix" . | trimSuffix "\n" }} +{{- end }} + +{{- define "jupyterhub.extraFiles.validate-file" -}} + {{- $file_key := index . 0 }} + {{- $file_details := index . 1 }} + + {{- /* Use of mountPath. */}} + {{- if not ($file_details.mountPath) }} + {{- print "\n\nextraFiles entries (" $file_key ") must contain the field 'mountPath'." | fail }} + {{- end }} + + {{- /* Use one of stringData, binaryData, data. */}} + {{- $field_count := 0 }} + {{- if $file_details.data }} + {{- $field_count = add1 $field_count }} + {{- end }} + {{- if $file_details.stringData }} + {{- $field_count = add1 $field_count }} + {{- end }} + {{- if $file_details.binaryData }} + {{- $field_count = add1 $field_count }} + {{- end }} + {{- if ne $field_count 1 }} + {{- print "\n\nextraFiles entries (" $file_key ") must only contain one of the fields: 'data', 'stringData', and 'binaryData'." | fail }} + {{- end }} +{{- end }} + +{{- /* + jupyterhub.chart-version-to-git-ref: + Renders a valid git reference from a chartpress generated version string. + In practice, either a git tag or a git commit hash will be returned. + + - The version string will follow a chartpress pattern, see + https://github.com/jupyterhub/chartpress#examples-chart-versions-and-image-tags. + + - The regexReplaceAll function is a sprig library function, see + https://masterminds.github.io/sprig/strings.html. + + - The regular expression is in golang syntax, but \d had to become \\d for + example. +*/}} +{{- define "jupyterhub.chart-version-to-git-ref" -}} +{{- regexReplaceAll ".*[.-]n\\d+[.]h(.*)" . "${1}" }} +{{- end }} diff --git a/jupyterhub/templates/hub/_helpers-passwords.tpl b/jupyterhub/templates/hub/_helpers-passwords.tpl new file mode 100644 index 0000000..83edf70 --- /dev/null +++ b/jupyterhub/templates/hub/_helpers-passwords.tpl @@ -0,0 +1,92 @@ +{{- /* + This file contains logic to lookup already + generated passwords or generate a new. + + proxy.secretToken / hub.config.ConfigurableHTTPProxy.auth_token + hub.cookieSecret / hub.config.JupyterHub.cookie_secret + auth.state.cryptoKey* / hub.config.CryptKeeper.keys + + *Note that the entire auth section is deprecated and users + are forced through "fail" in NOTES.txt to migrate to hub.config. + + Note that lookup logic returns falsy value when run with + `helm diff upgrade`, so it is a bit troublesome to test. +*/}} + +{{- /* + Returns given number of random Hex characters. + + - randNumeric 4 | atoi generates a random number in [0, 10^4) + This is a range range evenly divisble by 16, but even if off by one, + that last partial interval offsetting randomness is only 1 part in 625. + - mod N 16 maps to the range 0-15 + - printf "%x" represents a single number 0-15 as a single hex character +*/}} +{{- define "jupyterhub.randHex" -}} + {{- $result := "" }} + {{- range $i := until . }} + {{- $rand_hex_char := mod (randNumeric 4 | atoi) 16 | printf "%x" }} + {{- $result = print $result $rand_hex_char }} + {{- end }} + {{- $result }} +{{- end }} + +{{- define "jupyterhub.hub.config.ConfigurableHTTPProxy.auth_token" -}} + {{- if (.Values.hub.config | dig "ConfigurableHTTPProxy" "auth_token" "") }} + {{- .Values.hub.config.ConfigurableHTTPProxy.auth_token }} + {{- else if .Values.proxy.secretToken }} + {{- .Values.proxy.secretToken }} + {{- else }} + {{- $k8s_state := lookup "v1" "Secret" .Release.Namespace (include "jupyterhub.hub.fullname" .) | default (dict "data" (dict)) }} + {{- if hasKey $k8s_state.data "hub.config.ConfigurableHTTPProxy.auth_token" }} + {{- index $k8s_state.data "hub.config.ConfigurableHTTPProxy.auth_token" | b64dec }} + {{- else }} + {{- randAlphaNum 64 }} + {{- end }} + {{- end }} +{{- end }} + +{{- define "jupyterhub.hub.config.JupyterHub.cookie_secret" -}} + {{- if (.Values.hub.config | dig "JupyterHub" "cookie_secret" "") }} + {{- .Values.hub.config.JupyterHub.cookie_secret }} + {{- else if .Values.hub.cookieSecret }} + {{- .Values.hub.cookieSecret }} + {{- else }} + {{- $k8s_state := lookup "v1" "Secret" .Release.Namespace (include "jupyterhub.hub.fullname" .) | default (dict "data" (dict)) }} + {{- if hasKey $k8s_state.data "hub.config.JupyterHub.cookie_secret" }} + {{- index $k8s_state.data "hub.config.JupyterHub.cookie_secret" | b64dec }} + {{- else }} + {{- include "jupyterhub.randHex" 64 }} + {{- end }} + {{- end }} +{{- end }} + +{{- define "jupyterhub.hub.config.CryptKeeper.keys" -}} + {{- if (.Values.hub.config | dig "CryptKeeper" "keys" "") }} + {{- .Values.hub.config.CryptKeeper.keys | join ";" }} + {{- else }} + {{- $k8s_state := lookup "v1" "Secret" .Release.Namespace (include "jupyterhub.hub.fullname" .) | default (dict "data" (dict)) }} + {{- if hasKey $k8s_state.data "hub.config.CryptKeeper.keys" }} + {{- index $k8s_state.data "hub.config.CryptKeeper.keys" | b64dec }} + {{- else }} + {{- include "jupyterhub.randHex" 64 }} + {{- end }} + {{- end }} +{{- end }} + +{{- define "jupyterhub.hub.services.get_api_token" -}} + {{- $_ := index . 0 }} + {{- $service_key := index . 1 }} + {{- $explicitly_set_api_token := or ($_.Values.hub.services | dig $service_key "api_token" "") ($_.Values.hub.services | dig $service_key "apiToken" "") }} + {{- if $explicitly_set_api_token }} + {{- $explicitly_set_api_token }} + {{- else }} + {{- $k8s_state := lookup "v1" "Secret" $_.Release.Namespace (include "jupyterhub.hub.fullname" $_) | default (dict "data" (dict)) }} + {{- $k8s_secret_key := print "hub.services." $service_key ".apiToken" }} + {{- if hasKey $k8s_state.data $k8s_secret_key }} + {{- index $k8s_state.data $k8s_secret_key | b64dec }} + {{- else }} + {{- include "jupyterhub.randHex" 64 }} + {{- end }} + {{- end }} +{{- end }} diff --git a/jupyterhub/templates/hub/configmap-theme.yaml b/jupyterhub/templates/hub/configmap-theme.yaml new file mode 100644 index 0000000..8d744b7 --- /dev/null +++ b/jupyterhub/templates/hub/configmap-theme.yaml @@ -0,0 +1,22 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: {{ include "jupyterhub.hub.fullname" . }}-theme + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +data: + {{- /* + Glob files to allow them to be mounted by the hub pod + + jupyterhub_config: | + multi line string content... + z2jh.py: | + multi line string content... + */}} + {{- (.Files.Glob "files/theme/*").AsConfig | nindent 2 }} + + {{- /* + Store away a checksum of the hook-image-puller daemonset so future upgrades + can compare and decide if it should run or not using the `lookup` function. + */}} + checksum_hook-image-puller: {{ include "jupyterhub.imagePuller.daemonset.hook.checksum" . | quote }} diff --git a/jupyterhub/templates/hub/configmap.yaml b/jupyterhub/templates/hub/configmap.yaml new file mode 100644 index 0000000..128a6a0 --- /dev/null +++ b/jupyterhub/templates/hub/configmap.yaml @@ -0,0 +1,30 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: {{ include "jupyterhub.hub.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +data: + {{- /* + Resource names exposed to reliably reference them. + + user-scheduler: "my-helm-release-user-scheduler" + ... + */}} + {{- include "jupyterhub.name-templates" . | nindent 2 }} + + {{- /* + Glob files to allow them to be mounted by the hub pod + + jupyterhub_config: | + multi line string content... + z2jh.py: | + multi line string content... + */}} + {{- (.Files.Glob "files/hub/*").AsConfig | nindent 2 }} + + {{- /* + Store away a checksum of the hook-image-puller daemonset so future upgrades + can compare and decide if it should run or not using the `lookup` function. + */}} + checksum_hook-image-puller: {{ include "jupyterhub.imagePuller.daemonset.hook.checksum" . | quote }} diff --git a/jupyterhub/templates/hub/deployment.yaml b/jupyterhub/templates/hub/deployment.yaml new file mode 100644 index 0000000..dd46d2f --- /dev/null +++ b/jupyterhub/templates/hub/deployment.yaml @@ -0,0 +1,252 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "jupyterhub.hub.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +spec: + {{- if typeIs "int" .Values.hub.revisionHistoryLimit }} + revisionHistoryLimit: {{ .Values.hub.revisionHistoryLimit }} + {{- end }} + replicas: 1 + selector: + matchLabels: + {{- include "jupyterhub.matchLabels" . | nindent 6 }} + strategy: + {{- .Values.hub.deploymentStrategy | toYaml | nindent 4 }} + template: + metadata: + labels: + {{- /* Changes here will cause the Deployment to restart the pods. */}} + {{- include "jupyterhub.matchLabels" . | nindent 8 }} + hub.jupyter.org/network-access-proxy-api: "true" + hub.jupyter.org/network-access-proxy-http: "true" + hub.jupyter.org/network-access-singleuser: "true" + {{- with .Values.hub.labels }} + {{- . | toYaml | nindent 8 }} + {{- end }} + annotations: + {{- /* This lets us autorestart when the secret changes! */}} + checksum/configmap: {{ include (print .Template.BasePath "/hub/configmap.yaml") . | sha256sum }} + checksum/theme: {{ include (print .Template.BasePath "/hub/configmap-theme.yaml") . | sha256sum }} + checksum/secret: {{ include (print .Template.BasePath "/hub/secret.yaml") . | sha256sum }} + {{- with .Values.hub.annotations }} + {{- . | toYaml | nindent 8 }} + {{- end }} + spec: + {{- if .Values.scheduling.podPriority.enabled }} + priorityClassName: {{ include "jupyterhub.priority.fullname" . }} + {{- end }} + {{- with .Values.hub.nodeSelector }} + nodeSelector: + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- with concat .Values.scheduling.corePods.tolerations .Values.hub.tolerations }} + tolerations: + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- include "jupyterhub.coreAffinity" . | nindent 6 }} + volumes: + - name: theme + configMap: + name: {{ include "jupyterhub.hub.fullname" . }}-theme + - name: config + configMap: + name: {{ include "jupyterhub.hub.fullname" . }} + - name: secret + secret: + secretName: {{ include "jupyterhub.hub.fullname" . }} + {{- with (include "jupyterhub.hub-existing-secret.fullname" .) }} + - name: existing-secret + secret: + secretName: {{ . }} + {{- end }} + {{- if .Values.hub.extraFiles }} + - name: files + secret: + secretName: {{ include "jupyterhub.hub.fullname" . }} + items: + {{- range $file_key, $file_details := .Values.hub.extraFiles }} + - key: {{ $file_key | quote }} + path: {{ $file_key | quote }} + {{- with $file_details.mode }} + mode: {{ . }} + {{- end }} + {{- end }} + {{- end }} + {{- with .Values.hub.extraVolumes }} + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- if eq .Values.hub.db.type "sqlite-pvc" }} + - name: pvc + persistentVolumeClaim: + claimName: {{ include "jupyterhub.hub-pvc.fullname" . }} + {{- end }} + {{- with include "jupyterhub.hub-serviceaccount.fullname" . }} + serviceAccountName: {{ . }} + {{- end }} + {{- with .Values.hub.podSecurityContext }} + securityContext: + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- with include "jupyterhub.imagePullSecrets" (dict "root" . "image" .Values.hub.image) }} + imagePullSecrets: {{ . }} + {{- end }} + {{- with .Values.hub.initContainers }} + initContainers: + {{- . | toYaml | nindent 8 }} + {{- end }} + containers: + {{- with .Values.hub.extraContainers }} + {{- . | toYaml | nindent 8 }} + {{- end }} + - name: hub + image: {{ .Values.hub.image.name }}:{{ .Values.hub.image.tag }} + {{- with .Values.hub.command }} + command: + {{- range . }} + - {{ tpl . $ }} + {{- end }} + {{- end }} + args: + {{- /* .Values.hub.args overrides everything the Helm chart otherside would set */}} + {{- if .Values.hub.args }} + {{- range .Values.hub.args }} + - {{ tpl . $ }} + {{- end }} + + {{- /* .Values.hub.args didn't replace the default logic */}} + {{- else }} + - jupyterhub + - --config + - /usr/local/etc/jupyterhub/jupyterhub_config.py + {{- if .Values.debug.enabled }} + - --debug + {{- end }} + {{- /* NOTE: + We want to do automatic upgrades for sqlite-pvc by default, but + allow users to opt out of that if they want. Users using their own + db need to 'opt in' Go Templates treat nil and "" and false as + 'false', making this code complex. We can probably make this a + one-liner, but doing combinations of boolean vars in go templates + is very inelegant & hard to reason about. + */}} + {{- $upgradeType := typeOf .Values.hub.db.upgrade }} + {{- if eq $upgradeType "bool" }} + {{- /* .Values.hub.db.upgrade has been explicitly set to true or false */}} + {{- if .Values.hub.db.upgrade }} + - --upgrade-db + {{- end }} + {{- else if eq $upgradeType "" }} + {{- /* .Values.hub.db.upgrade is nil */}} + {{- if eq .Values.hub.db.type "sqlite-pvc" }} + - --upgrade-db + {{- end }} + {{- end }} + {{- end }} + volumeMounts: + - mountPath: /usr/local/etc/jupyterhub/config.yml + subPath: config.yml + name: config + - mountPath: /usr/local/etc/jupyterhub/jupyterhub_config.py + subPath: jupyterhub_config.py + name: config + - mountPath: /usr/local/etc/jupyterhub/z2jh.py + subPath: z2jh.py + name: config + - mountPath: /usr/local/etc/jupyterhub/config/ + name: config + - mountPath: /opt/jupyterhub/template/ + name: theme + - mountPath: /usr/local/etc/jupyterhub/secret/ + name: secret + {{- if (include "jupyterhub.hub-existing-secret.fullname" .) }} + - mountPath: /usr/local/etc/jupyterhub/existing-secret/ + name: existing-secret + {{- end }} + {{- range $file_key, $file_details := .Values.hub.extraFiles }} + - mountPath: {{ $file_details.mountPath }} + subPath: {{ $file_key | quote }} + name: files + {{- end }} + {{- with .Values.hub.extraVolumeMounts }} + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- if eq .Values.hub.db.type "sqlite-pvc" }} + - mountPath: /srv/jupyterhub + name: pvc + {{- with .Values.hub.db.pvc.subPath }} + subPath: {{ . | quote }} + {{- end }} + {{- end }} + {{- with .Values.hub.resources }} + resources: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- with .Values.hub.image.pullPolicy }} + imagePullPolicy: {{ . }} + {{- end }} + {{- with .Values.hub.containerSecurityContext }} + securityContext: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- with .Values.hub.lifecycle }} + lifecycle: + {{- . | toYaml | nindent 12 }} + {{- end }} + env: + - name: PYTHONUNBUFFERED + value: "1" + - name: HELM_RELEASE_NAME + value: {{ .Release.Name | quote }} + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: CONFIGPROXY_AUTH_TOKEN + valueFrom: + secretKeyRef: + {{- /* NOTE: + References the chart managed k8s Secret even if + hub.existingSecret is specified to avoid using the lookup + function on the user managed k8s Secret which is assumed to + not be possible. + */}} + name: {{ include "jupyterhub.hub.fullname" . }} + key: hub.config.ConfigurableHTTPProxy.auth_token + {{- with .Values.hub.extraEnv }} + {{- include "jupyterhub.extraEnv" . | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 8081 + {{- if .Values.hub.livenessProbe.enabled }} + {{- /* NOTE: + We don't know how long hub database upgrades could take so having a + liveness probe could be a bit risky unless we put a + initialDelaySeconds value with long enough margin for that to not be + an issue. If it is too short, we could end up aborting database + upgrades midway or ending up in an infinite restart loop. + */}} + livenessProbe: + initialDelaySeconds: {{ .Values.hub.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.hub.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.hub.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.hub.livenessProbe.failureThreshold }} + httpGet: + path: {{ .Values.hub.baseUrl | trimSuffix "/" }}/hub/health + port: http + {{- end }} + {{- if .Values.hub.readinessProbe.enabled }} + readinessProbe: + initialDelaySeconds: {{ .Values.hub.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.hub.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.hub.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.hub.readinessProbe.failureThreshold }} + httpGet: + path: {{ .Values.hub.baseUrl | trimSuffix "/" }}/hub/health + port: http + {{- end }} + {{- with .Values.hub.extraPodSpec }} + {{- . | toYaml | nindent 6 }} + {{- end }} diff --git a/jupyterhub/templates/hub/netpol.yaml b/jupyterhub/templates/hub/netpol.yaml new file mode 100644 index 0000000..904b2c3 --- /dev/null +++ b/jupyterhub/templates/hub/netpol.yaml @@ -0,0 +1,84 @@ +{{- if .Values.hub.networkPolicy.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ include "jupyterhub.hub.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + {{- include "jupyterhub.matchLabels" . | nindent 6 }} + policyTypes: + - Ingress + - Egress + + # IMPORTANT: + # NetworkPolicy's ingress "from" and egress "to" rule specifications require + # great attention to detail. A quick summary is: + # + # 1. You can provide "from"/"to" rules that provide access either ports or a + # subset of ports. + # 2. You can for each "from"/"to" rule provide any number of + # "sources"/"destinations" of four different kinds. + # - podSelector - targets pods with a certain label in the same namespace as the NetworkPolicy + # - namespaceSelector - targets all pods running in namespaces with a certain label + # - namespaceSelector and podSelector - targets pods with a certain label running in namespaces with a certain label + # - ipBlock - targets network traffic from/to a set of IP address ranges + # + # Read more at: https://kubernetes.io/docs/concepts/services-networking/network-policies/#behavior-of-to-and-from-selectors + # + ingress: + {{- with .Values.hub.networkPolicy.allowedIngressPorts }} + # allow incoming traffic to these ports independent of source + - ports: + {{- range $port := . }} + - port: {{ $port }} + {{- end }} + {{- end }} + + # allowed pods (hub.jupyter.org/network-access-hub) --> hub + - ports: + - port: http + from: + # source 1 - labeled pods + - podSelector: + matchLabels: + hub.jupyter.org/network-access-hub: "true" + {{- if eq .Values.hub.networkPolicy.interNamespaceAccessLabels "accept" }} + namespaceSelector: + matchLabels: {} # without this, the podSelector would only consider pods in the local namespace + # source 2 - pods in labeled namespaces + - namespaceSelector: + matchLabels: + hub.jupyter.org/network-access-hub: "true" + {{- end }} + + {{- with .Values.hub.networkPolicy.ingress }} + # depends, but default is nothing --> hub + {{- . | toYaml | nindent 4 }} + {{- end }} + + egress: + # hub --> proxy + - to: + - podSelector: + matchLabels: + {{- $_ := merge (dict "componentLabel" "proxy") . }} + {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} + ports: + - port: 8001 + + # hub --> singleuser-server + - to: + - podSelector: + matchLabels: + {{- $_ := merge (dict "componentLabel" "singleuser-server") . }} + {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} + ports: + - port: 8888 + + {{- with (include "jupyterhub.networkPolicy.renderEgressRules" (list . .Values.hub.networkPolicy)) }} + {{- . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/jupyterhub/templates/hub/pdb.yaml b/jupyterhub/templates/hub/pdb.yaml new file mode 100644 index 0000000..3a22e39 --- /dev/null +++ b/jupyterhub/templates/hub/pdb.yaml @@ -0,0 +1,23 @@ +{{- if .Values.hub.pdb.enabled -}} +{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} +{{- /* k8s 1.21+ required */ -}} +apiVersion: policy/v1 +{{- else }} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ include "jupyterhub.hub.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +spec: + {{- if not (typeIs "" .Values.hub.pdb.maxUnavailable) }} + maxUnavailable: {{ .Values.hub.pdb.maxUnavailable }} + {{- end }} + {{- if not (typeIs "" .Values.hub.pdb.minAvailable) }} + minAvailable: {{ .Values.hub.pdb.minAvailable }} + {{- end }} + selector: + matchLabels: + {{- include "jupyterhub.matchLabels" . | nindent 6 }} +{{- end }} diff --git a/jupyterhub/templates/hub/pvc.yaml b/jupyterhub/templates/hub/pvc.yaml new file mode 100644 index 0000000..a433a97 --- /dev/null +++ b/jupyterhub/templates/hub/pvc.yaml @@ -0,0 +1,25 @@ +{{- if eq .Values.hub.db.type "sqlite-pvc" -}} +kind: PersistentVolumeClaim +apiVersion: v1 +metadata: + name: {{ include "jupyterhub.hub-pvc.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} + {{- with .Values.hub.db.pvc.annotations }} + annotations: + {{- . | toYaml | nindent 4 }} + {{- end }} +spec: + {{- with .Values.hub.db.pvc.selector }} + selector: + {{- . | toYaml | nindent 4 }} + {{- end }} + {{- if typeIs "string" .Values.hub.db.pvc.storageClassName }} + storageClassName: {{ .Values.hub.db.pvc.storageClassName | quote }} + {{- end }} + accessModes: + {{- .Values.hub.db.pvc.accessModes | toYaml | nindent 4 }} + resources: + requests: + storage: {{ .Values.hub.db.pvc.storage | quote }} +{{- end }} diff --git a/jupyterhub/templates/hub/rbac.yaml b/jupyterhub/templates/hub/rbac.yaml new file mode 100644 index 0000000..3abf00e --- /dev/null +++ b/jupyterhub/templates/hub/rbac.yaml @@ -0,0 +1,30 @@ +{{- if .Values.rbac.create -}} +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "jupyterhub.hub.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +rules: + - apiGroups: [""] # "" indicates the core API group + resources: ["pods", "persistentvolumeclaims", "secrets", "services"] + verbs: ["get", "watch", "list", "create", "delete"] + - apiGroups: [""] # "" indicates the core API group + resources: ["events"] + verbs: ["get", "watch", "list"] +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "jupyterhub.hub.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ include "jupyterhub.hub-serviceaccount.fullname" . }} + namespace: "{{ .Release.Namespace }}" +roleRef: + kind: Role + name: {{ include "jupyterhub.hub.fullname" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} diff --git a/jupyterhub/templates/hub/secret.yaml b/jupyterhub/templates/hub/secret.yaml new file mode 100644 index 0000000..851bda0 --- /dev/null +++ b/jupyterhub/templates/hub/secret.yaml @@ -0,0 +1,50 @@ +kind: Secret +apiVersion: v1 +metadata: + name: {{ include "jupyterhub.hub.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +type: Opaque +data: + {{- $values := merge dict .Values }} + {{- /* also passthrough subset of Chart / Release */}} + {{- $_ := set $values "Chart" (dict "Name" .Chart.Name "Version" .Chart.Version) }} + {{- $_ := set $values "Release" (pick .Release "Name" "Namespace" "Service") }} + values.yaml: {{ $values | toYaml | b64enc | quote }} + + {{- with .Values.hub.db.password }} + # Used to mount MYSQL_PWD or PGPASSWORD on hub pod, unless hub.existingSecret + # is set as then that k8s Secret's value must be specified instead. + hub.db.password: {{ . | b64enc | quote }} + {{- end }} + + # Any JupyterHub Services api_tokens are exposed in this k8s Secret as a + # convinience for external services running in the k8s cluster that could + # mount them directly from this k8s Secret. + {{- range $key, $service := .Values.hub.services }} + hub.services.{{ $key }}.apiToken: {{ include "jupyterhub.hub.services.get_api_token" (list $ $key) | b64enc | quote }} + {{- end }} + + # During Helm template rendering, these values that can be autogenerated for + # users are set using the following logic: + # + # 1. Use chart configuration's value + # 2. Use k8s Secret's value + # 3. Use a new autogenerated value + # + # hub.config.ConfigurableHTTPProxy.auth_token: for hub to proxy-api authorization (JupyterHub.proxy_auth_token is deprecated) + # hub.config.JupyterHub.cookie_secret: for cookie encryption + # hub.config.CryptKeeper.keys: for auth state encryption + # + hub.config.ConfigurableHTTPProxy.auth_token: {{ include "jupyterhub.hub.config.ConfigurableHTTPProxy.auth_token" . | required "This should not happen: blank output from 'jupyterhub.hub.config.ConfigurableHTTPProxy.auth_token' template" | b64enc | quote }} + hub.config.JupyterHub.cookie_secret: {{ include "jupyterhub.hub.config.JupyterHub.cookie_secret" . | required "This should not happen: blank output from 'jupyterhub.hub.config.JupyterHub.cookie_secret' template" | b64enc | quote }} + hub.config.CryptKeeper.keys: {{ include "jupyterhub.hub.config.CryptKeeper.keys" . | required "This should not happen: blank output from 'jupyterhub.hub.config.CryptKeeper.keys' template" | b64enc | quote }} + + {{- with include "jupyterhub.extraFiles.data" .Values.hub.extraFiles }} + {{- . | nindent 2 }} + {{- end }} + +{{- with include "jupyterhub.extraFiles.stringData" .Values.hub.extraFiles }} +stringData: + {{- . | nindent 2 }} +{{- end }} diff --git a/jupyterhub/templates/hub/service.yaml b/jupyterhub/templates/hub/service.yaml new file mode 100644 index 0000000..13f80b5 --- /dev/null +++ b/jupyterhub/templates/hub/service.yaml @@ -0,0 +1,37 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "jupyterhub.hub.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} + annotations: + {{- if not (index .Values.hub.service.annotations "prometheus.io/scrape") }} + prometheus.io/scrape: "true" + {{- end }} + {{- if not (index .Values.hub.service.annotations "prometheus.io/path") }} + prometheus.io/path: {{ .Values.hub.baseUrl | trimSuffix "/" }}/hub/metrics + {{- end }} + {{- if not (index .Values.hub.service.annotations "prometheus.io/port") }} + prometheus.io/port: "8081" + {{- end }} + {{- with .Values.hub.service.annotations }} + {{- . | toYaml | nindent 4 }} + {{- end }} +spec: + type: {{ .Values.hub.service.type }} + {{- with .Values.hub.service.loadBalancerIP }} + loadBalancerIP: {{ . }} + {{- end }} + selector: + {{- include "jupyterhub.matchLabels" . | nindent 4 }} + ports: + - name: hub + port: 8081 + targetPort: http + {{- with .Values.hub.service.ports.nodePort }} + nodePort: {{ . }} + {{- end }} + + {{- with .Values.hub.service.extraPorts }} + {{- . | toYaml | nindent 4 }} + {{- end }} diff --git a/jupyterhub/templates/hub/serviceaccount.yaml b/jupyterhub/templates/hub/serviceaccount.yaml new file mode 100644 index 0000000..06a5069 --- /dev/null +++ b/jupyterhub/templates/hub/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.hub.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "jupyterhub.hub-serviceaccount.fullname" . }} + {{- with .Values.hub.serviceAccount.annotations }} + annotations: + {{- . | toYaml | nindent 4 }} + {{- end }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +{{- end }} diff --git a/jupyterhub/templates/image-pull-secret.yaml b/jupyterhub/templates/image-pull-secret.yaml new file mode 100644 index 0000000..e033ec6 --- /dev/null +++ b/jupyterhub/templates/image-pull-secret.yaml @@ -0,0 +1,15 @@ +{{- if .Values.imagePullSecret.create }} +kind: Secret +apiVersion: v1 +metadata: + name: {{ include "jupyterhub.image-pull-secret.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation + "helm.sh/hook-weight": "-20" +type: kubernetes.io/dockerconfigjson +data: + .dockerconfigjson: {{ include "jupyterhub.dockerconfigjson" . }} +{{- end }} diff --git a/jupyterhub/templates/image-puller/_helpers-daemonset.tpl b/jupyterhub/templates/image-puller/_helpers-daemonset.tpl new file mode 100644 index 0000000..1fe8276 --- /dev/null +++ b/jupyterhub/templates/image-puller/_helpers-daemonset.tpl @@ -0,0 +1,251 @@ +{{- /* +Returns an image-puller daemonset. Two daemonsets will be created like this. +- hook-image-puller: for pre helm upgrade image pulling (lives temporarily) +- continuous-image-puller: for newly added nodes image pulling +*/}} +{{- define "jupyterhub.imagePuller.daemonset" -}} +apiVersion: apps/v1 +kind: DaemonSet +metadata: + {{- if .hook }} + name: {{ include "jupyterhub.hook-image-puller.fullname" . }} + {{- else }} + name: {{ include "jupyterhub.continuous-image-puller.fullname" . }} + {{- end }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} + {{- if .hook }} + hub.jupyter.org/deletable: "true" + {{- end }} + {{- if .hook }} + annotations: + {{- /* + Allows the daemonset to be deleted when the image-awaiter job is completed. + */}} + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + "helm.sh/hook-weight": "-10" + {{- end }} +spec: + selector: + matchLabels: + {{- include "jupyterhub.matchLabels" . | nindent 6 }} + updateStrategy: + type: RollingUpdate + rollingUpdate: + maxUnavailable: 100% + {{- if typeIs "int" .Values.prePuller.revisionHistoryLimit }} + revisionHistoryLimit: {{ .Values.prePuller.revisionHistoryLimit }} + {{- end }} + template: + metadata: + labels: + {{- include "jupyterhub.matchLabels" . | nindent 8 }} + {{- with .Values.prePuller.annotations }} + annotations: + {{- . | toYaml | nindent 8 }} + {{- end }} + spec: + {{- /* + image-puller pods are made evictable to save on the k8s pods + per node limit all k8s clusters have and have a higher priority + than user-placeholder pods that could block an entire node. + */}} + {{- if .Values.scheduling.podPriority.enabled }} + priorityClassName: {{ include "jupyterhub.image-puller-priority.fullname" . }} + {{- end }} + {{- with .Values.singleuser.nodeSelector }} + nodeSelector: + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- with concat .Values.scheduling.userPods.tolerations .Values.singleuser.extraTolerations .Values.prePuller.extraTolerations }} + tolerations: + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- if include "jupyterhub.userNodeAffinityRequired" . }} + affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + {{- include "jupyterhub.userNodeAffinityRequired" . | nindent 14 }} + {{- end }} + terminationGracePeriodSeconds: 0 + automountServiceAccountToken: false + {{- with include "jupyterhub.imagePullSecrets" (dict "root" . "image" .Values.singleuser.image) }} + imagePullSecrets: {{ . }} + {{- end }} + initContainers: + {{- /* --- Conditionally pull an image all user pods will use in an initContainer --- */}} + {{- $blockWithIptables := hasKey .Values.singleuser.cloudMetadata "enabled" | ternary (not .Values.singleuser.cloudMetadata.enabled) .Values.singleuser.cloudMetadata.blockWithIptables }} + {{- if $blockWithIptables }} + - name: image-pull-metadata-block + image: {{ .Values.singleuser.networkTools.image.name }}:{{ .Values.singleuser.networkTools.image.tag }} + {{- with .Values.singleuser.networkTools.image.pullPolicy }} + imagePullPolicy: {{ . }} + {{- end }} + command: + - /bin/sh + - -c + - echo "Pulling complete" + {{- with .Values.prePuller.resources }} + resources: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- with .Values.prePuller.containerSecurityContext }} + securityContext: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- end }} + + {{- /* --- Pull default image --- */}} + - name: image-pull-singleuser + image: {{ .Values.singleuser.image.name }}:{{ .Values.singleuser.image.tag }} + command: + - /bin/sh + - -c + - echo "Pulling complete" + {{- with .Values.prePuller.resources }} + resources: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- with .Values.prePuller.containerSecurityContext }} + securityContext: + {{- . | toYaml | nindent 12 }} + {{- end }} + + {{- /* --- Pull extra containers' images --- */}} + {{- range $k, $container := concat .Values.singleuser.initContainers .Values.singleuser.extraContainers }} + - name: image-pull-singleuser-init-and-extra-containers-{{ $k }} + image: {{ $container.image }} + command: + - /bin/sh + - -c + - echo "Pulling complete" + {{- with $.Values.prePuller.resources }} + resources: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- with $.Values.prePuller.containerSecurityContext }} + securityContext: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- end }} + + {{- /* --- Conditionally pull profileList images --- */}} + {{- if .Values.prePuller.pullProfileListImages }} + {{- range $k, $container := .Values.singleuser.profileList }} + {{- if $container.kubespawner_override }} + {{- if $container.kubespawner_override.image }} + - name: image-pull-singleuser-profilelist-{{ $k }} + image: {{ $container.kubespawner_override.image }} + command: + - /bin/sh + - -c + - echo "Pulling complete" + {{- with $.Values.prePuller.resources }} + resources: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- with $.Values.prePuller.containerSecurityContext }} + securityContext: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + {{- end }} + + {{- /* --- Pull extra images --- */}} + {{- range $k, $v := .Values.prePuller.extraImages }} + - name: image-pull-{{ $k }} + image: {{ $v.name }}:{{ $v.tag }} + command: + - /bin/sh + - -c + - echo "Pulling complete" + {{- with $.Values.prePuller.resources }} + resources: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- with $.Values.prePuller.containerSecurityContext }} + securityContext: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- end }} + containers: + - name: pause + image: {{ .Values.prePuller.pause.image.name }}:{{ .Values.prePuller.pause.image.tag }} + {{- with .Values.prePuller.resources }} + resources: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- with .Values.prePuller.pause.containerSecurityContext }} + securityContext: + {{- . | toYaml | nindent 12 }} + {{- end }} +{{- end }} + + +{{- /* + Returns a rendered k8s DaemonSet resource: continuous-image-puller +*/}} +{{- define "jupyterhub.imagePuller.daemonset.continuous" -}} + {{- $_ := merge (dict "hook" false "componentPrefix" "continuous-") . }} + {{- include "jupyterhub.imagePuller.daemonset" $_ }} +{{- end }} + + +{{- /* + Returns a rendered k8s DaemonSet resource: hook-image-puller +*/}} +{{- define "jupyterhub.imagePuller.daemonset.hook" -}} + {{- $_ := merge (dict "hook" true "componentPrefix" "hook-") . }} + {{- include "jupyterhub.imagePuller.daemonset" $_ }} +{{- end }} + + +{{- /* + Returns a checksum of the rendered k8s DaemonSet resource: hook-image-puller + + This checksum is used when prePuller.hook.pullOnlyOnChanges=true to decide if + it is worth creating the hook-image-puller associated resources. +*/}} +{{- define "jupyterhub.imagePuller.daemonset.hook.checksum" -}} + {{- /* + We pin componentLabel and Chart.Version as doing so can pin labels + of no importance if they would change. Chart.Name is also pinned as + a harmless technical workaround when we compute the checksum. + */}} + {{- $_ := merge (dict "componentLabel" "pinned" "Chart" (dict "Name" "jupyterhub" "Version" "pinned")) . -}} + {{- $yaml := include "jupyterhub.imagePuller.daemonset.hook" $_ }} + {{- $yaml | sha256sum }} +{{- end }} + + +{{- /* + Returns a truthy string or a blank string depending on if the + hook-image-puller should be installed. The truthy strings are comments + that summarize the state that led to returning a truthy string. + + - prePuller.hook.enabled must be true + - if prePuller.hook.pullOnlyOnChanges is true, the checksum of the + hook-image-puller daemonset must differ since last upgrade +*/}} +{{- define "jupyterhub.imagePuller.daemonset.hook.install" -}} + {{- if .Values.prePuller.hook.enabled }} + {{- if .Values.prePuller.hook.pullOnlyOnChanges }} + {{- $new_checksum := include "jupyterhub.imagePuller.daemonset.hook.checksum" . }} + {{- $k8s_state := lookup "v1" "ConfigMap" .Release.Namespace (include "jupyterhub.hub.fullname" .) | default (dict "data" (dict)) }} + {{- $old_checksum := index $k8s_state.data "checksum_hook-image-puller" | default "" }} + {{- if ne $new_checksum $old_checksum -}} +# prePuller.hook.enabled={{ .Values.prePuller.hook.enabled }} +# prePuller.hook.pullOnlyOnChanges={{ .Values.prePuller.hook.pullOnlyOnChanges }} +# post-upgrade checksum != pre-upgrade checksum (of the hook-image-puller DaemonSet) +# "{{ $new_checksum }}" != "{{ $old_checksum}}" + {{- end }} + {{- else -}} +# prePuller.hook.enabled={{ .Values.prePuller.hook.enabled }} +# prePuller.hook.pullOnlyOnChanges={{ .Values.prePuller.hook.pullOnlyOnChanges }} + {{- end }} + {{- end }} +{{- end }} diff --git a/jupyterhub/templates/image-puller/daemonset-continuous.yaml b/jupyterhub/templates/image-puller/daemonset-continuous.yaml new file mode 100644 index 0000000..85a572f --- /dev/null +++ b/jupyterhub/templates/image-puller/daemonset-continuous.yaml @@ -0,0 +1,8 @@ +{{- /* +The continuous-image-puller daemonset task is to pull required images to nodes +that are added in between helm upgrades, for example by manually adding a node +or by the cluster autoscaler. +*/}} +{{- if .Values.prePuller.continuous.enabled }} +{{- include "jupyterhub.imagePuller.daemonset.continuous" . }} +{{- end }} diff --git a/jupyterhub/templates/image-puller/daemonset-hook.yaml b/jupyterhub/templates/image-puller/daemonset-hook.yaml new file mode 100644 index 0000000..7e9c2d0 --- /dev/null +++ b/jupyterhub/templates/image-puller/daemonset-hook.yaml @@ -0,0 +1,9 @@ +{{- /* +The hook-image-puller daemonset will be created with the highest priority during +helm upgrades. It's task is to pull the required images on all nodes. When the +image-awaiter job confirms the required images to be pulled, the daemonset is +deleted. Only then will the actual helm upgrade start. +*/}} +{{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) -}} +{{- include "jupyterhub.imagePuller.daemonset.hook" . }} +{{- end }} diff --git a/jupyterhub/templates/image-puller/job.yaml b/jupyterhub/templates/image-puller/job.yaml new file mode 100644 index 0000000..5509f13 --- /dev/null +++ b/jupyterhub/templates/image-puller/job.yaml @@ -0,0 +1,76 @@ +{{- /* +This job has a part to play in a helm upgrade process. It simply waits for the +hook-image-puller daemonset which is started slightly before this job to get +its' pods running. If all those pods are running they must have pulled all the +required images on all nodes as they are used as init containers with a dummy +command. +*/}} +{{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) -}} +apiVersion: batch/v1 +kind: Job +metadata: + name: {{ include "jupyterhub.hook-image-awaiter.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} + hub.jupyter.org/deletable: "true" + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + "helm.sh/hook-weight": "10" +spec: + template: + # The hook-image-awaiter Job and hook-image-puller DaemonSet was + # conditionally created based on this state: + # + {{- include "jupyterhub.imagePuller.daemonset.hook.install" . | nindent 4 }} + # + metadata: + labels: + {{- /* Changes here will cause the Job to restart the pods. */}} + {{- include "jupyterhub.matchLabels" . | nindent 8 }} + {{- with .Values.prePuller.labels }} + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- with .Values.prePuller.annotations }} + annotations: + {{- . | toYaml | nindent 8 }} + {{- end }} + spec: + restartPolicy: Never + {{- with include "jupyterhub.hook-image-awaiter-serviceaccount.fullname" . }} + serviceAccountName: {{ . }} + {{- end }} + {{- with .Values.prePuller.hook.nodeSelector }} + nodeSelector: + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- with concat .Values.scheduling.corePods.tolerations .Values.prePuller.hook.tolerations }} + tolerations: + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- with include "jupyterhub.imagePullSecrets" (dict "root" . "image" .Values.prePuller.hook.image) }} + imagePullSecrets: {{ . }} + {{- end }} + containers: + - image: {{ .Values.prePuller.hook.image.name }}:{{ .Values.prePuller.hook.image.tag }} + name: {{ include "jupyterhub.hook-image-awaiter.fullname" . }} + {{- with .Values.prePuller.hook.image.pullPolicy }} + imagePullPolicy: {{ . }} + {{- end }} + command: + - /image-awaiter + - -ca-path=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt + - -auth-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token + - -api-server-address=https://kubernetes.default.svc:$(KUBERNETES_SERVICE_PORT) + - -namespace={{ .Release.Namespace }} + - -daemonset={{ include "jupyterhub.hook-image-puller.fullname" . }} + - -pod-scheduling-wait-duration={{ .Values.prePuller.hook.podSchedulingWaitDuration }} + {{- with .Values.prePuller.hook.containerSecurityContext }} + securityContext: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- with .Values.prePuller.hook.resources }} + resources: + {{- . | toYaml | nindent 12 }} + {{- end }} +{{- end }} diff --git a/jupyterhub/templates/image-puller/priorityclass.yaml b/jupyterhub/templates/image-puller/priorityclass.yaml new file mode 100644 index 0000000..b2dbae0 --- /dev/null +++ b/jupyterhub/templates/image-puller/priorityclass.yaml @@ -0,0 +1,18 @@ +{{- if .Values.scheduling.podPriority.enabled }} +{{- if or .Values.prePuller.hook.enabled .Values.prePuller.continuous.enabled -}} +apiVersion: scheduling.k8s.io/v1 +kind: PriorityClass +metadata: + name: {{ include "jupyterhub.image-puller-priority.fullname" . }} + annotations: + meta.helm.sh/release-name: "{{ .Release.Name }}" + meta.helm.sh/release-namespace: "{{ .Release.Namespace }}" + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +value: {{ .Values.scheduling.podPriority.imagePullerPriority }} +globalDefault: false +description: >- + Enables [hook|continuous]-image-puller pods to fit on nodes even though they + are clogged by user-placeholder pods, while not evicting normal user pods. +{{- end }} +{{- end }} diff --git a/jupyterhub/templates/image-puller/rbac.yaml b/jupyterhub/templates/image-puller/rbac.yaml new file mode 100644 index 0000000..996a59a --- /dev/null +++ b/jupyterhub/templates/image-puller/rbac.yaml @@ -0,0 +1,45 @@ +{{- /* +Permissions to be used by the hook-image-awaiter job +*/}} +{{- if .Values.rbac.create -}} +{{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) -}} +kind: Role +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "jupyterhub.hook-image-awaiter.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} + hub.jupyter.org/deletable: "true" + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + "helm.sh/hook-weight": "0" +rules: + - apiGroups: ["apps"] # "" indicates the core API group + resources: ["daemonsets"] + verbs: ["get"] +--- +{{- /* +... as declared by this binding. +*/}} +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "jupyterhub.hook-image-awaiter.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} + hub.jupyter.org/deletable: "true" + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + "helm.sh/hook-weight": "0" +subjects: + - kind: ServiceAccount + name: {{ include "jupyterhub.hook-image-awaiter-serviceaccount.fullname" . }} + namespace: "{{ .Release.Namespace }}" +roleRef: + kind: Role + name: {{ include "jupyterhub.hook-image-awaiter.fullname" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} +{{- end }} diff --git a/jupyterhub/templates/image-puller/serviceaccount.yaml b/jupyterhub/templates/image-puller/serviceaccount.yaml new file mode 100644 index 0000000..8161101 --- /dev/null +++ b/jupyterhub/templates/image-puller/serviceaccount.yaml @@ -0,0 +1,21 @@ +{{- /* +ServiceAccount for the pre-puller hook's image-awaiter-job +*/}} +{{- if .Values.prePuller.hook.serviceAccount.create -}} +{{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "jupyterhub.hook-image-awaiter-serviceaccount.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} + hub.jupyter.org/deletable: "true" + annotations: + "helm.sh/hook": pre-install,pre-upgrade + "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded + "helm.sh/hook-weight": "0" + {{- with .Values.prePuller.hook.serviceAccount.annotations }} + {{- . | toYaml | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} diff --git a/jupyterhub/templates/ingress.yaml b/jupyterhub/templates/ingress.yaml new file mode 100644 index 0000000..91f96f4 --- /dev/null +++ b/jupyterhub/templates/ingress.yaml @@ -0,0 +1,35 @@ +{{- if .Values.ingress.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: {{ include "jupyterhub.ingress.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- . | toYaml | nindent 4 }} + {{- end }} +spec: + {{- with .Values.ingress.ingressClassName }} + ingressClassName: "{{ . }}" + {{- end }} + rules: + {{- range $host := .Values.ingress.hosts | default (list "") }} + - http: + paths: + - path: {{ $.Values.hub.baseUrl | trimSuffix "/" }}/{{ $.Values.ingress.pathSuffix }} + pathType: {{ $.Values.ingress.pathType }} + backend: + service: + name: {{ include "jupyterhub.proxy-public.fullname" $ }} + port: + name: http + {{- if $host }} + host: {{ $host | quote }} + {{- end }} + {{- end }} + {{- with .Values.ingress.tls }} + tls: + {{- . | toYaml | nindent 4 }} + {{- end }} +{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/_README.txt b/jupyterhub/templates/proxy/autohttps/_README.txt new file mode 100644 index 0000000..eaf1c5c --- /dev/null +++ b/jupyterhub/templates/proxy/autohttps/_README.txt @@ -0,0 +1,9 @@ +# Automatic HTTPS Terminator + +This directory has Kubernetes objects for automatic Let's Encrypt Support. +When enabled, we create a new deployment object that has an nginx-ingress +and kube-lego container in it. This is responsible for requesting, +storing and renewing certificates as needed from Let's Encrypt. + +The only change required outside of this directory is in the `proxy-public` +service, which targets different hubs based on automatic HTTPS status. diff --git a/jupyterhub/templates/proxy/autohttps/_configmap-dynamic.yaml b/jupyterhub/templates/proxy/autohttps/_configmap-dynamic.yaml new file mode 100644 index 0000000..0e2a8f4 --- /dev/null +++ b/jupyterhub/templates/proxy/autohttps/_configmap-dynamic.yaml @@ -0,0 +1,109 @@ +{{- define "jupyterhub.dynamic.yaml" -}} +# Content of dynamic.yaml to be merged merged with +# proxy.traefik.extraDynamicConfig. +# ---------------------------------------------------------------------------- +http: + # Middlewares tweaks requests. We define them here and reference them in + # our routers. We use them to redirect http traffic and headers to proxied + # web requests. + # + # ref: https://docs.traefik.io/middlewares/overview/ + middlewares: + hsts: + # A middleware to add a HTTP Strict-Transport-Security (HSTS) response + # header, they function as a request for browsers to enforce HTTPS on + # their end in for a given time into the future, and optionally + # subdomains for requests to subdomains as well. + # + # ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security + headers: + stsIncludeSubdomains: {{ .Values.proxy.traefik.hsts.includeSubdomains }} + stsPreload: {{ .Values.proxy.traefik.hsts.preload }} + stsSeconds: {{ .Values.proxy.traefik.hsts.maxAge }} + # A middleware to redirect to https + redirect: + redirectScheme: + permanent: true + scheme: https + # A middleware to add a X-Scheme (X-Forwarded-Proto) header that + # JupyterHub's Tornado web-server needs if expecting to serve https + # traffic. Without it we would run into issues like: + # https://github.com/jupyterhub/jupyterhub/issues/2284 + scheme: + headers: + customRequestHeaders: + # DISCUSS ME: Can we use the X-Forwarded-Proto header instead? It + # seems more recognized. Mozilla calls it the de-facto standard + # header for this purpose, and Tornado recognizes both. + # + # ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto + # ref: https://www.tornadoweb.org/en/stable/httpserver.html#http-server + X-Scheme: https + + # Routers routes web requests to a service and optionally tweaks them with + # middleware. + # + # ref: https://docs.traefik.io/routing/routers/ + routers: + # Route secure https traffic to the configurable-http-proxy managed by + # JupyterHub. + default: + entrypoints: + - "https" + middlewares: + - "hsts" + - "scheme" + rule: PathPrefix(`/`) + service: default + # Use our predefined TLS options and certificate resolver, enabling + # this route to act as a TLS termination proxy with high security + # standards. + tls: + certResolver: default + domains: + {{- range $host := .Values.proxy.https.hosts }} + - main: {{ $host }} + {{- end }} + options: default + + # Route insecure http traffic to https + insecure: + entrypoints: + - "http" + middlewares: + - "redirect" + rule: PathPrefix(`/`) + service: default + + # Services represents the destinations we route traffic to. + # + # ref: https://docs.traefik.io/routing/services/ + services: + # Represents the configurable-http-proxy (chp) server that is managed by + # JupyterHub to route traffic both to itself and to user pods. + default: + loadBalancer: + servers: + - url: 'http://proxy-http:8000/' + +# Configure TLS to give us an A+ in the ssllabs.com test +# +# ref: https://www.ssllabs.com/ssltest/ +tls: + options: + default: + # Allowed ciphers adapted from Mozillas SSL Configuration Generator + # configured for Intermediate support which doesn't support very old + # systems but doesn't require very modern either. + # + # ref: https://ssl-config.mozilla.org/#server=traefik&version=2.1.2&config=intermediate&guideline=5.4 + cipherSuites: + - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 + - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 + - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 + - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 + - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 + - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 + minVersion: VersionTLS12 + sniStrict: true +{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/_configmap-traefik.yaml b/jupyterhub/templates/proxy/autohttps/_configmap-traefik.yaml new file mode 100644 index 0000000..7287a70 --- /dev/null +++ b/jupyterhub/templates/proxy/autohttps/_configmap-traefik.yaml @@ -0,0 +1,68 @@ +{{- define "jupyterhub.traefik.yaml" -}} +# Content of traefik.yaml to be merged merged with +# proxy.traefik.extraStaticConfig. +# ---------------------------------------------------------------------------- + +# Config of logs about web requests +# +# ref: https://docs.traefik.io/observability/access-logs/ +accessLog: + # Redact commonly sensitive headers + fields: + headers: + names: + Authorization: redacted + Cookie: redacted + Set-Cookie: redacted + X-Xsrftoken: redacted + # Only log errors + filters: + statusCodes: + - 500-599 + +# Automatically acquire certificates certificates form a Certificate +# Authority (CA) like Let's Encrypt using the ACME protocol's HTTP-01 +# challenge. +# +# ref: https://docs.traefik.io/https/acme/#certificate-resolvers +certificatesResolvers: + default: + acme: + caServer: {{ .Values.proxy.https.letsencrypt.acmeServer }} + email: {{ .Values.proxy.https.letsencrypt.contactEmail }} + httpChallenge: + entryPoint: http + storage: /etc/acme/acme.json + +# Let Traefik listen to port 80 and port 443 +# +# ref: https://docs.traefik.io/routing/entrypoints/ +entryPoints: + # Port 80, used for: + # - ACME HTTP-01 challenges + # - Redirects to HTTPS + http: + address: ':8080' + # Port 443, used for: + # - TLS Termination Proxy, where HTTPS transitions to HTTP. + https: + address: ':8443' + # Configure a high idle timeout for our websockets connections + transport: + respondingTimeouts: + idleTimeout: 10m0s + +# Config of logs about what happens to Traefik itself (startup, +# configuration, events, shutdown, and so on). +# +# ref: https://docs.traefik.io/observability/logs +log: + level: {{ if .Values.debug.enabled -}} DEBUG {{- else -}} WARN {{- end }} + +# Let Traefik monitor another file we mount for dynamic configuration. As we +# mount this file through this configmap, we can make a `kubectl edit` on the +# configmap and have Traefik update on changes to dynamic.yaml. +providers: + file: + filename: /etc/traefik/dynamic.yaml +{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/configmap.yaml b/jupyterhub/templates/proxy/autohttps/configmap.yaml new file mode 100644 index 0000000..4804bf7 --- /dev/null +++ b/jupyterhub/templates/proxy/autohttps/configmap.yaml @@ -0,0 +1,28 @@ +{{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) }} +{{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) }} +{{- if $autoHTTPS -}} +{{- $_ := .Values.proxy.https.letsencrypt.contactEmail | required "proxy.https.letsencrypt.contactEmail is a required field" -}} + +# This configmap contains Traefik configuration files to be mounted. +# - traefik.yaml will only be read during startup (static configuration) +# - dynamic.yaml will be read on change (dynamic configuration) +# +# ref: https://docs.traefik.io/getting-started/configuration-overview/ +# +# The configuration files are first rendered with Helm templating to large YAML +# strings. Then we use the fromYAML function on these strings to get an object, +# that we in turn merge with user provided extra configuration. +# +kind: ConfigMap +apiVersion: v1 +metadata: + name: {{ include "jupyterhub.autohttps.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +data: + traefik.yaml: | + {{- include "jupyterhub.traefik.yaml" . | fromYaml | merge .Values.proxy.traefik.extraStaticConfig | toYaml | nindent 4 }} + dynamic.yaml: | + {{- include "jupyterhub.dynamic.yaml" . | fromYaml | merge .Values.proxy.traefik.extraDynamicConfig | toYaml | nindent 4 }} + +{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/deployment.yaml b/jupyterhub/templates/proxy/autohttps/deployment.yaml new file mode 100644 index 0000000..f76f3ef --- /dev/null +++ b/jupyterhub/templates/proxy/autohttps/deployment.yaml @@ -0,0 +1,154 @@ +{{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) }} +{{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) }} +{{- if $autoHTTPS -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "jupyterhub.autohttps.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +spec: + {{- if typeIs "int" .Values.proxy.traefik.revisionHistoryLimit }} + revisionHistoryLimit: {{ .Values.proxy.traefik.revisionHistoryLimit }} + {{- end }} + replicas: 1 + selector: + matchLabels: + {{- include "jupyterhub.matchLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "jupyterhub.matchLabels" . | nindent 8 }} + hub.jupyter.org/network-access-proxy-http: "true" + {{- with .Values.proxy.traefik.labels }} + {{- . | toYaml | nindent 8 }} + {{- end }} + annotations: + # Only force a restart through a change to this checksum when the static + # configuration is changed, as the dynamic can be updated after start. + # Any disruptions to this deployment impacts everything, it is the + # entrypoint of all network traffic. + checksum/static-config: {{ include "jupyterhub.traefik.yaml" . | fromYaml | merge .Values.proxy.traefik.extraStaticConfig | toYaml | sha256sum }} + spec: + {{- with include "jupyterhub.autohttps-serviceaccount.fullname" . }} + serviceAccountName: {{ . }} + {{- end }} + {{- if .Values.scheduling.podPriority.enabled }} + priorityClassName: {{ include "jupyterhub.priority.fullname" . }} + {{- end }} + {{- with .Values.proxy.traefik.nodeSelector }} + nodeSelector: + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- with concat .Values.scheduling.corePods.tolerations .Values.proxy.traefik.tolerations }} + tolerations: + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- include "jupyterhub.coreAffinity" . | nindent 6 }} + volumes: + - name: certificates + emptyDir: {} + - name: traefik-config + configMap: + name: {{ include "jupyterhub.autohttps.fullname" . }} + {{- with .Values.proxy.traefik.extraVolumes }} + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- with include "jupyterhub.imagePullSecrets" (dict "root" . "image" .Values.proxy.traefik.image) }} + imagePullSecrets: {{ . }} + {{- end }} + initContainers: + - name: load-acme + image: "{{ .Values.proxy.secretSync.image.name }}:{{ .Values.proxy.secretSync.image.tag }}" + {{- with .Values.proxy.secretSync.image.pullPolicy }} + imagePullPolicy: {{ . }} + {{- end }} + args: + - load + - {{ include "jupyterhub.proxy-public-tls.fullname" . }} + - acme.json + - /etc/acme/acme.json + env: + # We need this to get logs immediately + - name: PYTHONUNBUFFERED + value: "True" + {{- with .Values.proxy.traefik.extraEnv }} + {{- include "jupyterhub.extraEnv" . | nindent 12 }} + {{- end }} + volumeMounts: + - name: certificates + mountPath: /etc/acme + {{- with .Values.proxy.secretSync.containerSecurityContext }} + securityContext: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- with .Values.proxy.traefik.extraInitContainers }} + {{- . | toYaml | nindent 8 }} + {{- end }} + containers: + - name: traefik + image: "{{ .Values.proxy.traefik.image.name }}:{{ .Values.proxy.traefik.image.tag }}" + {{- with .Values.proxy.traefik.image.pullPolicy }} + imagePullPolicy: {{ . }} + {{- end }} + {{- with .Values.proxy.traefik.resources }} + resources: + {{- . | toYaml | nindent 12 }} + {{- end }} + ports: + - name: http + containerPort: 8080 + - name: https + containerPort: 8443 + {{- with .Values.proxy.traefik.extraPorts }} + {{- . | toYaml | nindent 12 }} + {{- end }} + volumeMounts: + - name: traefik-config + mountPath: /etc/traefik + - name: certificates + mountPath: /etc/acme + {{- with .Values.proxy.traefik.extraVolumeMounts }} + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- with .Values.proxy.traefik.extraEnv }} + env: + {{- include "jupyterhub.extraEnv" . | nindent 12 }} + {{- end }} + {{- with .Values.proxy.traefik.containerSecurityContext }} + securityContext: + {{- . | toYaml | nindent 12 }} + {{- end }} + - name: secret-sync + image: "{{ .Values.proxy.secretSync.image.name }}:{{ .Values.proxy.secretSync.image.tag }}" + {{- with .Values.proxy.secretSync.image.pullPolicy }} + imagePullPolicy: {{ . }} + {{- end }} + {{- with .Values.proxy.secretSync.resources }} + resources: + {{- . | toYaml | nindent 12 }} + {{- end }} + args: + - watch-save + - --label=app={{ include "jupyterhub.appLabel" . }} + - --label=release={{ .Release.Name }} + - --label=chart={{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} + - --label=heritage=secret-sync + - {{ include "jupyterhub.proxy-public-tls.fullname" . }} + - acme.json + - /etc/acme/acme.json + env: + # We need this to get logs immediately + - name: PYTHONUNBUFFERED + value: "True" + volumeMounts: + - name: certificates + mountPath: /etc/acme + {{- with .Values.proxy.secretSync.containerSecurityContext }} + securityContext: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- with .Values.proxy.traefik.extraPodSpec }} + {{- . | toYaml | nindent 6 }} + {{- end }} +{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/netpol.yaml b/jupyterhub/templates/proxy/autohttps/netpol.yaml new file mode 100644 index 0000000..78270b6 --- /dev/null +++ b/jupyterhub/templates/proxy/autohttps/netpol.yaml @@ -0,0 +1,78 @@ +{{- $HTTPS := .Values.proxy.https.enabled -}} +{{- $autoHTTPS := and $HTTPS (and (eq .Values.proxy.https.type "letsencrypt") .Values.proxy.https.hosts) -}} +{{- if and $autoHTTPS .Values.proxy.traefik.networkPolicy.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ include "jupyterhub.autohttps.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + {{- include "jupyterhub.matchLabels" . | nindent 6 }} + policyTypes: + - Ingress + - Egress + + # IMPORTANT: + # NetworkPolicy's ingress "from" and egress "to" rule specifications require + # great attention to detail. A quick summary is: + # + # 1. You can provide "from"/"to" rules that provide access either ports or a + # subset of ports. + # 2. You can for each "from"/"to" rule provide any number of + # "sources"/"destinations" of four different kinds. + # - podSelector - targets pods with a certain label in the same namespace as the NetworkPolicy + # - namespaceSelector - targets all pods running in namespaces with a certain label + # - namespaceSelector and podSelector - targets pods with a certain label running in namespaces with a certain label + # - ipBlock - targets network traffic from/to a set of IP address ranges + # + # Read more at: https://kubernetes.io/docs/concepts/services-networking/network-policies/#behavior-of-to-and-from-selectors + # + ingress: + {{- with .Values.proxy.traefik.networkPolicy.allowedIngressPorts }} + # allow incoming traffic to these ports independent of source + - ports: + {{- range $port := . }} + - port: {{ $port }} + {{- end }} + {{- end }} + + # allowed pods (hub.jupyter.org/network-access-proxy-http) --> proxy (http/https port) + - ports: + - port: http + - port: https + from: + # source 1 - labeled pods + - podSelector: + matchLabels: + hub.jupyter.org/network-access-proxy-http: "true" + {{- if eq .Values.proxy.traefik.networkPolicy.interNamespaceAccessLabels "accept" }} + namespaceSelector: + matchLabels: {} # without this, the podSelector would only consider pods in the local namespace + # source 2 - pods in labeled namespaces + - namespaceSelector: + matchLabels: + hub.jupyter.org/network-access-proxy-http: "true" + {{- end }} + + {{- with .Values.proxy.traefik.networkPolicy.ingress}} + # depends, but default is nothing --> proxy + {{- . | toYaml | nindent 4 }} + {{- end }} + + egress: + # autohttps --> proxy (http port) + - to: + - podSelector: + matchLabels: + {{- $_ := merge (dict "componentLabel" "proxy") . }} + {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} + ports: + - port: 8000 + + {{- with (include "jupyterhub.networkPolicy.renderEgressRules" (list . .Values.proxy.traefik.networkPolicy)) }} + {{- . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/pdb.yaml b/jupyterhub/templates/proxy/autohttps/pdb.yaml new file mode 100644 index 0000000..074d0ac --- /dev/null +++ b/jupyterhub/templates/proxy/autohttps/pdb.yaml @@ -0,0 +1,23 @@ +{{- if .Values.proxy.traefik.pdb.enabled -}} +{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} +{{- /* k8s 1.21+ required */ -}} +apiVersion: policy/v1 +{{- else }} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: proxy + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +spec: + {{- if not (typeIs "" .Values.proxy.traefik.pdb.maxUnavailable) }} + maxUnavailable: {{ .Values.proxy.traefik.pdb.maxUnavailable }} + {{- end }} + {{- if not (typeIs "" .Values.proxy.traefik.pdb.minAvailable) }} + minAvailable: {{ .Values.proxy.traefik.pdb.minAvailable }} + {{- end }} + selector: + matchLabels: + {{- include "jupyterhub.matchLabels" . | nindent 6 }} +{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/rbac.yaml b/jupyterhub/templates/proxy/autohttps/rbac.yaml new file mode 100644 index 0000000..a0fd41a --- /dev/null +++ b/jupyterhub/templates/proxy/autohttps/rbac.yaml @@ -0,0 +1,35 @@ +{{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) -}} +{{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) -}} +{{- if $autoHTTPS -}} +{{- if .Values.rbac.create -}} +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: {{ include "jupyterhub.autohttps.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} + {{- with .Values.proxy.traefik.serviceAccount.annotations }} + annotations: + {{- . | toYaml | nindent 4 }} + {{- end }} +rules: +- apiGroups: [""] + resources: ["secrets"] + verbs: ["get", "patch", "list", "create"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: {{ include "jupyterhub.autohttps.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +subjects: +- kind: ServiceAccount + name: {{ include "jupyterhub.autohttps-serviceaccount.fullname" . }} + apiGroup: +roleRef: + kind: Role + name: {{ include "jupyterhub.autohttps.fullname" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} +{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/service.yaml b/jupyterhub/templates/proxy/autohttps/service.yaml new file mode 100644 index 0000000..615e36d --- /dev/null +++ b/jupyterhub/templates/proxy/autohttps/service.yaml @@ -0,0 +1,25 @@ +{{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) }} +{{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) }} +{{- if $autoHTTPS -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "jupyterhub.proxy-http.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} + {{- with .Values.proxy.service.labels }} + {{- . | toYaml | nindent 4 }} + {{- end }} + {{- with .Values.proxy.service.annotations }} + annotations: + {{- . | toYaml | nindent 4 }} + {{- end }} +spec: + type: ClusterIP + selector: + {{- $_ := merge (dict "componentLabel" "proxy") . }} + {{- include "jupyterhub.matchLabels" $_ | nindent 4 }} + ports: + - port: 8000 + targetPort: http +{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml b/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml new file mode 100644 index 0000000..5b340bb --- /dev/null +++ b/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) -}} +{{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) -}} +{{- if $autoHTTPS -}} +{{- if .Values.proxy.traefik.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "jupyterhub.autohttps-serviceaccount.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +{{- end }} +{{- end }} diff --git a/jupyterhub/templates/proxy/deployment.yaml b/jupyterhub/templates/proxy/deployment.yaml new file mode 100644 index 0000000..2b35382 --- /dev/null +++ b/jupyterhub/templates/proxy/deployment.yaml @@ -0,0 +1,178 @@ +{{- $manualHTTPS := and .Values.proxy.https.enabled (eq .Values.proxy.https.type "manual") -}} +{{- $manualHTTPSwithsecret := and .Values.proxy.https.enabled (eq .Values.proxy.https.type "secret") -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "jupyterhub.proxy.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +spec: + {{- if typeIs "int" .Values.proxy.chp.revisionHistoryLimit }} + revisionHistoryLimit: {{ .Values.proxy.chp.revisionHistoryLimit }} + {{- end }} + replicas: 1 + selector: + matchLabels: + {{- include "jupyterhub.matchLabels" . | nindent 6 }} + strategy: + {{- .Values.proxy.deploymentStrategy | toYaml | nindent 4 }} + template: + metadata: + labels: + {{- /* Changes here will cause the Deployment to restart the pods. */}} + {{- include "jupyterhub.matchLabels" . | nindent 8 }} + hub.jupyter.org/network-access-hub: "true" + hub.jupyter.org/network-access-singleuser: "true" + {{- with .Values.proxy.labels }} + {{- . | toYaml | nindent 8 }} + {{- end }} + annotations: + # We want to restart proxy only if the auth token changes + # Other changes to the hub config should not restart. + # We truncate to 4 chars to avoid leaking auth token info, + # since someone could brute force the hash to obtain the token + # + # Note that if auth_token has to be generated at random, it will be + # generated at random here separately from being generated at random in + # the k8s Secret template. This will cause this annotation to change to + # match the k8s Secret during the first upgrade following an auth_token + # was generated. + checksum/auth-token: {{ include "jupyterhub.hub.config.ConfigurableHTTPProxy.auth_token" . | sha256sum | trunc 4 | quote }} + checksum/proxy-secret: {{ include (print $.Template.BasePath "/proxy/secret.yaml") . | sha256sum | quote }} + {{- with .Values.proxy.annotations }} + {{- . | toYaml | nindent 8 }} + {{- end }} + spec: + terminationGracePeriodSeconds: 60 + {{- if .Values.scheduling.podPriority.enabled }} + priorityClassName: {{ include "jupyterhub.priority.fullname" . }} + {{- end }} + {{- with .Values.proxy.chp.nodeSelector }} + nodeSelector: + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- with concat .Values.scheduling.corePods.tolerations .Values.proxy.chp.tolerations }} + tolerations: + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- include "jupyterhub.coreAffinity" . | nindent 6 }} + {{- if $manualHTTPS }} + volumes: + - name: tls-secret + secret: + secretName: {{ include "jupyterhub.proxy-public-manual-tls.fullname" . }} + {{- else if $manualHTTPSwithsecret }} + volumes: + - name: tls-secret + secret: + secretName: {{ .Values.proxy.https.secret.name }} + {{- end }} + {{- with include "jupyterhub.imagePullSecrets" (dict "root" . "image" .Values.proxy.chp.image) }} + imagePullSecrets: {{ . }} + {{- end }} + containers: + - name: chp + image: {{ .Values.proxy.chp.image.name }}:{{ .Values.proxy.chp.image.tag }} + {{- $hubNameAsEnv := include "jupyterhub.hub.fullname" . | upper | replace "-" "_" }} + {{- $hubHost := printf "http://%s:$(%s_SERVICE_PORT)" (include "jupyterhub.hub.fullname" .) $hubNameAsEnv }} + command: + - configurable-http-proxy + - "--ip=" + - "--api-ip=" + - --api-port=8001 + - --default-target={{ .Values.proxy.chp.defaultTarget | default $hubHost }} + - --error-target={{ .Values.proxy.chp.errorTarget | default (printf "%s/hub/error" $hubHost) }} + {{- if $manualHTTPS }} + - --port=8443 + - --redirect-port=8000 + - --redirect-to=443 + - --ssl-key=/etc/chp/tls/tls.key + - --ssl-cert=/etc/chp/tls/tls.crt + {{- else if $manualHTTPSwithsecret }} + - --port=8443 + - --redirect-port=8000 + - --redirect-to=443 + - --ssl-key=/etc/chp/tls/{{ .Values.proxy.https.secret.key }} + - --ssl-cert=/etc/chp/tls/{{ .Values.proxy.https.secret.crt }} + {{- else }} + - --port=8000 + {{- end }} + {{- if .Values.debug.enabled }} + - --log-level=debug + {{- end }} + {{- range .Values.proxy.chp.extraCommandLineFlags }} + - {{ tpl . $ }} + {{- end }} + {{- if or $manualHTTPS $manualHTTPSwithsecret }} + volumeMounts: + - name: tls-secret + mountPath: /etc/chp/tls + readOnly: true + {{- end }} + {{- with .Values.proxy.chp.resources }} + resources: + {{- . | toYaml | nindent 12 }} + {{- end }} + env: + - name: CONFIGPROXY_AUTH_TOKEN + valueFrom: + secretKeyRef: + # NOTE: References the chart managed k8s Secret even if + # hub.existingSecret is specified to avoid using the + # lookup function on the user managed k8s Secret. + name: {{ include "jupyterhub.hub.fullname" . }} + key: hub.config.ConfigurableHTTPProxy.auth_token + {{- with .Values.proxy.chp.extraEnv }} + {{- include "jupyterhub.extraEnv" . | nindent 12 }} + {{- end }} + {{- with .Values.proxy.chp.image.pullPolicy }} + imagePullPolicy: {{ . }} + {{- end }} + ports: + {{- if or $manualHTTPS $manualHTTPSwithsecret }} + - name: https + containerPort: 8443 + {{- end }} + - name: http + containerPort: 8000 + - name: api + containerPort: 8001 + {{- if .Values.proxy.chp.livenessProbe.enabled }} + livenessProbe: + initialDelaySeconds: {{ .Values.proxy.chp.livenessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.proxy.chp.livenessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.proxy.chp.livenessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.proxy.chp.livenessProbe.failureThreshold }} + httpGet: + path: /_chp_healthz + {{- if or $manualHTTPS $manualHTTPSwithsecret }} + port: https + scheme: HTTPS + {{- else }} + port: http + scheme: HTTP + {{- end }} + {{- end }} + {{- if .Values.proxy.chp.readinessProbe.enabled }} + readinessProbe: + initialDelaySeconds: {{ .Values.proxy.chp.readinessProbe.initialDelaySeconds }} + periodSeconds: {{ .Values.proxy.chp.readinessProbe.periodSeconds }} + timeoutSeconds: {{ .Values.proxy.chp.readinessProbe.timeoutSeconds }} + failureThreshold: {{ .Values.proxy.chp.readinessProbe.failureThreshold }} + httpGet: + path: /_chp_healthz + {{- if or $manualHTTPS $manualHTTPSwithsecret }} + port: https + scheme: HTTPS + {{- else }} + port: http + scheme: HTTP + {{- end }} + {{- end }} + {{- with .Values.proxy.chp.containerSecurityContext }} + securityContext: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- with .Values.proxy.chp.extraPodSpec }} + {{- . | toYaml | nindent 6 }} + {{- end }} diff --git a/jupyterhub/templates/proxy/netpol.yaml b/jupyterhub/templates/proxy/netpol.yaml new file mode 100644 index 0000000..0af853a --- /dev/null +++ b/jupyterhub/templates/proxy/netpol.yaml @@ -0,0 +1,108 @@ +{{- $HTTPS := .Values.proxy.https.enabled -}} +{{- $autoHTTPS := and $HTTPS (and (eq .Values.proxy.https.type "letsencrypt") .Values.proxy.https.hosts) -}} +{{- $manualHTTPS := and $HTTPS (eq .Values.proxy.https.type "manual") -}} +{{- $manualHTTPSwithsecret := and $HTTPS (eq .Values.proxy.https.type "secret") -}} +{{- if .Values.proxy.chp.networkPolicy.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ include "jupyterhub.proxy.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + {{- include "jupyterhub.matchLabels" . | nindent 6 }} + policyTypes: + - Ingress + - Egress + + # IMPORTANT: + # NetworkPolicy's ingress "from" and egress "to" rule specifications require + # great attention to detail. A quick summary is: + # + # 1. You can provide "from"/"to" rules that provide access either ports or a + # subset of ports. + # 2. You can for each "from"/"to" rule provide any number of + # "sources"/"destinations" of four different kinds. + # - podSelector - targets pods with a certain label in the same namespace as the NetworkPolicy + # - namespaceSelector - targets all pods running in namespaces with a certain label + # - namespaceSelector and podSelector - targets pods with a certain label running in namespaces with a certain label + # - ipBlock - targets network traffic from/to a set of IP address ranges + # + # Read more at: https://kubernetes.io/docs/concepts/services-networking/network-policies/#behavior-of-to-and-from-selectors + # + ingress: + {{- with .Values.proxy.chp.networkPolicy.allowedIngressPorts }} + # allow incoming traffic to these ports independent of source + - ports: + {{- range $port := . }} + - port: {{ $port }} + {{- end }} + {{- end }} + + # allowed pods (hub.jupyter.org/network-access-proxy-http) --> proxy (http/https port) + - ports: + - port: http + {{- if or $manualHTTPS $manualHTTPSwithsecret }} + - port: https + {{- end }} + from: + # source 1 - labeled pods + - podSelector: + matchLabels: + hub.jupyter.org/network-access-proxy-http: "true" + {{- if eq .Values.proxy.chp.networkPolicy.interNamespaceAccessLabels "accept" }} + namespaceSelector: + matchLabels: {} # without this, the podSelector would only consider pods in the local namespace + # source 2 - pods in labeled namespaces + - namespaceSelector: + matchLabels: + hub.jupyter.org/network-access-proxy-http: "true" + {{- end }} + + # allowed pods (hub.jupyter.org/network-access-proxy-api) --> proxy (api port) + - ports: + - port: api + from: + # source 1 - labeled pods + - podSelector: + matchLabels: + hub.jupyter.org/network-access-proxy-api: "true" + {{- if eq .Values.proxy.chp.networkPolicy.interNamespaceAccessLabels "accept" }} + namespaceSelector: + matchLabels: {} # without this, the podSelector would only consider pods in the local namespace + # source 2 - pods in labeled namespaces + - namespaceSelector: + matchLabels: + hub.jupyter.org/network-access-proxy-api: "true" + {{- end }} + + {{- with .Values.proxy.chp.networkPolicy.ingress}} + # depends, but default is nothing --> proxy + {{- . | toYaml | nindent 4 }} + {{- end }} + + egress: + # proxy --> hub + - to: + - podSelector: + matchLabels: + {{- $_ := merge (dict "componentLabel" "hub") . }} + {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} + ports: + - port: 8081 + + # proxy --> singleuser-server + - to: + - podSelector: + matchLabels: + {{- $_ := merge (dict "componentLabel" "singleuser-server") . }} + {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} + ports: + - port: 8888 + + {{- with (include "jupyterhub.networkPolicy.renderEgressRules" (list . .Values.proxy.chp.networkPolicy)) }} + {{- . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/jupyterhub/templates/proxy/pdb.yaml b/jupyterhub/templates/proxy/pdb.yaml new file mode 100644 index 0000000..d8651f5 --- /dev/null +++ b/jupyterhub/templates/proxy/pdb.yaml @@ -0,0 +1,23 @@ +{{- if .Values.proxy.chp.pdb.enabled -}} +{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} +{{- /* k8s 1.21+ required */ -}} +apiVersion: policy/v1 +{{- else }} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ include "jupyterhub.proxy.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +spec: + {{- if not (typeIs "" .Values.proxy.chp.pdb.maxUnavailable) }} + maxUnavailable: {{ .Values.proxy.chp.pdb.maxUnavailable }} + {{- end }} + {{- if not (typeIs "" .Values.proxy.chp.pdb.minAvailable) }} + minAvailable: {{ .Values.proxy.chp.pdb.minAvailable }} + {{- end }} + selector: + matchLabels: + {{- include "jupyterhub.matchLabels" . | nindent 6 }} +{{- end }} diff --git a/jupyterhub/templates/proxy/secret.yaml b/jupyterhub/templates/proxy/secret.yaml new file mode 100644 index 0000000..d9ff8ad --- /dev/null +++ b/jupyterhub/templates/proxy/secret.yaml @@ -0,0 +1,13 @@ +{{- $manualHTTPS := and .Values.proxy.https.enabled (eq .Values.proxy.https.type "manual") -}} +{{- if $manualHTTPS -}} +apiVersion: v1 +kind: Secret +metadata: + name: {{ include "jupyterhub.proxy-public-manual-tls.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +type: kubernetes.io/tls +data: + tls.crt: {{ .Values.proxy.https.manual.cert | required "Required configuration missing: proxy.https.manual.cert" | b64enc }} + tls.key: {{ .Values.proxy.https.manual.key | required "Required configuration missing: proxy.https.manual.key" | b64enc }} +{{- end }} diff --git a/jupyterhub/templates/proxy/service.yaml b/jupyterhub/templates/proxy/service.yaml new file mode 100644 index 0000000..8a96eb1 --- /dev/null +++ b/jupyterhub/templates/proxy/service.yaml @@ -0,0 +1,80 @@ +{{- $enabled := .Values.proxy.https.enabled -}} +{{- $autoHTTPS := and $enabled (and (eq .Values.proxy.https.type "letsencrypt") .Values.proxy.https.hosts) -}} +{{- $manualHTTPS := and $enabled (eq .Values.proxy.https.type "manual") -}} +{{- $manualHTTPSwithsecret := and $enabled (eq .Values.proxy.https.type "secret") -}} +{{- $offloadHTTPS := and $enabled (eq .Values.proxy.https.type "offload") -}} +{{- $valid := or $autoHTTPS (or $manualHTTPS (or $manualHTTPSwithsecret $offloadHTTPS)) -}} +{{- $HTTPS := and $enabled $valid -}} +apiVersion: v1 +kind: Service +metadata: + name: {{ include "jupyterhub.proxy-api.fullname" . }} + labels: + {{- $_ := merge (dict "componentSuffix" "-api") . }} + {{- include "jupyterhub.labels" $_ | nindent 4 }} +spec: + selector: + {{- include "jupyterhub.matchLabels" . | nindent 4 }} + ports: + - port: 8001 + targetPort: api +--- +apiVersion: v1 +kind: Service +metadata: + name: {{ include "jupyterhub.proxy-public.fullname" . }} + labels: + {{- $_ := merge (dict "componentSuffix" "-public") . }} + {{- include "jupyterhub.labels" $_ | nindent 4 }} + {{- with .Values.proxy.service.labels }} + {{- . | toYaml | nindent 4 }} + {{- end }} + {{- with .Values.proxy.service.annotations }} + annotations: + {{- . | toYaml | nindent 4 }} + {{- end }} +spec: + selector: + {{- if $autoHTTPS }} + component: autohttps + {{- else }} + component: proxy + {{- end }} + release: {{ .Release.Name }} + ports: + {{- if $HTTPS }} + - name: https + port: 443 + # When HTTPS termination is handled outside our helm chart, pass traffic + # coming in via this Service's port 443 to targeted pod's port meant for + # HTTP traffic. + {{- if $offloadHTTPS }} + targetPort: http + {{- else }} + targetPort: https + {{- end }} + {{- with .Values.proxy.service.nodePorts.https }} + nodePort: {{ . }} + {{- end }} + {{- end }} + {{- if ne .Values.proxy.service.disableHttpPort true }} + - name: http + port: 80 + targetPort: http + {{- with .Values.proxy.service.nodePorts.http }} + nodePort: {{ . }} + {{- end }} + {{- end }} + {{- with .Values.proxy.service.extraPorts }} + {{- . | toYaml | nindent 4 }} + {{- end }} + type: {{ .Values.proxy.service.type }} + {{- with .Values.proxy.service.loadBalancerIP }} + loadBalancerIP: {{ . }} + {{- end }} + {{- if eq .Values.proxy.service.type "LoadBalancer" }} + {{- with .Values.proxy.service.loadBalancerSourceRanges }} + loadBalancerSourceRanges: + {{- . | toYaml | nindent 4 }} + {{- end }} + {{- end }} diff --git a/jupyterhub/templates/scheduling/_scheduling-helpers.tpl b/jupyterhub/templates/scheduling/_scheduling-helpers.tpl new file mode 100644 index 0000000..0a1a741 --- /dev/null +++ b/jupyterhub/templates/scheduling/_scheduling-helpers.tpl @@ -0,0 +1,138 @@ +{{- define "jupyterhub.userNodeAffinityRequired" -}} +{{- if eq .Values.scheduling.userPods.nodeAffinity.matchNodePurpose "require" -}} +- matchExpressions: + - key: hub.jupyter.org/node-purpose + operator: In + values: [user] +{{- end }} +{{- with .Values.singleuser.extraNodeAffinity.required }} +{{- . | toYaml | nindent 0 }} +{{- end }} +{{- end }} + +{{- define "jupyterhub.userNodeAffinityPreferred" -}} +{{- if eq .Values.scheduling.userPods.nodeAffinity.matchNodePurpose "prefer" -}} +- weight: 100 + preference: + matchExpressions: + - key: hub.jupyter.org/node-purpose + operator: In + values: [user] +{{- end }} +{{- with .Values.singleuser.extraNodeAffinity.preferred }} +{{- . | toYaml | nindent 0 }} +{{- end }} +{{- end }} + +{{- define "jupyterhub.userPodAffinityRequired" -}} +{{- with .Values.singleuser.extraPodAffinity.required -}} +{{ . | toYaml }} +{{- end }} +{{- end }} + +{{- define "jupyterhub.userPodAffinityPreferred" -}} +{{- with .Values.singleuser.extraPodAffinity.preferred -}} +{{ . | toYaml }} +{{- end }} +{{- end }} + +{{- define "jupyterhub.userPodAntiAffinityRequired" -}} +{{- with .Values.singleuser.extraPodAntiAffinity.required -}} +{{ . | toYaml }} +{{- end }} +{{- end }} + +{{- define "jupyterhub.userPodAntiAffinityPreferred" -}} +{{- with .Values.singleuser.extraPodAntiAffinity.preferred -}} +{{ . | toYaml }} +{{- end }} +{{- end }} + + + +{{- /* + jupyterhub.userAffinity: + It is used by user-placeholder to set the same affinity on them as the + spawned user pods spawned by kubespawner. +*/}} +{{- define "jupyterhub.userAffinity" -}} + +{{- $dummy := set . "nodeAffinityRequired" (include "jupyterhub.userNodeAffinityRequired" .) -}} +{{- $dummy := set . "podAffinityRequired" (include "jupyterhub.userPodAffinityRequired" .) -}} +{{- $dummy := set . "podAntiAffinityRequired" (include "jupyterhub.userPodAntiAffinityRequired" .) -}} +{{- $dummy := set . "nodeAffinityPreferred" (include "jupyterhub.userNodeAffinityPreferred" .) -}} +{{- $dummy := set . "podAffinityPreferred" (include "jupyterhub.userPodAffinityPreferred" .) -}} +{{- $dummy := set . "podAntiAffinityPreferred" (include "jupyterhub.userPodAntiAffinityPreferred" .) -}} +{{- $dummy := set . "hasNodeAffinity" (or .nodeAffinityRequired .nodeAffinityPreferred) -}} +{{- $dummy := set . "hasPodAffinity" (or .podAffinityRequired .podAffinityPreferred) -}} +{{- $dummy := set . "hasPodAntiAffinity" (or .podAntiAffinityRequired .podAntiAffinityPreferred) -}} + +{{- if .hasNodeAffinity -}} +nodeAffinity: + {{- if .nodeAffinityRequired }} + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + {{- .nodeAffinityRequired | nindent 6 }} + {{- end }} + + {{- if .nodeAffinityPreferred }} + preferredDuringSchedulingIgnoredDuringExecution: + {{- .nodeAffinityPreferred | nindent 4 }} + {{- end }} +{{- end }} + +{{- if .hasPodAffinity }} +podAffinity: + {{- if .podAffinityRequired }} + requiredDuringSchedulingIgnoredDuringExecution: + {{- .podAffinityRequired | nindent 4 }} + {{- end }} + + {{- if .podAffinityPreferred }} + preferredDuringSchedulingIgnoredDuringExecution: + {{- .podAffinityPreferred | nindent 4 }} + {{- end }} +{{- end }} + +{{- if .hasPodAntiAffinity }} +podAntiAffinity: + {{- if .podAntiAffinityRequired }} + requiredDuringSchedulingIgnoredDuringExecution: + {{- .podAntiAffinityRequired | nindent 4 }} + {{- end }} + + {{- if .podAntiAffinityPreferred }} + preferredDuringSchedulingIgnoredDuringExecution: + {{- .podAntiAffinityPreferred | nindent 4 }} + {{- end }} +{{- end }} + +{{- end }} + + + +{{- define "jupyterhub.coreAffinity" -}} +{{- $require := eq .Values.scheduling.corePods.nodeAffinity.matchNodePurpose "require" -}} +{{- $prefer := eq .Values.scheduling.corePods.nodeAffinity.matchNodePurpose "prefer" -}} +{{- if or $require $prefer -}} +affinity: + nodeAffinity: + {{- if $require }} + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: hub.jupyter.org/node-purpose + operator: In + values: [core] + {{- end }} + {{- if $prefer }} + preferredDuringSchedulingIgnoredDuringExecution: + - weight: 100 + preference: + matchExpressions: + - key: hub.jupyter.org/node-purpose + operator: In + values: [core] + {{- end }} +{{- end }} +{{- end }} diff --git a/jupyterhub/templates/scheduling/priorityclass.yaml b/jupyterhub/templates/scheduling/priorityclass.yaml new file mode 100644 index 0000000..77c84cb --- /dev/null +++ b/jupyterhub/templates/scheduling/priorityclass.yaml @@ -0,0 +1,15 @@ +{{- if .Values.scheduling.podPriority.enabled }} +apiVersion: scheduling.k8s.io/v1 +kind: PriorityClass +metadata: + name: {{ include "jupyterhub.priority.fullname" . }} + annotations: + meta.helm.sh/release-name: "{{ .Release.Name }}" + meta.helm.sh/release-namespace: "{{ .Release.Namespace }}" + labels: + {{- $_ := merge (dict "componentLabel" "default-priority") . }} + {{- include "jupyterhub.labels" $_ | nindent 4 }} +value: {{ .Values.scheduling.podPriority.defaultPriority }} +globalDefault: {{ .Values.scheduling.podPriority.globalDefault }} +description: "A default priority higher than user placeholders priority." +{{- end }} diff --git a/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml b/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml new file mode 100644 index 0000000..ec84fb5 --- /dev/null +++ b/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml @@ -0,0 +1,22 @@ +{{- /* +The cluster autoscaler should be allowed to evict and reschedule these pods if +it would help in order to scale down a node. +*/}} +{{- if .Values.scheduling.userPlaceholder.enabled -}} +{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} +{{- /* k8s 1.21+ required */ -}} +apiVersion: policy/v1 +{{- else }} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ include "jupyterhub.user-placeholder.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +spec: + minAvailable: 0 + selector: + matchLabels: + {{- include "jupyterhub.matchLabels" . | nindent 6 }} +{{- end }} diff --git a/jupyterhub/templates/scheduling/user-placeholder/priorityclass.yaml b/jupyterhub/templates/scheduling/user-placeholder/priorityclass.yaml new file mode 100644 index 0000000..bdedbdd --- /dev/null +++ b/jupyterhub/templates/scheduling/user-placeholder/priorityclass.yaml @@ -0,0 +1,16 @@ +{{- if .Values.scheduling.podPriority.enabled }} +{{- if .Values.scheduling.userPlaceholder.enabled -}} +apiVersion: scheduling.k8s.io/v1 +kind: PriorityClass +metadata: + name: {{ include "jupyterhub.user-placeholder-priority.fullname" . }} + annotations: + meta.helm.sh/release-name: "{{ .Release.Name }}" + meta.helm.sh/release-namespace: "{{ .Release.Namespace }}" + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +value: {{ .Values.scheduling.podPriority.userPlaceholderPriority }} +globalDefault: false +description: "With a priority higher or eqaul to a cluster autoscalers priority cutoff, a pod can trigger a cluster scale up. At the same time, placeholder pods priority should be lower than other pods to make them evictable." +{{- end }} +{{- end }} diff --git a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml new file mode 100644 index 0000000..e0f6f59 --- /dev/null +++ b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml @@ -0,0 +1,80 @@ + +{{- /* +These user-placeholder pods can be used to test cluster autoscaling in a +controlled fashion. + +Example: +$ echo 'Simulating four users...' +$ kubectl scale sts/user-placeholder --replicas 4 +*/}} +{{- if .Values.scheduling.userPlaceholder.enabled -}} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "jupyterhub.user-placeholder.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +spec: + podManagementPolicy: Parallel + {{- if typeIs "int" .Values.scheduling.userPlaceholder.revisionHistoryLimit }} + revisionHistoryLimit: {{ .Values.scheduling.userPlaceholder.revisionHistoryLimit }} + {{- end }} + replicas: {{ .Values.scheduling.userPlaceholder.replicas }} + selector: + matchLabels: + {{- include "jupyterhub.matchLabels" . | nindent 6 }} + serviceName: {{ include "jupyterhub.user-placeholder.fullname" . }} + template: + metadata: + {{- with .Values.scheduling.userPlaceholder.annotations }} + annotations: + {{- . | toYaml | nindent 8 }} + {{- end }} + labels: + {{- /* Changes here will cause the Deployment to restart the pods. */}} + {{- include "jupyterhub.matchLabels" . | nindent 8 }} + {{- with .Values.scheduling.userPlaceholder.labels }} + {{- . | toYaml | nindent 8 }} + {{- end }} + spec: + {{- if .Values.scheduling.podPriority.enabled }} + priorityClassName: {{ include "jupyterhub.user-placeholder-priority.fullname" . }} + {{- end }} + {{- if .Values.scheduling.userScheduler.enabled }} + schedulerName: {{ include "jupyterhub.user-scheduler.fullname" . }} + {{- end }} + {{- with .Values.singleuser.nodeSelector }} + nodeSelector: + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- with concat .Values.scheduling.userPods.tolerations .Values.singleuser.extraTolerations }} + tolerations: + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- if include "jupyterhub.userAffinity" . }} + affinity: + {{- include "jupyterhub.userAffinity" . | nindent 8 }} + {{- end }} + terminationGracePeriodSeconds: 0 + automountServiceAccountToken: false + {{- with include "jupyterhub.imagePullSecrets" (dict "root" . "image" .Values.scheduling.userPlaceholder.image) }} + imagePullSecrets: {{ . }} + {{- end }} + containers: + - name: pause + image: {{ .Values.scheduling.userPlaceholder.image.name }}:{{ .Values.scheduling.userPlaceholder.image.tag }} + {{- if .Values.scheduling.userPlaceholder.resources }} + resources: + {{- .Values.scheduling.userPlaceholder.resources | toYaml | nindent 12 }} + {{- else if (include "jupyterhub.singleuser.resources" .) }} + resources: + {{- include "jupyterhub.singleuser.resources" . | nindent 12 }} + {{- end }} + {{- with .Values.scheduling.userPlaceholder.image.pullPolicy }} + imagePullPolicy: {{ . }} + {{- end }} + {{- with .Values.scheduling.userPlaceholder.containerSecurityContext }} + securityContext: + {{- . | toYaml | nindent 12 }} + {{- end }} +{{- end }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml b/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml new file mode 100644 index 0000000..22ea9a8 --- /dev/null +++ b/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml @@ -0,0 +1,95 @@ +{{- if .Values.scheduling.userScheduler.enabled -}} +kind: ConfigMap +apiVersion: v1 +metadata: + name: {{ include "jupyterhub.user-scheduler-deploy.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +data: + {{- /* + This is configuration of a k8s official kube-scheduler binary running in the + user-scheduler pod. + + ref: https://kubernetes.io/docs/reference/scheduling/config/ + ref: https://kubernetes.io/docs/reference/config-api/kube-scheduler-config.v1beta2/ + ref: https://kubernetes.io/docs/reference/config-api/kube-scheduler-config.v1beta3/ + + v1beta1 can be used with kube-scheduler binary version <=1.21 + v1beta2 requires kube-scheduler binary version >=1.22 + v1beta3 requires kube-scheduler binary version >=1.23 + + kube-scheduler binaries versioned >=1.21 will error in k8s clusters + versioned <=1.20. To support a modern version of kube-scheduler and k8s + versions <=1.20 upwards, we provide two scenarios: + + 1. For k8s >= 1.21 we use a modern version of kube-scheduler and Helm chart + configuration works as expected. + 2. For k8s <= 1.20 we use a hardcoded version of kube-scheduler (v1.20.15) + and configuration (v1beta1) of kube-scheduler. + */}} + config.yaml: | + {{- /* + FIXME: We have added a workaround for EKS where + .Capabilities.KubeVersion.Minor can return a + string like "22+" instead of just "22". + + See https://github.com/aws/eks-distro/issues/1128. + */}} + {{- if ge (atoi (.Capabilities.KubeVersion.Minor | replace "+" "")) 21 }} + apiVersion: kubescheduler.config.k8s.io/v1beta3 + kind: KubeSchedulerConfiguration + leaderElection: + resourceLock: endpoints + resourceName: {{ include "jupyterhub.user-scheduler-lock.fullname" . }} + resourceNamespace: "{{ .Release.Namespace }}" + profiles: + - schedulerName: {{ include "jupyterhub.user-scheduler.fullname" . }} + {{- with .Values.scheduling.userScheduler.plugins }} + plugins: + {{- . | toYaml | nindent 10 }} + {{- end }} + {{- with .Values.scheduling.userScheduler.pluginConfig }} + pluginConfig: + {{- . | toYaml | nindent 10 }} + {{- end }} + {{- else }} + # WARNING: The tag of this image is hardcoded, and the + # "scheduling.userScheduler.plugins" configuration of the Helm + # chart that generated this resource manifest wasn't respected. If + # you install the Helm chart in a k8s cluster versioned 1.21 or + # higher, your configuration will be respected. + apiVersion: kubescheduler.config.k8s.io/v1beta1 + kind: KubeSchedulerConfiguration + leaderElection: + resourceLock: endpoints + resourceName: {{ include "jupyterhub.user-scheduler-lock.fullname" . }} + resourceNamespace: "{{ .Release.Namespace }}" + profiles: + - schedulerName: {{ include "jupyterhub.user-scheduler.fullname" . }} + plugins: + score: + disabled: + - name: SelectorSpread + - name: TaintToleration + - name: PodTopologySpread + - name: NodeResourcesBalancedAllocation + - name: NodeResourcesLeastAllocated + # Disable plugins to be allowed to enable them again with a + # different weight and avoid an error. + - name: NodePreferAvoidPods + - name: NodeAffinity + - name: InterPodAffinity + - name: ImageLocality + enabled: + - name: NodePreferAvoidPods + weight: 161051 + - name: NodeAffinity + weight: 14631 + - name: InterPodAffinity + weight: 1331 + - name: NodeResourcesMostAllocated + weight: 121 + - name: ImageLocality + weight: 11 + {{- end }} +{{- end }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml new file mode 100644 index 0000000..58bb23a --- /dev/null +++ b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml @@ -0,0 +1,109 @@ +{{- if .Values.scheduling.userScheduler.enabled -}} +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "jupyterhub.user-scheduler-deploy.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +spec: + {{- if typeIs "int" .Values.scheduling.userScheduler.revisionHistoryLimit }} + revisionHistoryLimit: {{ .Values.scheduling.userScheduler.revisionHistoryLimit }} + {{- end }} + replicas: {{ .Values.scheduling.userScheduler.replicas }} + selector: + matchLabels: + {{- include "jupyterhub.matchLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "jupyterhub.matchLabels" . | nindent 8 }} + {{- with .Values.scheduling.userScheduler.labels }} + {{- . | toYaml | nindent 8 }} + {{- end }} + annotations: + checksum/config-map: {{ include (print $.Template.BasePath "/scheduling/user-scheduler/configmap.yaml") . | sha256sum }} + {{- with .Values.scheduling.userScheduler.annotations }} + {{- . | toYaml | nindent 8 }} + {{- end }} + spec: + {{ with include "jupyterhub.user-scheduler-serviceaccount.fullname" . }} + serviceAccountName: {{ . }} + {{- end }} + {{- if .Values.scheduling.podPriority.enabled }} + priorityClassName: {{ include "jupyterhub.priority.fullname" . }} + {{- end }} + {{- with .Values.scheduling.userScheduler.nodeSelector }} + nodeSelector: + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- with concat .Values.scheduling.corePods.tolerations .Values.scheduling.userScheduler.tolerations }} + tolerations: + {{- . | toYaml | nindent 8 }} + {{- end }} + {{- include "jupyterhub.coreAffinity" . | nindent 6 }} + volumes: + - name: config + configMap: + name: {{ include "jupyterhub.user-scheduler-deploy.fullname" . }} + {{- with include "jupyterhub.imagePullSecrets" (dict "root" . "image" .Values.scheduling.userScheduler.image) }} + imagePullSecrets: {{ . }} + {{- end }} + containers: + - name: kube-scheduler + {{- /* + FIXME: We have added a workaround for EKS where + .Capabilities.KubeVersion.Minor can return a + string like "22+" instead of just "22". + + See https://github.com/aws/eks-distro/issues/1128. + */}} + {{- if ge (atoi (.Capabilities.KubeVersion.Minor | replace "+" "")) 21 }} + image: {{ .Values.scheduling.userScheduler.image.name }}:{{ .Values.scheduling.userScheduler.image.tag }} + {{- else }} + # WARNING: The tag of this image is hardcoded, and the + # "scheduling.userScheduler.image.tag" configuration of the + # Helm chart that generated this resource manifest isn't + # respected. If you install the Helm chart in a k8s cluster + # versioned 1.21 or higher, your configuration will be + # respected. + image: {{ .Values.scheduling.userScheduler.image.name }}:v1.20.15 + {{- end }} + {{- with .Values.scheduling.userScheduler.image.pullPolicy }} + imagePullPolicy: {{ . }} + {{- end }} + command: + - /usr/local/bin/kube-scheduler + # NOTE: --authentication-skip-lookup=true is used to avoid a + # seemingly harmless error, if we need to not skip + # "authentication lookup" in the future, see the linked issue. + # + # ref: https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues/1894 + - --config=/etc/user-scheduler/config.yaml + - --authentication-skip-lookup=true + - --v={{ .Values.scheduling.userScheduler.logLevel }} + volumeMounts: + - mountPath: /etc/user-scheduler + name: config + livenessProbe: + httpGet: + path: /healthz + scheme: HTTPS + port: 10259 + initialDelaySeconds: 15 + readinessProbe: + httpGet: + path: /healthz + scheme: HTTPS + port: 10259 + {{- with .Values.scheduling.userScheduler.resources }} + resources: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- with .Values.scheduling.userScheduler.containerSecurityContext }} + securityContext: + {{- . | toYaml | nindent 12 }} + {{- end }} + {{- with .Values.scheduling.userScheduler.extraPodSpec }} + {{- . | toYaml | nindent 6 }} + {{- end }} +{{- end }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml b/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml new file mode 100644 index 0000000..3a9544e --- /dev/null +++ b/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml @@ -0,0 +1,23 @@ +{{- if and .Values.scheduling.userScheduler.enabled .Values.scheduling.userScheduler.pdb.enabled -}} +{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} +{{- /* k8s 1.21+ required */ -}} +apiVersion: policy/v1 +{{- else }} +apiVersion: policy/v1beta1 +{{- end }} +kind: PodDisruptionBudget +metadata: + name: {{ include "jupyterhub.user-scheduler-deploy.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +spec: + {{- if not (typeIs "" .Values.scheduling.userScheduler.pdb.maxUnavailable) }} + maxUnavailable: {{ .Values.scheduling.userScheduler.pdb.maxUnavailable }} + {{- end }} + {{- if not (typeIs "" .Values.scheduling.userScheduler.pdb.minAvailable) }} + minAvailable: {{ .Values.scheduling.userScheduler.pdb.minAvailable }} + {{- end }} + selector: + matchLabels: + {{- include "jupyterhub.matchLabels" . | nindent 6 }} +{{- end }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml new file mode 100644 index 0000000..f77640b --- /dev/null +++ b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml @@ -0,0 +1,233 @@ +{{- if .Values.scheduling.userScheduler.enabled -}} +{{- if .Values.rbac.create -}} +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "jupyterhub.user-scheduler.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +rules: + # Copied from the system:kube-scheduler ClusterRole of the k8s version + # matching the kube-scheduler binary we use. A modification has been made to + # resourceName fields to remain relevant for how we have named our resources + # in this Helm chart. + # + # NOTE: These rules have been: + # - unchanged between 1.12 and 1.15 + # - changed in 1.16 + # - changed in 1.17 + # - unchanged between 1.18 and 1.20 + # - changed in 1.21: get/list/watch permission for namespace, + # csidrivers, csistoragecapacities was added. + # - unchanged between 1.22 and 1.23 + # + # ref: https://github.com/kubernetes/kubernetes/blob/v1.23.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L705-L861 + - apiGroups: + - "" + - events.k8s.io + resources: + - events + verbs: + - create + - patch + - update + - apiGroups: + - coordination.k8s.io + resources: + - leases + verbs: + - create + - apiGroups: + - coordination.k8s.io + resourceNames: + - {{ include "jupyterhub.user-scheduler-lock.fullname" . }} + resources: + - leases + verbs: + - get + - update + - apiGroups: + - "" + resources: + - endpoints + verbs: + - create + - apiGroups: + - "" + resourceNames: + - {{ include "jupyterhub.user-scheduler-lock.fullname" . }} + resources: + - endpoints + verbs: + - get + - update + - apiGroups: + - "" + resources: + - nodes + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - pods + verbs: + - delete + - get + - list + - watch + - apiGroups: + - "" + resources: + - bindings + - pods/binding + verbs: + - create + - apiGroups: + - "" + resources: + - pods/status + verbs: + - patch + - update + - apiGroups: + - "" + resources: + - replicationcontrollers + - services + verbs: + - get + - list + - watch + - apiGroups: + - apps + - extensions + resources: + - replicasets + verbs: + - get + - list + - watch + - apiGroups: + - apps + resources: + - statefulsets + verbs: + - get + - list + - watch + - apiGroups: + - policy + resources: + - poddisruptionbudgets + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - persistentvolumeclaims + - persistentvolumes + verbs: + - get + - list + - watch + - apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create + - apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create + - apiGroups: + - storage.k8s.io + resources: + - csinodes + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - namespaces + verbs: + - get + - list + - watch + - apiGroups: + - storage.k8s.io + resources: + - csidrivers + verbs: + - get + - list + - watch + - apiGroups: + - storage.k8s.io + resources: + - csistoragecapacities + verbs: + - get + - list + - watch + + # Copied from the system:volume-scheduler ClusterRole of the k8s version + # matching the kube-scheduler binary we use. + # + # NOTE: These rules have not changed between 1.12 and 1.23. + # + # ref: https://github.com/kubernetes/kubernetes/blob/v1.23.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L1280-L1307 + - apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list + - patch + - update + - watch + - apiGroups: + - storage.k8s.io + resources: + - storageclasses + verbs: + - get + - list + - watch + - apiGroups: + - "" + resources: + - persistentvolumeclaims + verbs: + - get + - list + - patch + - update + - watch +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: {{ include "jupyterhub.user-scheduler.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +subjects: + - kind: ServiceAccount + name: {{ include "jupyterhub.user-scheduler-serviceaccount.fullname" . }} + namespace: "{{ .Release.Namespace }}" +roleRef: + kind: ClusterRole + name: {{ include "jupyterhub.user-scheduler.fullname" . }} + apiGroup: rbac.authorization.k8s.io +{{- end }} +{{- end }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml b/jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml new file mode 100644 index 0000000..f84ffc1 --- /dev/null +++ b/jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml @@ -0,0 +1,14 @@ +{{- if .Values.scheduling.userScheduler.enabled -}} +{{- if .Values.scheduling.userScheduler.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "jupyterhub.user-scheduler-serviceaccount.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} + {{- with .Values.scheduling.userScheduler.serviceAccount.annotations }} + annotations: + {{- . | toYaml | nindent 4 }} + {{- end }} +{{- end }} +{{- end }} diff --git a/jupyterhub/templates/singleuser/netpol.yaml b/jupyterhub/templates/singleuser/netpol.yaml new file mode 100644 index 0000000..f388b81 --- /dev/null +++ b/jupyterhub/templates/singleuser/netpol.yaml @@ -0,0 +1,99 @@ +{{- if and .Values.singleuser.networkPolicy.enabled -}} +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + name: {{ include "jupyterhub.singleuser.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +spec: + podSelector: + matchLabels: + {{- $_ := merge (dict "componentLabel" "singleuser-server") . }} + {{- include "jupyterhub.matchLabels" $_ | nindent 6 }} + policyTypes: + - Ingress + - Egress + + # IMPORTANT: + # NetworkPolicy's ingress "from" and egress "to" rule specifications require + # great attention to detail. A quick summary is: + # + # 1. You can provide "from"/"to" rules that provide access either ports or a + # subset of ports. + # 2. You can for each "from"/"to" rule provide any number of + # "sources"/"destinations" of four different kinds. + # - podSelector - targets pods with a certain label in the same namespace as the NetworkPolicy + # - namespaceSelector - targets all pods running in namespaces with a certain label + # - namespaceSelector and podSelector - targets pods with a certain label running in namespaces with a certain label + # - ipBlock - targets network traffic from/to a set of IP address ranges + # + # Read more at: https://kubernetes.io/docs/concepts/services-networking/network-policies/#behavior-of-to-and-from-selectors + # + ingress: + {{- with .Values.singleuser.networkPolicy.allowedIngressPorts }} + # allow incoming traffic to these ports independent of source + - ports: + {{- range $port := . }} + - port: {{ $port }} + {{- end }} + {{- end }} + + # allowed pods (hub.jupyter.org/network-access-singleuser) --> singleuser-server + - ports: + - port: notebook-port + from: + # source 1 - labeled pods + - podSelector: + matchLabels: + hub.jupyter.org/network-access-singleuser: "true" + {{- if eq .Values.singleuser.networkPolicy.interNamespaceAccessLabels "accept" }} + namespaceSelector: + matchLabels: {} # without this, the podSelector would only consider pods in the local namespace + # source 2 - pods in labeled namespaces + - namespaceSelector: + matchLabels: + hub.jupyter.org/network-access-singleuser: "true" + {{- end }} + + {{- with .Values.singleuser.networkPolicy.ingress }} + # depends, but default is nothing --> singleuser-server + {{- . | toYaml | nindent 4 }} + {{- end }} + + egress: + # singleuser-server --> hub + - to: + - podSelector: + matchLabels: + {{- $_ := merge (dict "componentLabel" "hub") . }} + {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} + ports: + - port: 8081 + + # singleuser-server --> proxy + # singleuser-server --> autohttps + # + # While not critical for core functionality, a user or library code may rely + # on communicating with the proxy or autohttps pods via a k8s Service it can + # detected from well known environment variables. + # + - to: + - podSelector: + matchLabels: + {{- $_ := merge (dict "componentLabel" "proxy") . }} + {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} + ports: + - port: 8000 + - to: + - podSelector: + matchLabels: + {{- $_ := merge (dict "componentLabel" "autohttps") . }} + {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} + ports: + - port: 8080 + - port: 8443 + + {{- with (include "jupyterhub.networkPolicy.renderEgressRules" (list . .Values.singleuser.networkPolicy)) }} + {{- . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/jupyterhub/templates/singleuser/secret.yaml b/jupyterhub/templates/singleuser/secret.yaml new file mode 100644 index 0000000..f4a5fe2 --- /dev/null +++ b/jupyterhub/templates/singleuser/secret.yaml @@ -0,0 +1,17 @@ +{{- if .Values.singleuser.extraFiles }} +kind: Secret +apiVersion: v1 +metadata: + name: {{ include "jupyterhub.singleuser.fullname" . }} + labels: + {{- include "jupyterhub.labels" . | nindent 4 }} +type: Opaque +{{- with include "jupyterhub.extraFiles.data" .Values.singleuser.extraFiles }} +data: + {{- . | nindent 2 }} +{{- end }} +{{- with include "jupyterhub.extraFiles.stringData" .Values.singleuser.extraFiles }} +stringData: + {{- . | nindent 2 }} +{{- end }} +{{- end }} diff --git a/jupyterhub/values-minikube.yaml b/jupyterhub/values-minikube.yaml new file mode 100644 index 0000000..e1c90aa --- /dev/null +++ b/jupyterhub/values-minikube.yaml @@ -0,0 +1,663 @@ +# fullnameOverride and nameOverride distinguishes blank strings, null values, +# and non-blank strings. For more details, see the configuration reference. +fullnameOverride: "" +nameOverride: + +# custom can contain anything you want to pass to the hub pod, as all passed +# Helm template values will be made available there. +custom: {} + +# imagePullSecret is configuration to create a k8s Secret that Helm chart's pods +# can get credentials from to pull their images. +imagePullSecret: + create: false + automaticReferenceInjection: true + registry: + username: + password: + email: +# imagePullSecrets is configuration to reference the k8s Secret resources the +# Helm chart's pods can get credentials from to pull their images. +imagePullSecrets: [] + +# hub relates to the hub pod, responsible for running JupyterHub, its configured +# Authenticator class KubeSpawner, and its configured Proxy class +# ConfigurableHTTPProxy. KubeSpawner creates the user pods, and +# ConfigurableHTTPProxy speaks with the actual ConfigurableHTTPProxy server in +# the proxy pod. +hub: + revisionHistoryLimit: + config: + JupyterHub: + admin_access: true + authenticator_class: dummy + service: + type: ClusterIP + annotations: {} + ports: + nodePort: + extraPorts: [] + loadBalancerIP: + baseUrl: / + cookieSecret: + initContainers: [] + nodeSelector: {} + tolerations: [] + concurrentSpawnLimit: 64 + consecutiveFailureLimit: 5 + activeServerLimit: + deploymentStrategy: + ## type: Recreate + ## - sqlite-pvc backed hubs require the Recreate deployment strategy as a + ## typical PVC storage can only be bound to one pod at the time. + ## - JupyterHub isn't designed to support being run in parallell. More work + ## needs to be done in JupyterHub itself for a fully highly available (HA) + ## deployment of JupyterHub on k8s is to be possible. + type: Recreate + db: + type: sqlite-pvc + upgrade: + pvc: + annotations: {} + selector: {} + accessModes: + - ReadWriteOnce + storage: 10Gi + subPath: + storageClassName: standard + url: + password: + labels: {} + annotations: {} + command: [] + args: [] + extraConfig: {} + extraFiles: {} + extraEnv: { + JUPYTERHUB_ENV: "dev", + JUPYTERHUB_SINGLE_USER_IMAGE_NOTEBOOKS: "", + } + extraContainers: [] + extraVolumes: [] + extraVolumeMounts: [] + image: + name: hubimage + tag: "dev" + pullPolicy: + pullSecrets: [] + resources: {} + podSecurityContext: + fsGroup: 1000 + containerSecurityContext: + runAsUser: 1000 + runAsGroup: 1000 + allowPrivilegeEscalation: true + lifecycle: {} + loadRoles: {} + services: {} + pdb: + enabled: false + maxUnavailable: + minAvailable: 1 + networkPolicy: + enabled: false + ingress: [] + egress: [] + egressAllowRules: + cloudMetadataServer: true + dnsPortsPrivateIPs: true + nonPrivateIPs: true + privateIPs: true + interNamespaceAccessLabels: ignore + allowedIngressPorts: [] + allowNamedServers: true + namedServerLimitPerUser: + authenticatePrometheus: + redirectToServer: + shutdownOnLogout: + templatePaths: [] + templateVars: {} + livenessProbe: + # The livenessProbe's aim to give JupyterHub sufficient time to startup but + # be able to restart if it becomes unresponsive for ~5 min. + enabled: true + initialDelaySeconds: 300 + periodSeconds: 10 + failureThreshold: 30 + timeoutSeconds: 3 + readinessProbe: + # The readinessProbe's aim is to provide a successful startup indication, + # but following that never become unready before its livenessProbe fail and + # restarts it if needed. To become unready following startup serves no + # purpose as there are no other pod to fallback to in our non-HA deployment. + enabled: true + initialDelaySeconds: 0 + periodSeconds: 2 + failureThreshold: 1000 + timeoutSeconds: 1 + existingSecret: + serviceAccount: + create: true + name: + annotations: {} + extraPodSpec: {} + +rbac: + create: true + +# proxy relates to the proxy pod, the proxy-public service, and the autohttps +# pod and proxy-http service. +proxy: + secretToken: '8af21006c7c3dc381c5d3b4b27e2c99e6311d5fc243fqbf9a14646020197d67c' + annotations: {} + deploymentStrategy: + ## type: Recreate + ## - JupyterHub's interaction with the CHP proxy becomes a lot more robust + ## with this configuration. To understand this, consider that JupyterHub + ## during startup will interact a lot with the k8s service to reach a + ## ready proxy pod. If the hub pod during a helm upgrade is restarting + ## directly while the proxy pod is making a rolling upgrade, the hub pod + ## could end up running a sequence of interactions with the old proxy pod + ## and finishing up the sequence of interactions with the new proxy pod. + ## As CHP proxy pods carry individual state this is very error prone. One + ## outcome when not using Recreate as a strategy has been that user pods + ## have been deleted by the hub pod because it considered them unreachable + ## as it only configured the old proxy pod but not the new before trying + ## to reach them. + type: Recreate + ## rollingUpdate: + ## - WARNING: + ## This is required to be set explicitly blank! Without it being + ## explicitly blank, k8s will let eventual old values under rollingUpdate + ## remain and then the Deployment becomes invalid and a helm upgrade would + ## fail with an error like this: + ## + ## UPGRADE FAILED + ## Error: Deployment.apps "proxy" is invalid: spec.strategy.rollingUpdate: Forbidden: may not be specified when strategy `type` is 'Recreate' + ## Error: UPGRADE FAILED: Deployment.apps "proxy" is invalid: spec.strategy.rollingUpdate: Forbidden: may not be specified when strategy `type` is 'Recreate' + rollingUpdate: + # service relates to the proxy-public service + service: + type: LoadBalancer + labels: {} + annotations: {} + nodePorts: + http: + https: + disableHttpPort: false + extraPorts: [] + loadBalancerIP: + loadBalancerSourceRanges: [] + # chp relates to the proxy pod, which is responsible for routing traffic based + # on dynamic configuration sent from JupyterHub to CHP's REST API. + chp: + revisionHistoryLimit: + containerSecurityContext: + runAsUser: 65534 # nobody user + runAsGroup: 65534 # nobody group + allowPrivilegeEscalation: false + image: + name: jupyterhub/configurable-http-proxy + # tag is automatically bumped to new patch versions by the + # watch-dependencies.yaml workflow. + # + tag: "4.5.3" # https://github.com/jupyterhub/configurable-http-proxy/releases + pullPolicy: + pullSecrets: [] + extraCommandLineFlags: [] + livenessProbe: + enabled: true + initialDelaySeconds: 60 + periodSeconds: 10 + failureThreshold: 30 + timeoutSeconds: 3 + readinessProbe: + enabled: true + initialDelaySeconds: 0 + periodSeconds: 2 + failureThreshold: 1000 + timeoutSeconds: 1 + resources: {} + defaultTarget: + errorTarget: + extraEnv: {} + nodeSelector: {} + tolerations: [] + networkPolicy: + enabled: false + ingress: [] + egress: [] + egressAllowRules: + cloudMetadataServer: true + dnsPortsPrivateIPs: true + nonPrivateIPs: true + privateIPs: true + interNamespaceAccessLabels: ignore + allowedIngressPorts: [http, https] + pdb: + enabled: false + maxUnavailable: + minAvailable: 1 + extraPodSpec: {} + # traefik relates to the autohttps pod, which is responsible for TLS + # termination when proxy.https.type=letsencrypt. + traefik: + revisionHistoryLimit: + containerSecurityContext: + runAsUser: 65534 # nobody user + runAsGroup: 65534 # nobody group + allowPrivilegeEscalation: false + image: + name: traefik + # tag is automatically bumped to new patch versions by the + # watch-dependencies.yaml workflow. + # + tag: "v2.8.4" # ref: https://hub.docker.com/_/traefik?tab=tags + pullPolicy: + pullSecrets: [] + hsts: + includeSubdomains: false + preload: false + maxAge: 15724800 # About 6 months + resources: {} + labels: {} + extraInitContainers: [] + extraEnv: {} + extraVolumes: [] + extraVolumeMounts: [] + extraStaticConfig: {} + extraDynamicConfig: {} + nodeSelector: {} + tolerations: [] + extraPorts: [] + networkPolicy: + enabled: false + ingress: [] + egress: [] + egressAllowRules: + cloudMetadataServer: true + dnsPortsPrivateIPs: true + nonPrivateIPs: true + privateIPs: true + interNamespaceAccessLabels: ignore + allowedIngressPorts: [http, https] + pdb: + enabled: false + maxUnavailable: + minAvailable: 1 + serviceAccount: + create: true + name: + annotations: {} + extraPodSpec: {} + secretSync: + containerSecurityContext: + runAsUser: 65534 # nobody user + runAsGroup: 65534 # nobody group + allowPrivilegeEscalation: false + image: + name: jupyterhub/k8s-secret-sync + tag: "2.0.0" + pullPolicy: + pullSecrets: [] + resources: {} + labels: {} + https: + enabled: false + type: letsencrypt + #type: letsencrypt, manual, offload, secret + letsencrypt: + contactEmail: + # Specify custom server here (https://acme-staging-v02.api.letsencrypt.org/directory) to hit staging LE + acmeServer: https://acme-v02.api.letsencrypt.org/directory + manual: + key: + cert: + secret: + name: + key: tls.key + crt: tls.crt + hosts: [] + +# singleuser relates to the configuration of KubeSpawner which runs in the hub +# pod, and its spawning of user pods such as jupyter-myusername. +singleuser: + podNameTemplate: + extraTolerations: [] + nodeSelector: {"k8s.scaleway.com/pool-name": "processing-node-pool-dev"} + extraNodeAffinity: + required: [] + preferred: [] + extraPodAffinity: + required: [] + preferred: [] + extraPodAntiAffinity: + required: [] + preferred: [] + networkTools: + image: + name: jupyterhub/k8s-network-tools + tag: "2.0.0" + pullPolicy: + pullSecrets: [] + resources: {} + cloudMetadata: + # block set to true will append a privileged initContainer using the + # iptables to block the sensitive metadata server at the provided ip. + blockWithIptables: true + ip: 169.254.169.254 + networkPolicy: + enabled: false + ingress: [] + egress: [] + egressAllowRules: + cloudMetadataServer: false + dnsPortsPrivateIPs: true + nonPrivateIPs: true + privateIPs: false + interNamespaceAccessLabels: ignore + allowedIngressPorts: [] + events: true + extraAnnotations: {} + extraLabels: + hub.jupyter.org/network-access-hub: "true" + extraFiles: {} + extraEnv: {} + lifecycleHooks: {} + initContainers: [] + extraContainers: [] + allowPrivilegeEscalation: false + uid: 1000 + fsGid: 100 + serviceAccountName: + storage: + type: dynamic + extraLabels: {} + extraVolumes: [] + extraVolumeMounts: [] + static: + pvcName: + subPath: "{username}" + capacity: 10Gi + homeMountPath: /home/jovyan + dynamic: + storageClass: standard + pvcNameTemplate: claim-{username}{servername} + volumeNameTemplate: volume-{username}{servername} + storageAccessModes: [ReadWriteOnce] + image: + name: jupyterhub/k8s-singleuser-sample + tag: "2.0.0" + pullPolicy: + pullSecrets: [] + startTimeout: 6000 + cpu: + limit: + guarantee: + memory: + limit: + guarantee: 1G + extraResource: + limits: {} + guarantees: {} + cmd: jupyterhub-singleuser + defaultUrl: + extraPodConfig: {} + profileList: [] + +# scheduling relates to the user-scheduler pods and user-placeholder pods. +scheduling: + userScheduler: + enabled: true + revisionHistoryLimit: + replicas: 2 + logLevel: 4 + # plugins are configured on the user-scheduler to make us score how we + # schedule user pods in a way to help us schedule on the most busy node. By + # doing this, we help scale down more effectively. It isn't obvious how to + # enable/disable scoring plugins, and configure them, to accomplish this. + # + # plugins ref: https://kubernetes.io/docs/reference/scheduling/config/#scheduling-plugins-1 + # migration ref: https://kubernetes.io/docs/reference/scheduling/config/#scheduler-configuration-migrations + # + plugins: + score: + # These scoring plugins are enabled by default according to + # https://kubernetes.io/docs/reference/scheduling/config/#scheduling-plugins + # 2022-02-22. + # + # Enabled with high priority: + # - NodeAffinity + # - InterPodAffinity + # - NodeResourcesFit + # - ImageLocality + # Remains enabled with low default priority: + # - TaintToleration + # - PodTopologySpread + # - VolumeBinding + # Disabled for scoring: + # - NodeResourcesBalancedAllocation + # + disabled: + # We disable these plugins (with regards to scoring) to not interfere + # or complicate our use of NodeResourcesFit. + - name: NodeResourcesBalancedAllocation + # Disable plugins to be allowed to enable them again with a different + # weight and avoid an error. + - name: NodeAffinity + - name: InterPodAffinity + - name: NodeResourcesFit + - name: ImageLocality + enabled: + - name: NodeAffinity + weight: 14631 + - name: InterPodAffinity + weight: 1331 + - name: NodeResourcesFit + weight: 121 + - name: ImageLocality + weight: 11 + pluginConfig: + # Here we declare that we should optimize pods to fit based on a + # MostAllocated strategy instead of the default LeastAllocated. + - name: NodeResourcesFit + args: + scoringStrategy: + resources: + - name: cpu + weight: 1 + - name: memory + weight: 1 + type: MostAllocated + containerSecurityContext: + runAsUser: 65534 # nobody user + runAsGroup: 65534 # nobody group + allowPrivilegeEscalation: false + image: + # IMPORTANT: Bumping the minor version of this binary should go hand in + # hand with an inspection of the user-scheduelrs RBAC resources + # that we have forked in + # templates/scheduling/user-scheduler/rbac.yaml. + # + # Debugging advice: + # + # - Is configuration of kube-scheduler broken in + # templates/scheduling/user-scheduler/configmap.yaml? + # + # - Is the kube-scheduler binary's compatibility to work + # against a k8s api-server that is too new or too old? + # + # - You can update the GitHub workflow that runs tests to + # include "deploy/user-scheduler" in the k8s namespace report + # and reduce the user-scheduler deployments replicas to 1 in + # dev-config.yaml to get relevant logs from the user-scheduler + # pods. Inspect the "Kubernetes namespace report" action! + # + # - Typical failures are that kube-scheduler fails to search for + # resources via its "informers", and won't start trying to + # schedule pods before they succeed which may require + # additional RBAC permissions or that the k8s api-server is + # aware of the resources. + # + # - If "successfully acquired lease" can be seen in the logs, it + # is a good sign kube-scheduler is ready to schedule pods. + # + name: k8s.gcr.io/kube-scheduler + # tag is automatically bumped to new patch versions by the + # watch-dependencies.yaml workflow. The minor version is pinned in the + # workflow, and should be updated there if a minor version bump is done + # here. + # + tag: "v1.23.10" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + pullPolicy: + pullSecrets: [] + nodeSelector: {} + tolerations: [] + labels: {} + annotations: {} + pdb: + enabled: true + maxUnavailable: 1 + minAvailable: + resources: {} + serviceAccount: + create: true + name: + annotations: {} + extraPodSpec: {} + podPriority: + enabled: false + globalDefault: false + defaultPriority: 0 + imagePullerPriority: -5 + userPlaceholderPriority: -10 + userPlaceholder: + enabled: true + image: + name: k8s.gcr.io/pause + # tag is automatically bumped to new patch versions by the + # watch-dependencies.yaml workflow. + # + # If you update this, also update prePuller.pause.image.tag + # + tag: "3.8" + pullPolicy: + pullSecrets: [] + revisionHistoryLimit: + replicas: 0 + labels: {} + annotations: {} + containerSecurityContext: + runAsUser: 65534 # nobody user + runAsGroup: 65534 # nobody group + allowPrivilegeEscalation: false + resources: {} + corePods: + tolerations: + - key: hub.jupyter.org/dedicated + operator: Equal + value: core + effect: NoSchedule + - key: hub.jupyter.org_dedicated + operator: Equal + value: core + effect: NoSchedule + nodeAffinity: + matchNodePurpose: prefer + userPods: + tolerations: + - key: hub.jupyter.org/dedicated + operator: Equal + value: user + effect: NoSchedule + - key: hub.jupyter.org_dedicated + operator: Equal + value: user + effect: NoSchedule + nodeAffinity: + matchNodePurpose: prefer + +# prePuller relates to the hook|continuous-image-puller DaemonsSets +prePuller: + revisionHistoryLimit: + labels: {} + annotations: {} + resources: {} + containerSecurityContext: + runAsUser: 65534 # nobody user + runAsGroup: 65534 # nobody group + allowPrivilegeEscalation: false + extraTolerations: [] + # hook relates to the hook-image-awaiter Job and hook-image-puller DaemonSet + hook: + enabled: true + pullOnlyOnChanges: true + # image and the configuration below relates to the hook-image-awaiter Job + image: + name: jupyterhub/k8s-image-awaiter + tag: "2.0.0" + pullPolicy: + pullSecrets: [] + containerSecurityContext: + runAsUser: 65534 # nobody user + runAsGroup: 65534 # nobody group + allowPrivilegeEscalation: false + podSchedulingWaitDuration: 10 + nodeSelector: {} + tolerations: [] + resources: {} + serviceAccount: + create: true + name: + annotations: {} + continuous: + enabled: true + pullProfileListImages: true + extraImages: {} + pause: + containerSecurityContext: + runAsUser: 65534 # nobody user + runAsGroup: 65534 # nobody group + allowPrivilegeEscalation: false + image: + name: k8s.gcr.io/pause + # tag is automatically bumped to new patch versions by the + # watch-dependencies.yaml workflow. + # + # If you update this, also update scheduling.userPlaceholder.image.tag + # + tag: "3.8" + pullPolicy: + pullSecrets: [] + +ingress: + enabled: false + annotations: {} + ingressClassName: + hosts: [] + pathSuffix: + pathType: Prefix + tls: [] + +# cull relates to the jupyterhub-idle-culler service, responsible for evicting +# inactive singleuser pods. +# +# The configuration below, except for enabled, corresponds to command-line flags +# for jupyterhub-idle-culler as documented here: +# https://github.com/jupyterhub/jupyterhub-idle-culler#as-a-standalone-script +# +cull: + enabled: true + users: false # --cull-users + adminUsers: true # --cull-admin-users + removeNamedServers: false # --remove-named-servers + timeout: 3600 # --timeout + every: 600 # --cull-every + concurrency: 10 # --concurrency + maxAge: 0 # --max-age + +debug: + enabled: false + +global: + safeToShowValues: false diff --git a/jupyterhub/values.schema.json b/jupyterhub/values.schema.json new file mode 100644 index 0000000..cc589df --- /dev/null +++ b/jupyterhub/values.schema.json @@ -0,0 +1 @@ +{"$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "required": ["imagePullSecrets", "hub", "proxy", "singleuser", "ingress", "prePuller", "custom", "cull", "debug", "rbac", "global"], "properties": {"fullnameOverride": {"type": ["string", "null"]}, "nameOverride": {"type": ["string", "null"]}, "imagePullSecret": {"type": "object", "required": ["create"], "if": {"properties": {"create": {"const": true}}}, "then": {"additionalProperties": false, "required": ["registry", "username", "password"], "description": "This is configuration to create a k8s Secret resource of `type:\nkubernetes.io/dockerconfigjson`, with credentials to pull images from a\nprivate image registry. If you opt to do so, it will be available for use\nby all pods in their respective `spec.imagePullSecrets` alongside other\nk8s Secrets defined in `imagePullSecrets` or the pod respective\n`...image.pullSecrets` configuration.\n\nIn other words, using this configuration option can automate both the\notherwise manual creation of a k8s Secret and the otherwise manual\nconfiguration to reference this k8s Secret in all the pods of the Helm\nchart.\n\n```sh\n# you won't need to create a k8s Secret manually...\nkubectl create secret docker-registry image-pull-secret \\\n --docker-server= \\\n --docker-username= \\\n --docker-email= \\\n --docker-password=\n```\n\nIf you just want to let all Pods reference an existing secret, use the\n[`imagePullSecrets`](schema_imagePullSecrets) configuration instead.\n", "properties": {"create": {"type": "boolean", "description": "Toggle the creation of the k8s Secret with provided credentials to\naccess a private image registry.\n"}, "automaticReferenceInjection": {"type": "boolean", "description": "Toggle the automatic reference injection of the created Secret to all\npods' `spec.imagePullSecrets` configuration.\n"}, "registry": {"type": "string", "description": "Name of the private registry you want to create a credential set for.\nIt will default to Docker Hub's image registry.\n\nExamples:\n - https://index.docker.io/v1/\n - quay.io\n - eu.gcr.io\n - alexmorreale.privatereg.net\n"}, "username": {"type": "string", "description": "Name of the user you want to use to connect to your private registry.\n\nFor external gcr.io, you will use the `_json_key`.\n\nExamples:\n - alexmorreale\n - alex@pfc.com\n - _json_key\n"}, "password": {"type": "string", "description": "Password for the private image registry's user.\n\nExamples:\n - plaintextpassword\n - abc123SECRETzyx098\n\nFor gcr.io registries the password will be a big JSON blob for a\nGoogle cloud service account, it should look something like below.\n\n```yaml\npassword: |-\n {\n \"type\": \"service_account\",\n \"project_id\": \"jupyter-se\",\n \"private_key_id\": \"f2ba09118a8d3123b3321bd9a7d6d0d9dc6fdb85\",\n ...\n }\n```\n"}, "email": {"type": ["string", "null"], "description": "Specification of an email is most often not required, but it is\nsupported.\n"}}}}, "imagePullSecrets": {"type": "array"}, "hub": {"type": "object", "additionalProperties": false, "required": ["baseUrl"], "properties": {"revisionHistoryLimit": {"type": ["integer", "null"], "minimum": 0}, "config": {"type": "object", "additionalProperties": true}, "extraFiles": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "object", "additionalProperties": false, "required": ["mountPath"], "oneOf": [{"required": ["data"]}, {"required": ["stringData"]}, {"required": ["binaryData"]}], "properties": {"mountPath": {"type": "string"}, "data": {"type": "object", "additionalProperties": true}, "stringData": {"type": "string"}, "binaryData": {"type": "string"}, "mode": {"type": "number"}}}}}, "baseUrl": {"type": "string"}, "command": {"type": "array"}, "args": {"type": "array"}, "cookieSecret": {"type": ["string", "null"]}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "networkPolicy": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "ingress": {"type": "array"}, "egress": {"type": "array"}, "egressAllowRules": {"type": "object", "additionalProperties": false, "properties": {"cloudMetadataServer": {"type": "boolean"}, "dnsPortsPrivateIPs": {"type": "boolean"}, "nonPrivateIPs": {"type": "boolean"}, "privateIPs": {"type": "boolean"}}}, "interNamespaceAccessLabels": {"enum": ["accept", "ignore"]}, "allowedIngressPorts": {"type": "array"}}}, "db": {"type": "object", "additionalProperties": false, "properties": {"type": {"enum": ["sqlite-pvc", "sqlite-memory", "mysql", "postgres", "other"]}, "pvc": {"type": "object", "additionalProperties": false, "required": ["storage"], "properties": {"annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "selector": {"type": "object", "additionalProperties": true}, "storage": {"type": "string"}, "accessModes": {"type": "array", "items": {"type": ["string", "null"]}}, "storageClassName": {"type": ["string", "null"]}, "subPath": {"type": ["string", "null"]}}}, "upgrade": {"type": ["boolean", "null"]}, "url": {"type": ["string", "null"]}, "password": {"type": ["string", "null"]}}}, "labels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "initContainers": {"type": "array"}, "extraEnv": {"type": ["object", "array"], "additionalProperties": true}, "extraConfig": {"type": "object", "additionalProperties": true}, "fsGid": {"type": ["integer", "null"], "minimum": 0}, "service": {"type": "object", "additionalProperties": false, "properties": {"type": {"enum": ["ClusterIP", "NodePort", "LoadBalancer", "ExternalName"]}, "ports": {"type": "object", "additionalProperties": false, "properties": {"nodePort": {"type": ["integer", "null"], "minimum": 0}}}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "extraPorts": {"type": "array"}, "loadBalancerIP": {"type": ["string", "null"]}}}, "pdb": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "maxUnavailable": {"type": ["integer", "null"]}, "minAvailable": {"type": ["integer", "null"]}}}, "existingSecret": {"type": ["string", "null"]}, "nodeSelector": {"type": "object", "additionalProperties": true}, "tolerations": {"type": "array"}, "activeServerLimit": {"type": ["integer", "null"]}, "allowNamedServers": {"type": ["boolean", "null"]}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "authenticatePrometheus": {"type": ["boolean", "null"]}, "concurrentSpawnLimit": {"type": ["integer", "null"]}, "consecutiveFailureLimit": {"type": ["integer", "null"]}, "podSecurityContext": {"additionalProperties": true}, "containerSecurityContext": {"type": "object", "additionalProperties": true}, "deploymentStrategy": {"type": "object", "additionalProperties": false, "properties": {"rollingUpdate": {"type": ["string", "null"]}, "type": {"type": ["string", "null"]}}}, "extraContainers": {"type": "array"}, "extraVolumeMounts": {"type": "array"}, "extraVolumes": {"type": "array"}, "livenessProbe": {"type": "object", "additionalProperties": true, "required": ["enabled"], "if": {"properties": {"enabled": {"const": true}}}, "then": {"description": "This config option is like the k8s native specification of a\ncontainer probe, except that it also supports an `enabled` boolean\nflag.\n\nSee [the k8s\ndocumentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#probe-v1-core)\nfor more details.\n"}}, "readinessProbe": {"type": "object", "additionalProperties": true, "required": ["enabled"], "if": {"properties": {"enabled": {"const": true}}}, "then": {"description": "This config option is like the k8s native specification of a\ncontainer probe, except that it also supports an `enabled` boolean\nflag.\n\nSee [the k8s\ndocumentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#probe-v1-core)\nfor more details.\n"}}, "namedServerLimitPerUser": {"type": ["integer", "null"]}, "redirectToServer": {"type": ["boolean", "null"]}, "resources": {"type": "object", "additionalProperties": true}, "lifecycle": {"type": "object", "additionalProperties": false, "properties": {"postStart": {"type": "object", "additionalProperties": true}, "preStop": {"type": "object", "additionalProperties": true}}}, "services": {"type": "object", "additionalProperties": true, "properties": {"name": {"type": "string"}, "admin": {"type": "boolean"}, "command": {"type": ["string", "array"]}, "url": {"type": "string"}, "api_token": {"type": ["string", "null"]}, "apiToken": {"type": ["string", "null"]}}}, "loadRoles": {"type": "object", "additionalProperties": true}, "shutdownOnLogout": {"type": ["boolean", "null"]}, "templatePaths": {"type": "array"}, "templateVars": {"type": "object", "additionalProperties": true}, "serviceAccount": {"type": "object", "required": ["create"], "additionalProperties": false, "properties": {"create": {"type": "boolean"}, "name": {"type": ["string", "null"]}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}}}, "extraPodSpec": {"type": "object", "additionalProperties": true}}}, "proxy": {"type": "object", "additionalProperties": false, "properties": {"chp": {"type": "object", "additionalProperties": false, "properties": {"revisionHistoryLimit": {"type": ["integer", "null"], "minimum": 0}, "networkPolicy": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "ingress": {"type": "array"}, "egress": {"type": "array"}, "egressAllowRules": {"type": "object", "additionalProperties": false, "properties": {"cloudMetadataServer": {"type": "boolean"}, "dnsPortsPrivateIPs": {"type": "boolean"}, "nonPrivateIPs": {"type": "boolean"}, "privateIPs": {"type": "boolean"}}}, "interNamespaceAccessLabels": {"enum": ["accept", "ignore"]}, "allowedIngressPorts": {"type": "array"}}}, "extraCommandLineFlags": {"type": "array"}, "extraEnv": {"type": ["object", "array"], "additionalProperties": true}, "pdb": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "maxUnavailable": {"type": ["integer", "null"]}, "minAvailable": {"type": ["integer", "null"]}}}, "nodeSelector": {"type": "object", "additionalProperties": true}, "tolerations": {"type": "array"}, "containerSecurityContext": {"type": "object", "additionalProperties": true}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "livenessProbe": {"type": "object", "additionalProperties": true, "required": ["enabled"], "if": {"properties": {"enabled": {"const": true}}}, "then": {"description": "This config option is like the k8s native specification of a\ncontainer probe, except that it also supports an `enabled` boolean\nflag.\n\nSee [the k8s\ndocumentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#probe-v1-core)\nfor more details.\n"}}, "readinessProbe": {"type": "object", "additionalProperties": true, "required": ["enabled"], "if": {"properties": {"enabled": {"const": true}}}, "then": {"description": "This config option is like the k8s native specification of a\ncontainer probe, except that it also supports an `enabled` boolean\nflag.\n\nSee [the k8s\ndocumentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#probe-v1-core)\nfor more details.\n"}}, "resources": {"type": "object", "additionalProperties": true}, "defaultTarget": {"type": ["string", "null"]}, "errorTarget": {"type": ["string", "null"]}, "extraPodSpec": {"type": "object", "additionalProperties": true}}}, "secretToken": {"type": ["string", "null"]}, "service": {"type": "object", "additionalProperties": false, "properties": {"type": {"enum": ["ClusterIP", "NodePort", "LoadBalancer", "ExternalName"]}, "labels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "nodePorts": {"type": "object", "additionalProperties": false, "properties": {"http": {"type": ["integer", "null"]}, "https": {"type": ["integer", "null"]}}}, "disableHttpPort": {"type": "boolean"}, "extraPorts": {"type": "array"}, "loadBalancerIP": {"type": ["string", "null"]}, "loadBalancerSourceRanges": {"type": "array"}}}, "https": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": ["boolean", "null"]}, "type": {"enum": [null, "", "letsencrypt", "manual", "offload", "secret"]}, "letsencrypt": {"type": "object", "additionalProperties": false, "properties": {"contactEmail": {"type": ["string", "null"]}, "acmeServer": {"type": ["string", "null"]}}}, "manual": {"type": "object", "additionalProperties": false, "properties": {"key": {"type": ["string", "null"]}, "cert": {"type": ["string", "null"]}}}, "secret": {"type": "object", "additionalProperties": false, "properties": {"name": {"type": ["string", "null"]}, "key": {"type": ["string", "null"]}, "crt": {"type": ["string", "null"]}}}, "hosts": {"type": "array"}}}, "traefik": {"type": "object", "additionalProperties": false, "properties": {"revisionHistoryLimit": {"type": ["integer", "null"], "minimum": 0}, "labels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "networkPolicy": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "ingress": {"type": "array"}, "egress": {"type": "array"}, "egressAllowRules": {"type": "object", "additionalProperties": false, "properties": {"cloudMetadataServer": {"type": "boolean"}, "dnsPortsPrivateIPs": {"type": "boolean"}, "nonPrivateIPs": {"type": "boolean"}, "privateIPs": {"type": "boolean"}}}, "interNamespaceAccessLabels": {"enum": ["accept", "ignore"]}, "allowedIngressPorts": {"type": "array"}}}, "extraInitContainers": {"type": "array"}, "extraEnv": {"type": ["object", "array"], "additionalProperties": true}, "pdb": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "maxUnavailable": {"type": ["integer", "null"]}, "minAvailable": {"type": ["integer", "null"]}}}, "nodeSelector": {"type": "object", "additionalProperties": true}, "tolerations": {"type": "array"}, "containerSecurityContext": {"type": "object", "additionalProperties": true}, "extraDynamicConfig": {"type": "object", "additionalProperties": true}, "extraPorts": {"type": "array"}, "extraStaticConfig": {"type": "object", "additionalProperties": true}, "extraVolumes": {"type": "array"}, "extraVolumeMounts": {"type": "array"}, "hsts": {"type": "object", "additionalProperties": false, "required": ["includeSubdomains", "maxAge", "preload"], "properties": {"includeSubdomains": {"type": "boolean"}, "maxAge": {"type": "integer"}, "preload": {"type": "boolean"}}}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "resources": {"type": "object", "additionalProperties": true}, "serviceAccount": {"type": "object", "required": ["create"], "additionalProperties": false, "properties": {"create": {"type": "boolean"}, "name": {"type": ["string", "null"]}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}}}, "extraPodSpec": {"type": "object", "additionalProperties": true}}}, "labels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "deploymentStrategy": {"type": "object", "additionalProperties": false, "properties": {"rollingUpdate": {"type": ["string", "null"]}, "type": {"type": ["string", "null"]}}}, "secretSync": {"type": "object", "additionalProperties": false, "properties": {"containerSecurityContext": {"type": "object", "additionalProperties": true}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "resources": {"type": "object", "additionalProperties": true}}}}}, "singleuser": {"type": "object", "additionalProperties": false, "properties": {"networkPolicy": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "ingress": {"type": "array"}, "egress": {"type": "array"}, "egressAllowRules": {"type": "object", "additionalProperties": false, "properties": {"cloudMetadataServer": {"type": "boolean"}, "dnsPortsPrivateIPs": {"type": "boolean"}, "nonPrivateIPs": {"type": "boolean"}, "privateIPs": {"type": "boolean"}}}, "interNamespaceAccessLabels": {"enum": ["accept", "ignore"]}, "allowedIngressPorts": {"type": "array"}}}, "podNameTemplate": {"type": ["string", "null"]}, "cpu": {"type": "object", "additionalProperties": false, "properties": {"limit": {"type": ["number", "null"]}, "guarantee": {"type": ["number", "null"]}}}, "memory": {"type": "object", "additionalProperties": false, "properties": {"limit": {"type": ["number", "string", "null"]}, "guarantee": {"type": ["number", "string", "null"]}}}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "initContainers": {"type": "array"}, "profileList": {"type": "array"}, "extraFiles": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "object", "additionalProperties": false, "required": ["mountPath"], "oneOf": [{"required": ["data"]}, {"required": ["stringData"]}, {"required": ["binaryData"]}], "properties": {"mountPath": {"type": "string"}, "data": {"type": "object", "additionalProperties": true}, "stringData": {"type": "string"}, "binaryData": {"type": "string"}, "mode": {"type": "number"}}}}}, "extraEnv": {"type": ["object", "array"], "additionalProperties": true}, "nodeSelector": {"type": "object", "additionalProperties": true}, "extraTolerations": {"type": "array"}, "extraNodeAffinity": {"type": "object", "additionalProperties": false, "properties": {"required": {"type": "array"}, "preferred": {"type": "array"}}}, "extraPodAffinity": {"type": "object", "additionalProperties": false, "properties": {"required": {"type": "array"}, "preferred": {"type": "array"}}}, "extraPodAntiAffinity": {"type": "object", "additionalProperties": false, "properties": {"required": {"type": "array"}, "preferred": {"type": "array"}}}, "cloudMetadata": {"type": "object", "additionalProperties": false, "properties": {"blockWithIptables": {"type": "boolean"}, "ip": {"type": "string"}}}, "cmd": {"type": ["array", "string", "null"]}, "defaultUrl": {"type": ["string", "null"]}, "events": {"type": ["boolean", "null"]}, "extraAnnotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "extraContainers": {"type": "array"}, "extraLabels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "extraPodConfig": {"type": "object", "additionalProperties": true}, "extraResource": {"type": "object", "additionalProperties": false, "properties": {"guarantees": {"type": "object", "additionalProperties": true}, "limits": {"type": "object", "additionalProperties": true}}}, "fsGid": {"type": ["integer", "null"]}, "lifecycleHooks": {"type": "object", "additionalProperties": false, "properties": {"postStart": {"type": "object", "additionalProperties": true}, "preStop": {"type": "object", "additionalProperties": true}}}, "networkTools": {"type": "object", "additionalProperties": false, "properties": {"image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "resources": {"type": "object", "additionalProperties": true}}}, "serviceAccountName": {"type": ["string", "null"]}, "startTimeout": {"type": ["integer", "null"]}, "storage": {"type": "object", "additionalProperties": false, "required": ["type", "homeMountPath"], "properties": {"capacity": {"type": ["string", "null"]}, "dynamic": {"type": "object", "additionalProperties": false, "properties": {"pvcNameTemplate": {"type": ["string", "null"]}, "storageAccessModes": {"type": "array", "items": {"type": ["string", "null"]}}, "storageClass": {"type": ["string", "null"]}, "volumeNameTemplate": {"type": ["string", "null"]}}}, "extraLabels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "extraVolumeMounts": {"type": "array"}, "extraVolumes": {"type": "array"}, "homeMountPath": {"type": "string"}, "static": {"type": "object", "additionalProperties": false, "properties": {"pvcName": {"type": ["string", "null"]}, "subPath": {"type": ["string", "null"]}}}, "type": {"enum": ["dynamic", "static", "none"]}}}, "allowPrivilegeEscalation": {"type": ["boolean", "null"]}, "uid": {"type": ["integer", "null"]}}}, "scheduling": {"type": "object", "additionalProperties": false, "properties": {"userScheduler": {"type": "object", "additionalProperties": false, "required": ["enabled", "plugins", "pluginConfig", "logLevel"], "properties": {"enabled": {"type": "boolean"}, "revisionHistoryLimit": {"type": ["integer", "null"], "minimum": 0}, "replicas": {"type": "integer"}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "pdb": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "maxUnavailable": {"type": ["integer", "null"]}, "minAvailable": {"type": ["integer", "null"]}}}, "nodeSelector": {"type": "object", "additionalProperties": true}, "tolerations": {"type": "array"}, "labels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "containerSecurityContext": {"type": "object", "additionalProperties": true}, "logLevel": {"type": "integer"}, "plugins": {"type": "object", "additionalProperties": true}, "pluginConfig": {"type": "array"}, "resources": {"type": "object", "additionalProperties": true}, "serviceAccount": {"type": "object", "required": ["create"], "additionalProperties": false, "properties": {"create": {"type": "boolean"}, "name": {"type": ["string", "null"]}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}}}, "extraPodSpec": {"type": "object", "additionalProperties": true}}}, "podPriority": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "globalDefault": {"type": "boolean"}, "defaultPriority": {"type": "integer"}, "imagePullerPriority": {"type": "integer"}, "userPlaceholderPriority": {"type": "integer"}}}, "userPlaceholder": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "revisionHistoryLimit": {"type": ["integer", "null"], "minimum": 0}, "replicas": {"type": "integer"}, "labels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "resources": {"type": "object", "additionalProperties": true}, "containerSecurityContext": {"type": "object", "additionalProperties": true}}}, "corePods": {"type": "object", "additionalProperties": false, "properties": {"tolerations": {"type": "array"}, "nodeAffinity": {"type": "object", "additionalProperties": false, "properties": {"matchNodePurpose": {"enum": ["ignore", "prefer", "require"]}}}}}, "userPods": {"type": "object", "additionalProperties": false, "properties": {"tolerations": {"type": "array"}, "nodeAffinity": {"type": "object", "additionalProperties": false, "properties": {"matchNodePurpose": {"enum": ["ignore", "prefer", "require"]}}}}}}}, "ingress": {"type": "object", "additionalProperties": false, "required": ["enabled"], "properties": {"enabled": {"type": "boolean"}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "ingressClassName": {"type": ["string", "null"]}, "hosts": {"type": "array"}, "pathSuffix": {"type": ["string", "null"]}, "pathType": {"enum": ["Prefix", "Exact", "ImplementationSpecific"]}, "tls": {"type": "array"}}}, "prePuller": {"type": "object", "additionalProperties": false, "required": ["hook", "continuous"], "properties": {"revisionHistoryLimit": {"type": ["integer", "null"], "minimum": 0}, "labels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "resources": {"type": "object", "additionalProperties": true}, "extraTolerations": {"type": "array"}, "hook": {"type": "object", "additionalProperties": false, "required": ["enabled"], "properties": {"enabled": {"type": "boolean"}, "pullOnlyOnChanges": {"type": "boolean"}, "podSchedulingWaitDuration": {"type": "integer"}, "nodeSelector": {"type": "object", "additionalProperties": true}, "tolerations": {"type": "array"}, "containerSecurityContext": {"type": "object", "additionalProperties": true}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "resources": {"type": "object", "additionalProperties": true}, "serviceAccount": {"type": "object", "required": ["create"], "additionalProperties": false, "properties": {"create": {"type": "boolean"}, "name": {"type": ["string", "null"]}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}}}}}, "continuous": {"type": "object", "additionalProperties": false, "required": ["enabled"], "properties": {"enabled": {"type": "boolean"}}}, "pullProfileListImages": {"type": "boolean"}, "extraImages": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}}}}}, "containerSecurityContext": {"type": "object", "additionalProperties": true}, "pause": {"type": "object", "additionalProperties": false, "properties": {"containerSecurityContext": {"type": "object", "additionalProperties": true}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}}}}}, "custom": {"type": "object", "additionalProperties": true}, "cull": {"type": "object", "additionalProperties": false, "required": ["enabled"], "properties": {"enabled": {"type": "boolean"}, "users": {"type": ["boolean", "null"]}, "adminUsers": {"type": ["boolean", "null"]}, "removeNamedServers": {"type": ["boolean", "null"]}, "timeout": {"type": ["integer", "null"]}, "every": {"type": ["integer", "null"]}, "concurrency": {"type": ["integer", "null"]}, "maxAge": {"type": ["integer", "null"]}}}, "debug": {"type": "object", "additionalProperties": false, "required": ["enabled"], "properties": {"enabled": {"type": "boolean"}}}, "rbac": {"type": "object", "additionalProperties": false, "required": ["create"], "properties": {"enabled": {"type": "boolean"}, "create": {"type": "boolean"}}}, "global": {"type": "object", "additionalProperties": true, "properties": {"safeToShowValues": {"type": "boolean"}}}}} diff --git a/sk-k8s/cluster-role-binding.yaml b/sk-k8s/cluster-role-binding.yaml new file mode 100644 index 0000000..e929f32 --- /dev/null +++ b/sk-k8s/cluster-role-binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: jupyter-rbac +subjects: + - kind: ServiceAccount + name: hub + namespace: jupyter +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: cluster-admin diff --git a/sk-k8s/job.yaml b/sk-k8s/job.yaml new file mode 100644 index 0000000..c122f07 --- /dev/null +++ b/sk-k8s/job.yaml @@ -0,0 +1,26 @@ +apiVersion: batch/v1 +kind: Job + +metadata: + name: hub-content-init + namespace: jupyter +spec: + template: + spec: + containers: + - name: hub-content-init + image: ubuntu:22.04 + command: ["/bin/bash", "-c"] + args: ["/init/init.sh"] + volumeMounts: + - name: init-script-volume + mountPath: /init/init.sh + readOnly: true + subPath: init.sh + restartPolicy: Never + volumes: + - name: init-script-volume + configMap: + defaultMode: 0700 + name: init-script-configmap + backoffLimit: 4 diff --git a/sk-k8s/script.yaml b/sk-k8s/script.yaml new file mode 100644 index 0000000..49bed58 --- /dev/null +++ b/sk-k8s/script.yaml @@ -0,0 +1,22 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: init-script-configmap + namespace: jupyter +data: + init.sh: | + #!/bin/bash + apt update + apt install -y jq curl + + token=`curl -X POST -d '{"auth": {"username": "jovyan", "token": "12345"}}' http://hub.jupyter.svc.cluster.local:8081/hub/api/users/jovyan/tokens | jq -r '.token'` + + curl --header "Authorization: Bearer $token" http://hub.jupyter.svc.cluster.local:8081/hub/api/groups + + # create groups + for group in group-a group-b group-c + do + curl --request POST --location http://hub.jupyter.svc.cluster.local:8081/hub/api/groups/${group} --header "Authorization: Bearer $token" --header 'Content-Type: application/json' + # add user to group + curl --request POST --location http://hub.jupyter.svc.cluster.local:8081/hub/api/groups/${group}/users --header "Authorization: Bearer $token" --header 'Content-Type: application/json' --data '{"users": ["jovyan"]}' + done diff --git a/skaffold.yaml b/skaffold.yaml new file mode 100644 index 0000000..7cca29e --- /dev/null +++ b/skaffold.yaml @@ -0,0 +1,32 @@ +apiVersion: skaffold/v4beta9 +kind: Config +build: + artifacts: + - image: hubimage + +profiles: + - name: reference + deploy: + helm: + releases: + - name: jupyterhub + chartPath: jupyterhub + namespace: jupyter + createNamespace: true + valuesFiles: + - jupyterhub/values-minikube.yaml + setValueTemplates: + hub.image.name: "{{.IMAGE_REPO_hubimage}}/{{.IMAGE_TAG_hubimage}}@{{.IMAGE_DIGEST_hubimage}}" + manifests: + rawYaml: + - sk-k8s/cluster-role-binding.yaml + - sk-k8s/script.yaml + - sk-k8s/job.yaml + + +portForward: +- resourceType: service + resourceName: proxy-public + namespace: jupyter # Optional, if you are using a specific namespace + port: 80 # Target port on the pod + localPort: 8000 # Local port on your machine diff --git a/validate_config.py b/validate_config.py deleted file mode 100644 index f320633..0000000 --- a/validate_config.py +++ /dev/null @@ -1,9 +0,0 @@ -from application_hub_context.parser import ConfigParser - -ws_config_parser = ConfigParser.read_file( - config_path="jupyterhub/files/hub/config.yml", user_groups=["group-2"] -) - -print(ws_config_parser.get_profile_by_slug(slug="ellip_studio_labs").dict()) - -print(ws_config_parser.get_profile_config_maps(profile_id="profile_studio_labs")) From 3fef71f9a3cc9b130cb14e8b4af0806c209dce3f Mon Sep 17 00:00:00 2001 From: Michele Sica <33629436+MicheleSica@users.noreply.github.com> Date: Wed, 25 Sep 2024 17:09:19 +0200 Subject: [PATCH 02/65] Updated version packages on requirements.txt --- requirements.txt | 321 ++++++++++++++--------------------------------- 1 file changed, 96 insertions(+), 225 deletions(-) diff --git a/requirements.txt b/requirements.txt index 8c2f081..157fe5e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,238 +1,109 @@ -# -# This file is autogenerated by pip-compile with python 3.8 -# To update, run: -# -# pip-compile requirements.in -# addict==2.4.0 - # via -r requirements.in -aiohttp==3.8.4 - # via kubernetes-asyncio +aiohappyeyeballs==2.4.0 +aiohttp==3.10.6 aiosignal==1.3.1 - # via aiohttp -alembic==1.11.1 - # via jupyterhub -annotated-types==0.6.0 - # via pydantic +alembic==1.13.3 +annotated-types==0.7.0 +arrow==1.3.0 async-generator==1.10 - # via jupyterhub -async-timeout==4.0.2 - # via aiohttp -attrs==23.1.0 - # via - # aiohttp - # jsonschema -cachetools==5.3.1 - # via google-auth -certifi==2023.5.7 - # via - # kubernetes - # kubernetes-asyncio - # requests -certipy==0.1.3 - # via jupyterhub -cffi==1.15.1 - # via cryptography -charset-normalizer==3.1.0 - # via - # aiohttp - # requests -coverage[toml]==7.2.7 - # via pytest-cov -cryptography==41.0.1 - # via pyopenssl +async-timeout==4.0.3 +attrs==24.2.0 +bcrypt==4.2.0 +cachetools==5.5.0 +certifi==2024.8.30 +certipy==0.2.1 +cffi==1.17.1 +charset-normalizer==3.3.2 +coverage==7.6.1 +cryptography==43.0.1 +durationpy==0.7 escapism==1.0.1 - # via jupyterhub-kubespawner -exceptiongroup==1.1.1 - # via pytest -frozenlist==1.3.3 - # via - # aiohttp - # aiosignal -google-auth==2.19.1 - # via kubernetes -greenlet==2.0.2 - # via sqlalchemy +exceptiongroup==1.2.2 +fqdn==1.5.1 +frozenlist==1.4.1 +google-auth==2.35.0 +greenlet==3.1.1 httplib2==0.22.0 - # via -r requirements.in -idna==3.4 - # via - # requests - # yarl -importlib-metadata==6.8.0 - # via - # alembic - # jupyterhub -importlib-resources==6.1.1 - # via - # alembic - # jsonschema +idna==3.10 +importlib_metadata==8.5.0 +importlib_resources==6.4.5 iniconfig==2.0.0 - # via pytest -jinja2==3.1.2 - # via - # jupyterhub - # jupyterhub-kubespawner -jsonschema==4.17.3 - # via - # jupyter-telemetry - # oauthenticator +isoduration==20.11.0 +Jinja2==3.1.4 +jsonpointer==3.0.0 +jsonschema==4.23.0 +jsonschema-specifications==2023.12.1 +jupyter-events==0.10.0 jupyter-telemetry==0.1.0 - # via jupyterhub -jupyterhub==4.0.0 - # via - # -r requirements.in - # jupyterhub-kubespawner - # oauthenticator -jupyterhub-idle-culler==1.2.1 - # via -r requirements.in -jupyterhub-kubespawner==6.0.0 - # via -r requirements.in -kubernetes==26.1.0 - # via -r requirements.in -kubernetes-asyncio==24.2.3 - # via jupyterhub-kubespawner -loguru==0.7.0 - # via -r requirements.in -mako==1.2.4 - # via alembic -markupsafe==2.1.3 - # via - # jinja2 - # mako -multidict==6.0.4 - # via - # aiohttp - # yarl -oauthenticator==15.1.0 - # via -r requirements.in +jupyterhub==5.1.0 +jupyterhub-firstuseauthenticator==1.1.0 +jupyterhub-hmacauthenticator==1.0 +jupyterhub-idle-culler==1.4.0 +jupyterhub-kubespawner==6.2.0 +jupyterhub-ldapauthenticator==1.3.2 +jupyterhub-ltiauthenticator==1.6.2 +jupyterhub-nativeauthenticator==1.3.0 +jupyterhub-tmpauthenticator==1.0.0 +kubernetes==31.0.0 +kubernetes_asyncio==31.1.0 +ldap3==2.9.1 +loguru==0.7.2 +Mako==1.3.5 +MarkupSafe==2.1.5 +multidict==6.1.0 +mwoauth==0.4.0 +nullauthenticator==1.0.0 +oauthenticator==17.0.0 oauthlib==3.2.2 - # via - # jupyterhub - # requests-oauthlib -packaging==23.1 - # via - # jupyterhub - # pytest -pamela==1.1.0 - # via jupyterhub -pkgutil-resolve-name==1.3.10 - # via jsonschema -pluggy==1.0.0 - # via pytest -prometheus-client==0.17.0 - # via jupyterhub -pyasn1==0.5.0 - # via - # pyasn1-modules - # rsa -pyasn1-modules==0.3.0 - # via google-auth -pycparser==2.21 - # via cffi -pydantic==2.4.2 - # via -r requirements.in -pydantic-core==2.10.1 - # via pydantic -pyjwt==2.7.0 - # via -r requirements.in -pyopenssl==23.2.0 - # via certipy -pyparsing==3.0.9 - # via httplib2 -pyrsistent==0.19.3 - # via jsonschema -pytest==7.3.1 - # via - # -r requirements.in - # pytest-asyncio - # pytest-cov -pytest-asyncio==0.21.0 - # via -r requirements.in -pytest-cov==4.1.0 - # via -r requirements.in -python-dateutil==2.8.2 - # via - # jupyterhub - # jupyterhub-idle-culler - # kubernetes - # kubernetes-asyncio +onetimepass==1.0.1 +packaging==24.1 +pamela==1.2.0 +pkgutil_resolve_name==1.3.10 +pluggy==1.5.0 +prometheus_client==0.21.0 +psycopg2-binary==2.9.9 +py-spy==0.3.14 +pyasn1==0.6.1 +pyasn1_modules==0.4.1 +pycparser==2.22 +pycurl==7.45.3 +pydantic==2.9.2 +pydantic_core==2.23.4 +PyJWT==2.9.0 +PyMySQL==1.1.1 +pyOpenSSL==24.2.1 +pyparsing==3.1.4 +pyrsistent==0.20.0 +pytest==8.3.3 +pytest-asyncio==0.24.0 +pytest-cov==5.0.0 +python-dateutil==2.9.0.post0 python-json-logger==2.0.7 - # via jupyter-telemetry -python-slugify==8.0.1 - # via jupyterhub-kubespawner -pyyaml==6.0 - # via - # jupyterhub-kubespawner - # kubernetes - # kubernetes-asyncio -requests==2.31.0 - # via - # jupyterhub - # kubernetes - # oauthenticator - # requests-mock - # requests-oauthlib -requests-mock==1.10.0 - # via -r requirements.in -requests-oauthlib==1.3.1 - # via kubernetes +python-slugify==8.0.4 +PyYAML==6.0.2 +referencing==0.35.1 +requests==2.32.3 +requests-mock==1.12.1 +requests-oauthlib==2.0.0 +rfc3339-validator==0.1.4 +rfc3986-validator==0.1.1 +rpds-py==0.20.0 rsa==4.9 - # via google-auth -ruamel-yaml==0.17.31 - # via - # jupyter-telemetry - # oauthenticator -ruamel-yaml-clib==0.2.7 - # via ruamel-yaml +ruamel.yaml==0.18.6 +ruamel.yaml.clib==0.2.8 six==1.16.0 - # via - # google-auth - # kubernetes - # kubernetes-asyncio - # python-dateutil - # requests-mock -sqlalchemy==2.0.15 - # via - # alembic - # jupyterhub +SQLAlchemy==2.0.35 +sqlalchemy-cockroachdb==2.0.2 +statsd==4.0.1 text-unidecode==1.3 - # via python-slugify tomli==2.0.1 - # via - # coverage - # pytest -tornado==6.3.2 - # via - # jupyterhub - # jupyterhub-idle-culler -traitlets==5.9.0 - # via - # jupyter-telemetry - # jupyterhub - # jupyterhub-kubespawner -typing-extensions==4.6.3 - # via - # alembic - # annotated-types - # pydantic - # pydantic-core - # sqlalchemy -urllib3==1.26.16 - # via - # google-auth - # jupyterhub-kubespawner - # kubernetes - # kubernetes-asyncio - # requests -websocket-client==1.5.2 - # via kubernetes -yarl==1.9.2 - # via aiohttp -zipp==3.17.0 - # via - # importlib-metadata - # importlib-resources - -# The following packages are considered to be unsafe in a requirements file: -# setuptools +tornado==6.4.1 +traitlets==5.14.3 +types-python-dateutil==2.9.0.20240906 +typing_extensions==4.12.2 +uri-template==1.3.0 +urllib3==2.2.3 +webcolors==24.8.0 +websocket-client==1.8.0 +yarl==1.12.1 +zipp==3.20.2 From 96002b716d13942b4417143289a47b7b366d96e5 Mon Sep 17 00:00:00 2001 From: Michele Sica <33629436+MicheleSica@users.noreply.github.com> Date: Wed, 25 Sep 2024 17:15:23 +0200 Subject: [PATCH 03/65] new Dockerfile improved --- Dockerfile | 52 +++++++++++++++++++++++++++++++++++----------------- 1 file changed, 35 insertions(+), 17 deletions(-) diff --git a/Dockerfile b/Dockerfile index 87b350c..b9dfa1b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,35 +6,53 @@ ARG HOME=/home/johub USER root -RUN apt update && \ - apt install npm git sudo -y && \ - npm install -g configurable-http-proxy - -RUN adduser --disabled-password \ - --gecos "Default user" \ +# Packages update and dependencies installation +RUN microdnf update -y && \ + microdnf install -y \ + npm \ + git \ + sudo \ + python3-pip \ + python3-devel \ + gcc \ + libcurl-devel \ + openssl-devel \ + && microdnf clean all + +# Installation of configurable-http-proxy via npm +RUN npm install -g configurable-http-proxy + +# User creation +RUN adduser \ --uid ${NB_UID} \ --home ${HOME} \ - --force-badname \ - ${NB_USER} + ${NB_USER} \ + --comment "Default user" \ + --shell /bin/bash -RUN adduser jovyan sudo && \ - echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers +# Add jovyan to the sudoers group +RUN usermod -aG wheel jovyan && \ + echo '%wheel ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers +# Python packages installation from requirements.txt COPY requirements.txt /tmp/requirements.txt -RUN pip3 install --upgrade --no-cache-dir \ - setuptools \ - pip +RUN pip3 install --upgrade --no-cache-dir setuptools pip +# Specific Python dependencies installation RUN PYCURL_SSL_LIBRARY=openssl \ - pip install --no-cache-dir \ - -r /tmp/requirements.txt + pip install --no-cache-dir -r /tmp/requirements.txt + +# Check and correct requirejs version +RUN sed -i 's/"version": "[^"]*"/"version": "2.3.7"/' /usr/local/share/jupyterhub/static/components/requirejs/package.json -# So we can actually write a db file here +# Set permission on the directory /srv/jupyterhub RUN chown ${NB_USER}:${NB_USER} /srv/jupyterhub COPY . /tmp -RUN cd /tmp && python setup.py install +RUN cd /tmp && python3 setup.py install +# Set not root user USER ${NB_USER} +# Command to start jupyterhub CMD ["jupyterhub", "--config", "/etc/jupyterhub/jupyterhub_config.py"] From 9d1f262da7bca486332a006d98012266366a5030 Mon Sep 17 00:00:00 2001 From: Michele Sica <33629436+MicheleSica@users.noreply.github.com> Date: Thu, 26 Sep 2024 17:51:46 +0200 Subject: [PATCH 04/65] Update Dockerfile set from image --- Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index b9dfa1b..d866f6c 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM jupyterhub/k8s-hub:2.0.0 +FROM ghcr.io/eoepca/container-k8s-hub/container-k8s-hub:2.0.0 ARG NB_USER=johub ARG NB_UID=1001 From 6de51f7a5ea5eab3b6478e415ceebfbcf739c3d3 Mon Sep 17 00:00:00 2001 From: Michele Sica <33629436+MicheleSica@users.noreply.github.com> Date: Thu, 26 Sep 2024 19:00:32 +0200 Subject: [PATCH 05/65] Create .github-ci.yaml --- .github/workflows/.github-ci.yaml | 94 +++++++++++++++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 .github/workflows/.github-ci.yaml diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml new file mode 100644 index 0000000..732c325 --- /dev/null +++ b/.github/workflows/.github-ci.yaml @@ -0,0 +1,94 @@ +name: Build, Test, and Deploy Docker Image + +on: + push: + branches: [eoepca-beta01] + +jobs: + build: + runs-on: ubuntu-latest + steps: + # Step 1: Checkout repository + - uses: actions/checkout@v4 + + # Step 2: Install Trivy + - name: Install Trivy + run: | + sudo apt-get update -y + sudo apt-get install -y wget apt-transport-https gnupg lsb-release + wget -qO - https://aquasecurity.github.io/trivy-repo/deb/public.key | sudo apt-key add - + echo deb https://aquasecurity.github.io/trivy-repo/deb $(lsb_release -sc) main | sudo tee -a /etc/apt/sources.list.d/trivy.list + sudo apt-get update -y + sudo apt-get install -y trivy + + # Step 3: Build Docker Image + - name: Build Docker Image + run: | + APP_NAME="application-hub-context" + APP_VERSION="1.6.2" + tag="${APP_NAME}:${APP_VERSION}" + echo "${{ secrets.CR_PASSWORD }}" | docker login -u "${{ secrets.CR_USERNAME }}" --password-stdin "${{ secrets.CR_REGISTRY }}" + docker build --build-arg CR_REGISTRY="${{ secrets.CR_REGISTRY }}" --build-arg CR_REPO="${{ secrets.CR_REPO }}" -t "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" --file Dockerfile . + + + # Step 4: Save Docker Image as tar.gz + - name: Save Docker Image as tar.gz + run: | + APP_NAME="application-hub-context" + APP_VERSION="1.6.2" + tag="${APP_NAME}:${APP_VERSION}" + docker save "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" -o "${APP_NAME}_${APP_VERSION}.tar" + tar -czf "${APP_NAME}_${APP_VERSION}.tar.gz" "${APP_NAME}_${APP_VERSION}.tar" + + # Step 5: Upload Docker Image tar.gz as an artifact + - name: Upload Docker Image Artifact + uses: actions/upload-artifact@v3 + with: + name: docker-image-tar + path: application-hub-context_1.6.2.tar.gz + + # Step 6: Scan Docker Image with Trivy + - name: Scan Docker Image with Trivy + run: | + APP_NAME="application-hub-context" + APP_VERSION="1.6.2" + tag="${APP_NAME}:${APP_VERSION}" + echo "${{ secrets.CR_PASSWORD }}" | docker login -u "${{ secrets.CR_USERNAME }}" --password-stdin "${{ secrets.CR_REGISTRY }}" + trivy image --no-progress --exit-code 1 --severity HIGH,CRITICAL,UNKNOWN --format table "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" + + deploy: + needs: build + runs-on: ubuntu-latest + steps: + # Step 1: Checkout repository + - uses: actions/checkout@v4 + + # Step 2: Download Docker Image tar.gz Artifact + - name: Download Docker Image Artifact + uses: actions/download-artifact@v3 + with: + name: docker-image-tar + + # Step 3: Extract the Docker Image tar.gz + - name: Extract Docker Image tar.gz + run: | + tar -xzf application-hub-context_1.6.2.tar.gz + + # Step 4: Load Docker Image + - name: Load Docker Image + run: | + docker load -i application-hub-context_1.6.2.tar + + # Step 5: Log in to Docker Registry (use GitHub secrets for security) + - name: Login to Docker Registry + run: | + echo "${{ secrets.CR_PASSWORD }}" | docker login -u "${{ secrets.CR_USERNAME }}" --password-stdin "${{ secrets.CR_REGISTRY }}" + + # Step 6: Push Docker Image to Registry + - name: Push Docker Image to Registry + run: | + APP_NAME="application-hub-context" + APP_VERSION="1.6.2" + tag="${APP_NAME}:${APP_VERSION}" + docker push "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" + From 77ee30ea4bd13a61c3a8e3f0026667e54a11ef37 Mon Sep 17 00:00:00 2001 From: Michele Sica <33629436+MicheleSica@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:18:12 +0200 Subject: [PATCH 06/65] Update .github-ci.yaml --- .github/workflows/.github-ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index 732c325..aca4a4a 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -28,7 +28,7 @@ jobs: APP_VERSION="1.6.2" tag="${APP_NAME}:${APP_VERSION}" echo "${{ secrets.CR_PASSWORD }}" | docker login -u "${{ secrets.CR_USERNAME }}" --password-stdin "${{ secrets.CR_REGISTRY }}" - docker build --build-arg CR_REGISTRY="${{ secrets.CR_REGISTRY }}" --build-arg CR_REPO="${{ secrets.CR_REPO }}" -t "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" --file Dockerfile . + docker build -t "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" --file Dockerfile . # Step 4: Save Docker Image as tar.gz From c6a4d23bd7b417bc352fcef08dd7bec8a8824a42 Mon Sep 17 00:00:00 2001 From: Michele Sica <33629436+MicheleSica@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:40:44 +0200 Subject: [PATCH 07/65] Update .github-ci.yaml --- .github/workflows/.github-ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index aca4a4a..56ac5df 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -21,7 +21,7 @@ jobs: sudo apt-get update -y sudo apt-get install -y trivy - # Step 3: Build Docker Image + # Step 3: Build Docker image - name: Build Docker Image run: | APP_NAME="application-hub-context" From 75af2dbd56e03bfbd3d54f9fc80bd4106e4a4418 Mon Sep 17 00:00:00 2001 From: Michele Sica <33629436+MicheleSica@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:45:29 +0200 Subject: [PATCH 08/65] Update .github-ci.yaml --- .github/workflows/.github-ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index 56ac5df..16054c0 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -31,7 +31,7 @@ jobs: docker build -t "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" --file Dockerfile . - # Step 4: Save Docker Image as tar.gz + # Step 4: Save Docker image as tar.gz - name: Save Docker Image as tar.gz run: | APP_NAME="application-hub-context" From fa72cf2485571793415860fd0a749bb0b8c0a441 Mon Sep 17 00:00:00 2001 From: Michele Sica <33629436+MicheleSica@users.noreply.github.com> Date: Fri, 27 Sep 2024 09:48:14 +0200 Subject: [PATCH 09/65] Update .github-ci.yaml --- .github/workflows/.github-ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index 16054c0..b23b3f0 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -22,7 +22,7 @@ jobs: sudo apt-get install -y trivy # Step 3: Build Docker image - - name: Build Docker Image + - name: Build Docker image run: | APP_NAME="application-hub-context" APP_VERSION="1.6.2" From a7c82f17f3aa1daa2d7510ff1190225c6902b86e Mon Sep 17 00:00:00 2001 From: Michele Sica <33629436+MicheleSica@users.noreply.github.com> Date: Fri, 27 Sep 2024 10:00:49 +0200 Subject: [PATCH 10/65] Update .github-ci.yaml --- .github/workflows/.github-ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index b23b3f0..aa7e6d6 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -90,5 +90,5 @@ jobs: APP_NAME="application-hub-context" APP_VERSION="1.6.2" tag="${APP_NAME}:${APP_VERSION}" - docker push "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" + docker push "${{ secrets.CR_REGISTRY }}"/"${{ secrets.CR_REPO }}"/${tag} From 4c6255dbdf6ac385c70493ab29ed355a62d4077a Mon Sep 17 00:00:00 2001 From: Michele Sica <33629436+MicheleSica@users.noreply.github.com> Date: Tue, 1 Oct 2024 12:32:47 +0200 Subject: [PATCH 11/65] Update requirements.in aligned to requirements.txt --- requirements.in | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/requirements.in b/requirements.in index 1e7680c..cfea6af 100644 --- a/requirements.in +++ b/requirements.in @@ -8,17 +8,17 @@ # https://github.com/jazzband/pip-tools -pyjwt -jupyterhub -pytest -pytest-asyncio -pytest-cov -requests-mock -jupyterhub-kubespawner -httplib2 -oauthenticator -jupyterhub-idle-culler -kubernetes -loguru -addict -pydantic>=2 +pyjwt==2.9.0 +jupyterhub==5.1.0 +pytest==8.3.3 +pytest-asyncio==0.24.0 +pytest-cov==5.0.0 +requests-mock==1.12.1 +jupyterhub-kubespawner==6.2.0 +httplib2==0.22.0 +oauthenticator==17.0.0 +jupyterhub-idle-culler==1.4.0 +kubernetes==31.0.0 +loguru==0.7.2 +addict==2.4.0 +pydantic==2.9.2 From 002182a7f377dbe458a705177ae2fc0a424d0b51 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 1 Oct 2024 12:39:46 +0200 Subject: [PATCH 12/65] fixes for demo cluster --- .dockerignore | 1 + .gitignore | 1 + application_hub_context/app_hub_context.py | 41 +- application_hub_context/models.py | 4 +- config-generator/README.md | 5 + config-generator/config-generator.ipynb | 8 +- config-generator/config-maps/bash-rc | 3 + config-generator/config-maps/init-stac.sh | 31 ++ config-generator/manifests/manifest.yaml | 421 ++++++++++++++++++ config-generator/models.py | 2 +- config-generator/requirements.txt | 3 + .../values-minikube.yaml => demo-values.yaml | 14 + files/hub/config.yml | 303 +++++++++++++ files/hub/jupyterhub_config.py | 179 ++++++++ files/hub/z2jh.py | 121 +++++ files/theme/page.html | 5 + files/theme/spawn.html | 7 + files/theme/spawn_pending.html | 94 ++++ jupyterhub/files/hub/jupyterhub_config.py | 21 +- jupyterhub/templates/hub/configmap-theme.yaml | 5 +- jupyterhub/templates/hub/configmap.yaml | 6 +- jupyterhub/values.schema.json | 1 - skaffold.yaml | 10 +- 23 files changed, 1254 insertions(+), 32 deletions(-) create mode 100644 config-generator/README.md create mode 100644 config-generator/config-maps/init-stac.sh create mode 100644 config-generator/manifests/manifest.yaml create mode 100644 config-generator/requirements.txt rename jupyterhub/values-minikube.yaml => demo-values.yaml (97%) create mode 100644 files/hub/config.yml create mode 100644 files/hub/jupyterhub_config.py create mode 100644 files/hub/z2jh.py create mode 100644 files/theme/page.html create mode 100644 files/theme/spawn.html create mode 100644 files/theme/spawn_pending.html delete mode 100644 jupyterhub/values.schema.json diff --git a/.dockerignore b/.dockerignore index 4f72fd2..05573c9 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,3 +2,4 @@ jupyterhub values.yaml config.yml config-generator +skaffold.yaml \ No newline at end of file diff --git a/.gitignore b/.gitignore index e239487..ec0db41 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ values.yaml build _README.md dist +.env-config-generator \ No newline at end of file diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index f805fec..ea46391 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -544,9 +544,7 @@ def apply_manifest(self, manifest): def unapply_manifests(self, manifest_content): - manifests = yaml.safe_load_all(manifest_content) - - for k8_object in manifests: + for k8_object in manifest_content: kind = k8_object.get("kind") self.spawner.log.info( f"Deleting {kind} {k8_object.get('metadata', {}).get('name')}" @@ -567,6 +565,14 @@ def unapply_manifests(self, manifest_content): self.batch_v1_api.delete_namespaced_job(name, namespace) elif kind == "Pod": self.core_v1_api.delete_namespaced_pod(name, namespace) + elif kind == "Role": + self.rbac_authorization_v1_api.delete_namespaced_role(name, namespace) + elif kind == "RoleBinding": + self.rbac_authorization_v1_api.delete_namespaced_role_binding(name, namespace) + elif kind == "ServiceAccount": + self.core_v1_api.delete_namespaced_service_account(name, namespace) + + # Add other kinds as needed else: self.spawner.log.error(f"Unsupported kind: {kind}") @@ -848,18 +854,25 @@ def initialise(self): for manifest in manifests: self.spawner.log.info(f"Apply manifest {manifest.name}") - try: - ms = yaml.safe_load_all(manifest.content) - for k8_object in ms: - self.spawner.log.info( - f"Apply manifest kind {k8_object['kind']}" + + for k8_object in manifest.content: + try: + # Log the object and its type + self.spawner.log.info(f"K8 Object: {k8_object}") + self.spawner.log.info(f"Object Type: {type(k8_object)}") + + # Check and log the 'kind' of the Kubernetes object + if 'kind' in k8_object: + self.spawner.log.info(f"Applying manifest of kind: {k8_object['kind']}") + self.apply_manifest(k8_object) # Apply the manifest + else: + self.spawner.log.warning(f"Manifest does not contain a 'kind': {k8_object}") + + except Exception as err: + self.spawner.log.error(f"Unexpected {err}, {type(err)}") + self.spawner.log.error( + f"Skipping creation of manifest {manifest.name}" ) - self.apply_manifest(k8_object) - except Exception as err: - self.spawner.log.error(f"Unexpected {err}, {type(err)}") - self.spawner.log.error( - f"Skipping creation of manifest {manifest.name}" - ) def dispose(self): profile_id = self.config_parser.get_profile_by_slug(slug=self.profile_slug).id diff --git a/application_hub_context/models.py b/application_hub_context/models.py index 51fa401..bda9082 100644 --- a/application_hub_context/models.py +++ b/application_hub_context/models.py @@ -1,5 +1,5 @@ from enum import Enum -from typing import List, Optional, Union +from typing import Dict, List, Optional, Union from pydantic import BaseModel, Field @@ -40,7 +40,7 @@ class defining a volume object: class Manifest(BaseModel): name: str key: str - content: Optional[str] = None + content: Optional[List[Dict]] = None persist: Optional[bool] = True diff --git a/config-generator/README.md b/config-generator/README.md new file mode 100644 index 0000000..8a4a3c1 --- /dev/null +++ b/config-generator/README.md @@ -0,0 +1,5 @@ +## ApplicationHub configuration generator + +This folder contains a notebook and the python modules to support the generation of ApplicationHub configurations. + +Create a Python environment with the dependencies listed in the file `requirements.txt` and open the notebook `config-generator.ipynb` \ No newline at end of file diff --git a/config-generator/config-generator.ipynb b/config-generator/config-generator.ipynb index a6d71d7..60fefb0 100644 --- a/config-generator/config-generator.ipynb +++ b/config-generator/config-generator.ipynb @@ -408,7 +408,7 @@ "image_pull_secret = ImagePullSecret(\n", " name=\"cr-config\",\n", " persist=False,\n", - " data=\"\",\n", + " data=\"ewogICAgImF1dGhzIjogewogICAgICAgICJjci50ZXJyYWR1ZS5jb20iOiB7CiAgICAgICAgICAgICJ1c2VybmFtZSI6ICJyb2JvdCRlb2VwY2EtcGx1cy1ybyIsCiAgICAgICAgICAgICJwYXNzd29yZCI6ICJQMlE4TnkyZ0lHODhkZkxveXlLN05QVUZVbHJOekFZSiIsCiAgICAgICAgICAgICJlbWFpbCI6ICJlb2VwY2EtcGx1c0B0ZXJyYWR1ZS5jb20iLAogICAgICAgICAgICAiYXV0aCI6ICJjbTlpYjNRa1pXOWxjR05oTFhCc2RYTXRjbTg2VURKUk9FNTVNbWRKUnpnNFpHWk1iM2w1U3pkT1VGVkdWV3h5VG5wQldVbz0iCiAgICAgICAgfQogICAgfQp9\",\n", ")" ] }, @@ -470,7 +470,7 @@ ")\n", "\n", "with open(\n", - " \"../jupyterhub/files/hub/config.yml\", \"w\"\n", + " \"../files/hub/config.yml\", \"w\"\n", ") as file:\n", " yaml.dump(config.dict(), file)" ] @@ -487,7 +487,7 @@ " Profile(id='profile_studio_coder2', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='Code Server Medium', description=None, slug='ellip_studio_coder_slug_m', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=4, cpu_guarantee=None, mem_limit='12G', mem_guarantee=None, image='eoepca/pde-code-server:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[ConfigMap(name='bash-rc', key='bash-rc', mount_path='/workspace/.bashrc', default_mode=None, readonly=True, content='alias ll=\"ls -l\"\\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\\nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\\n. /home/jovyan/.bashrc\\n# >>> conda initialize >>>\\n# !! Contents within this block are managed by \\'conda init\\' !!\\n__conda_setup=\"$(\\'/opt/conda/bin/conda\\' \\'shell.bash\\' \\'hook\\' 2> /dev/null)\"\\nif [ $? -eq 0 ]; then\\n eval \"$__conda_setup\"\\nelse\\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/conda.sh\"\\n else\\n export PATH=\"/srv/conda/bin:$PATH\"\\n fi\\nfi\\nunset __conda_setup\\n\\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/mamba.sh\"\\nfi\\n# <<< conda initialize <<<\\n', persist=True)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteMany'], volume_mount=VolumeMount(name='calrissian-volume', mount_path='/calrissian'), persist=False), Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': ' /workspace/.envs'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[], manifests=None),\n", " Profile(id='profile_demo_init_script', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='Coder demo init script', description='This profile is used to demonstrate the use of an init script', slug='eoepca_demo_init_script', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='eoepca/pde-code-server:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[ConfigMap(name='init', key='init', mount_path='/opt/init/.init.sh', default_mode='0660', readonly=True, content=\"set -x\\n\\ncd /workspace\\n\\ngit clone 'https://github.com/eoap/mastering-app-package.git'\\n\\ncode-server --install-extension ms-python.python\\ncode-server --install-extension redhat.vscode-yaml\\ncode-server --install-extension sbg-rabix.benten-cwl\\ncode-server --install-extension ms-toolsai.jupyter\\n\\nln -s /workspace/.local/share/code-server/extensions /workspace/extensions\\n\\nexit 0\\n\", persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteMany'], volume_mount=VolumeMount(name='calrissian-volume', mount_path='/calrissian'), persist=False), Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': '/workspace/.envs', 'CONDARC': '/workspace/.condarc', 'XDG_RUNTIME_DIR': '/workspace/.local', 'CODE_SERVER_WS': '/workspace/mastering-app-package'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[InitContainer(name='init-file-on-volume', image='eoepca/pde-code-server:develop', command=['sh', '-c', 'sh /opt/init/.init.sh'], volume_mounts=[VolumeMount(name='workspace-volume', mount_path='/workspace'), InitContainerVolumeMount(name='init', mount_path='/opt/init/.init.sh', sub_path='init')])], manifests=None),\n", " Profile(id='profile_jupyter_lab', groups=['group-c'], definition=ProfileDefinition(display_name='Jupyter Lab', description='Jupyter Lab with Python 3.11', slug='eoepca_jupyter_lab', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='jupyter/scipy-notebook', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'XDG_RUNTIME_DIR': '/workspace/.local', 'XDG_CONFIG_HOME': '/workspace/.config'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[], manifests=None),\n", - " Profile(id='profile_jupyter_lab_2', groups=['group-c'], definition=ProfileDefinition(display_name='Jupyter Lab - profile 2', description='Jupyter Lab with Python 3.11 - demoes the use of an image pull secret', slug='eoepca_jupyter_lab_2', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='jupyter/scipy-notebook', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'XDG_RUNTIME_DIR': '/workspace/.local', 'XDG_CONFIG_HOME': '/workspace/.config'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[ImagePullSecret(name='cr-config', persist=False, data='')], init_containers=[], manifests=None)]" + " Profile(id='profile_jupyter_lab_2', groups=['group-c'], definition=ProfileDefinition(display_name='Jupyter Lab - profile 2', description='Jupyter Lab with Python 3.11 - demoes the use of an image pull secret', slug='eoepca_jupyter_lab_2', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='jupyter/scipy-notebook', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'XDG_RUNTIME_DIR': '/workspace/.local', 'XDG_CONFIG_HOME': '/workspace/.config'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[ImagePullSecret(name='cr-config', persist=False, data='ewogICAgImF1dGhzIjogewogICAgICAgICJjci50ZXJyYWR1ZS5jb20iOiB7CiAgICAgICAgICAgICJ1c2VybmFtZSI6ICJyb2JvdCRlb2VwY2EtcGx1cy1ybyIsCiAgICAgICAgICAgICJwYXNzd29yZCI6ICJQMlE4TnkyZ0lHODhkZkxveXlLN05QVUZVbHJOekFZSiIsCiAgICAgICAgICAgICJlbWFpbCI6ICJlb2VwY2EtcGx1c0B0ZXJyYWR1ZS5jb20iLAogICAgICAgICAgICAiYXV0aCI6ICJjbTlpYjNRa1pXOWxjR05oTFhCc2RYTXRjbTg2VURKUk9FNTVNbWRKUnpnNFpHWk1iM2w1U3pkT1VGVkdWV3h5VG5wQldVbz0iCiAgICAgICAgfQogICAgfQp9')], init_containers=[], manifests=None)]" ] }, "execution_count": 15, @@ -537,7 +537,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.8" + "version": "3.10.14" }, "orig_nbformat": 4 }, diff --git a/config-generator/config-maps/bash-rc b/config-generator/config-maps/bash-rc index 6623894..4dff3fa 100644 --- a/config-generator/config-maps/bash-rc +++ b/config-generator/config-maps/bash-rc @@ -2,6 +2,9 @@ alias ll="ls -l" alias calrissian="/opt/conda/bin/calrissian --pod-nodeselectors /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram 16G --max-cores "8" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/" alias cwltool="/opt/conda/bin/cwltool --podman" . /home/jovyan/.bashrc + +alias aws="aws --endpoint-url=http://localstack:4566" + # >>> conda initialize >>> # !! Contents within this block are managed by 'conda init' !! __conda_setup="$('/opt/conda/bin/conda' 'shell.bash' 'hook' 2> /dev/null)" diff --git a/config-generator/config-maps/init-stac.sh b/config-generator/config-maps/init-stac.sh new file mode 100644 index 0000000..f61d57a --- /dev/null +++ b/config-generator/config-maps/init-stac.sh @@ -0,0 +1,31 @@ +set -x + +cd /workspace + +git clone 'https://github.com/eoap/stac-eoap.git' + +code-server --install-extension ms-python.python +code-server --install-extension redhat.vscode-yaml +code-server --install-extension sbg-rabix.benten-cwl +code-server --install-extension ms-toolsai.jupyter + +ln -s /workspace/.local/share/code-server/extensions /workspace/extensions + +mkdir -p /workspace/User/ + +echo '{"workbench.colorTheme": "Visual Studio Dark"}' > /workspace/User/settings.json + +python -m venv /workspace/.venv +source /workspace/.venv/bin/activate +/workspace/.venv/bin/python -m pip install --no-cache-dir stactools rasterio requests stac-asset click-logging tabulate tqdm pystac-client ipykernel loguru scikit-image rio_stac boto3==1.35.23 + +/workspace/.venv/bin/python -m pip install --index-url https://test.pypi.org/simple cwl-wrapper + +/workspace/.venv/bin/python -m ipykernel install --user --name stac_env --display-name "Python (STAC)" + +export AWS_DEFAULT_REGION="us-east-1" +export AWS_ACCESS_KEY_ID="test" +export AWS_SECRET_ACCESS_KEY="test" +aws s3 mb s3://results --endpoint-url=http://localstack:4566 + +exit 0 \ No newline at end of file diff --git a/config-generator/manifests/manifest.yaml b/config-generator/manifests/manifest.yaml new file mode 100644 index 0000000..d570078 --- /dev/null +++ b/config-generator/manifests/manifest.yaml @@ -0,0 +1,421 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: localstack +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: localstack +rules: +- apiGroups: [""] # "" indicates the core API group + resources: ["pods"] + verbs: ["*"] +- apiGroups: [""] + resources: ["pods/log"] + verbs: ["get"] +- apiGroups: [""] + resources: ["pods/exec"] + verbs: ["get", "create"] +- apiGroups: [""] + resources: ["services"] + verbs: ["get", "list"] +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: localstack +subjects: +- kind: ServiceAccount + name: localstack +roleRef: + kind: Role + name: localstack + apiGroup: rbac.authorization.k8s.io +--- +apiVersion: v1 +kind: Service +metadata: + name: localstack +spec: + type: ClusterIP + ports: + - name: edge + port: 4566 + targetPort: 4566 + - name: "external-service-port-4510" + port: 4510 + targetPort: "ext-svc-4510" + - name: "external-service-port-4511" + port: 4511 + targetPort: "ext-svc-4511" + - name: "external-service-port-4512" + port: 4512 + targetPort: "ext-svc-4512" + - name: "external-service-port-4513" + port: 4513 + targetPort: "ext-svc-4513" + - name: "external-service-port-4514" + port: 4514 + targetPort: "ext-svc-4514" + - name: "external-service-port-4515" + port: 4515 + targetPort: "ext-svc-4515" + - name: "external-service-port-4516" + port: 4516 + targetPort: "ext-svc-4516" + - name: "external-service-port-4517" + port: 4517 + targetPort: "ext-svc-4517" + - name: "external-service-port-4518" + port: 4518 + targetPort: "ext-svc-4518" + - name: "external-service-port-4519" + port: 4519 + targetPort: "ext-svc-4519" + - name: "external-service-port-4520" + port: 4520 + targetPort: "ext-svc-4520" + - name: "external-service-port-4521" + port: 4521 + targetPort: "ext-svc-4521" + - name: "external-service-port-4522" + port: 4522 + targetPort: "ext-svc-4522" + - name: "external-service-port-4523" + port: 4523 + targetPort: "ext-svc-4523" + - name: "external-service-port-4524" + port: 4524 + targetPort: "ext-svc-4524" + - name: "external-service-port-4525" + port: 4525 + targetPort: "ext-svc-4525" + - name: "external-service-port-4526" + port: 4526 + targetPort: "ext-svc-4526" + - name: "external-service-port-4527" + port: 4527 + targetPort: "ext-svc-4527" + - name: "external-service-port-4528" + port: 4528 + targetPort: "ext-svc-4528" + - name: "external-service-port-4529" + port: 4529 + targetPort: "ext-svc-4529" + - name: "external-service-port-4530" + port: 4530 + targetPort: "ext-svc-4530" + - name: "external-service-port-4531" + port: 4531 + targetPort: "ext-svc-4531" + - name: "external-service-port-4532" + port: 4532 + targetPort: "ext-svc-4532" + - name: "external-service-port-4533" + port: 4533 + targetPort: "ext-svc-4533" + - name: "external-service-port-4534" + port: 4534 + targetPort: "ext-svc-4534" + - name: "external-service-port-4535" + port: 4535 + targetPort: "ext-svc-4535" + - name: "external-service-port-4536" + port: 4536 + targetPort: "ext-svc-4536" + - name: "external-service-port-4537" + port: 4537 + targetPort: "ext-svc-4537" + - name: "external-service-port-4538" + port: 4538 + targetPort: "ext-svc-4538" + - name: "external-service-port-4539" + port: 4539 + targetPort: "ext-svc-4539" + - name: "external-service-port-4540" + port: 4540 + targetPort: "ext-svc-4540" + - name: "external-service-port-4541" + port: 4541 + targetPort: "ext-svc-4541" + - name: "external-service-port-4542" + port: 4542 + targetPort: "ext-svc-4542" + - name: "external-service-port-4543" + port: 4543 + targetPort: "ext-svc-4543" + - name: "external-service-port-4544" + port: 4544 + targetPort: "ext-svc-4544" + - name: "external-service-port-4545" + port: 4545 + targetPort: "ext-svc-4545" + - name: "external-service-port-4546" + port: 4546 + targetPort: "ext-svc-4546" + - name: "external-service-port-4547" + port: 4547 + targetPort: "ext-svc-4547" + - name: "external-service-port-4548" + port: 4548 + targetPort: "ext-svc-4548" + - name: "external-service-port-4549" + port: 4549 + targetPort: "ext-svc-4549" + - name: "external-service-port-4550" + port: 4550 + targetPort: "ext-svc-4550" + - name: "external-service-port-4551" + port: 4551 + targetPort: "ext-svc-4551" + - name: "external-service-port-4552" + port: 4552 + targetPort: "ext-svc-4552" + - name: "external-service-port-4553" + port: 4553 + targetPort: "ext-svc-4553" + - name: "external-service-port-4554" + port: 4554 + targetPort: "ext-svc-4554" + - name: "external-service-port-4555" + port: 4555 + targetPort: "ext-svc-4555" + - name: "external-service-port-4556" + port: 4556 + targetPort: "ext-svc-4556" + - name: "external-service-port-4557" + port: 4557 + targetPort: "ext-svc-4557" + - name: "external-service-port-4558" + port: 4558 + targetPort: "ext-svc-4558" + - name: "external-service-port-4559" + port: 4559 + targetPort: "ext-svc-4559" + selector: + app.kubernetes.io/name: localstack + app.kubernetes.io/instance: localstack +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: localstack +spec: + replicas: 1 + strategy: + type: RollingUpdate + selector: + matchLabels: + app.kubernetes.io/name: localstack + app.kubernetes.io/instance: localstack + template: + metadata: + labels: + app.kubernetes.io/name: localstack + app.kubernetes.io/instance: localstack + spec: + serviceAccountName: localstack + securityContext: + {} + containers: + - name: localstack + securityContext: + {} + image: "localstack/localstack:latest" + imagePullPolicy: IfNotPresent + ports: + - name: edge + containerPort: 4566 + protocol: TCP + - name: "ext-svc-4510" + containerPort: 4510 + protocol: TCP + - name: "ext-svc-4511" + containerPort: 4511 + protocol: TCP + - name: "ext-svc-4512" + containerPort: 4512 + protocol: TCP + - name: "ext-svc-4513" + containerPort: 4513 + protocol: TCP + - name: "ext-svc-4514" + containerPort: 4514 + protocol: TCP + - name: "ext-svc-4515" + containerPort: 4515 + protocol: TCP + - name: "ext-svc-4516" + containerPort: 4516 + protocol: TCP + - name: "ext-svc-4517" + containerPort: 4517 + protocol: TCP + - name: "ext-svc-4518" + containerPort: 4518 + protocol: TCP + - name: "ext-svc-4519" + containerPort: 4519 + protocol: TCP + - name: "ext-svc-4520" + containerPort: 4520 + protocol: TCP + - name: "ext-svc-4521" + containerPort: 4521 + protocol: TCP + - name: "ext-svc-4522" + containerPort: 4522 + protocol: TCP + - name: "ext-svc-4523" + containerPort: 4523 + protocol: TCP + - name: "ext-svc-4524" + containerPort: 4524 + protocol: TCP + - name: "ext-svc-4525" + containerPort: 4525 + protocol: TCP + - name: "ext-svc-4526" + containerPort: 4526 + protocol: TCP + - name: "ext-svc-4527" + containerPort: 4527 + protocol: TCP + - name: "ext-svc-4528" + containerPort: 4528 + protocol: TCP + - name: "ext-svc-4529" + containerPort: 4529 + protocol: TCP + - name: "ext-svc-4530" + containerPort: 4530 + protocol: TCP + - name: "ext-svc-4531" + containerPort: 4531 + protocol: TCP + - name: "ext-svc-4532" + containerPort: 4532 + protocol: TCP + - name: "ext-svc-4533" + containerPort: 4533 + protocol: TCP + - name: "ext-svc-4534" + containerPort: 4534 + protocol: TCP + - name: "ext-svc-4535" + containerPort: 4535 + protocol: TCP + - name: "ext-svc-4536" + containerPort: 4536 + protocol: TCP + - name: "ext-svc-4537" + containerPort: 4537 + protocol: TCP + - name: "ext-svc-4538" + containerPort: 4538 + protocol: TCP + - name: "ext-svc-4539" + containerPort: 4539 + protocol: TCP + - name: "ext-svc-4540" + containerPort: 4540 + protocol: TCP + - name: "ext-svc-4541" + containerPort: 4541 + protocol: TCP + - name: "ext-svc-4542" + containerPort: 4542 + protocol: TCP + - name: "ext-svc-4543" + containerPort: 4543 + protocol: TCP + - name: "ext-svc-4544" + containerPort: 4544 + protocol: TCP + - name: "ext-svc-4545" + containerPort: 4545 + protocol: TCP + - name: "ext-svc-4546" + containerPort: 4546 + protocol: TCP + - name: "ext-svc-4547" + containerPort: 4547 + protocol: TCP + - name: "ext-svc-4548" + containerPort: 4548 + protocol: TCP + - name: "ext-svc-4549" + containerPort: 4549 + protocol: TCP + - name: "ext-svc-4550" + containerPort: 4550 + protocol: TCP + - name: "ext-svc-4551" + containerPort: 4551 + protocol: TCP + - name: "ext-svc-4552" + containerPort: 4552 + protocol: TCP + - name: "ext-svc-4553" + containerPort: 4553 + protocol: TCP + - name: "ext-svc-4554" + containerPort: 4554 + protocol: TCP + - name: "ext-svc-4555" + containerPort: 4555 + protocol: TCP + - name: "ext-svc-4556" + containerPort: 4556 + protocol: TCP + - name: "ext-svc-4557" + containerPort: 4557 + protocol: TCP + - name: "ext-svc-4558" + containerPort: 4558 + protocol: TCP + - name: "ext-svc-4559" + containerPort: 4559 + protocol: TCP + livenessProbe: + failureThreshold: 3 + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + httpGet: + path: /_localstack/health + port: edge + readinessProbe: + failureThreshold: 3 + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + httpGet: + path: /_localstack/health + port: edge + resources: {} + env: + - name: DEBUG + value: "0" + - name: EXTERNAL_SERVICE_PORTS_START + value: "4510" + - name: EXTERNAL_SERVICE_PORTS_END + value: "4560" + - name: LOCALSTACK_K8S_SERVICE_NAME + value: localstack + - name: LOCALSTACK_K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: LAMBDA_RUNTIME_EXECUTOR + value: "docker" + - name: LAMBDA_K8S_IMAGE_PREFIX + value: "localstack/lambda-" + - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT + value: "60" + - name: OVERRIDE_IN_DOCKER + value: "1" + volumes: [] \ No newline at end of file diff --git a/config-generator/models.py b/config-generator/models.py index aee37fa..02eabdc 100644 --- a/config-generator/models.py +++ b/config-generator/models.py @@ -74,7 +74,7 @@ class Volume(BaseModel): class Manifest(BaseModel): name: str key: str - content: Optional[str] = None + content: Optional[List[Dict]] = None persist: Optional[bool] = True diff --git a/config-generator/requirements.txt b/config-generator/requirements.txt new file mode 100644 index 0000000..3c47a85 --- /dev/null +++ b/config-generator/requirements.txt @@ -0,0 +1,3 @@ +ipykernel +pydantic +pyyaml \ No newline at end of file diff --git a/jupyterhub/values-minikube.yaml b/demo-values.yaml similarity index 97% rename from jupyterhub/values-minikube.yaml rename to demo-values.yaml index e1c90aa..f3a60c1 100644 --- a/jupyterhub/values-minikube.yaml +++ b/demo-values.yaml @@ -1,3 +1,4 @@ + # fullnameOverride and nameOverride distinguishes blank strings, null values, # and non-blank strings. For more details, see the configuration reference. fullnameOverride: "" @@ -79,7 +80,20 @@ hub: } extraContainers: [] extraVolumes: [] + # - name: application-hub-config + # configMap: + # name: application-hub-jupyter-config + # defaultMode: 0744 + # - name: application-hub-config-theme + # configMap: + # name: application-hub-jupyter-config-theme + # defaultMode: 0744 extraVolumeMounts: [] + # - name: application-hub-config + # mountPath: /usr/local/etc/applicationhub + # - name: application-hub-config-theme + # mountPath: /opt/jupyterhub/template/ + image: name: hubimage tag: "dev" diff --git a/files/hub/config.yml b/files/hub/config.yml new file mode 100644 index 0000000..70a6dbe --- /dev/null +++ b/files/hub/config.yml @@ -0,0 +1,303 @@ +profiles: +- config_maps: + - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors\ + \ /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram\ + \ 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ + \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\ + # >>> conda initialize >>>\n# !! Contents within this block are managed by 'conda\ + \ init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' 2>\ + \ /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n \ + \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ + \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset\ + \ __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ + \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n" + default_mode: null + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: true + readonly: true + default_url: null + definition: + default: false + description: null + display_name: Code Server Small + kubespawner_override: + cpu_guarantee: null + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: eoepca/pde-code-server:develop + mem_guarantee: null + mem_limit: 8G + slug: ellip_studio_coder_slug_s + groups: + - group-a + - group-b + id: profile_studio_coder1 + image_pull_secrets: [] + init_containers: [] + manifests: null + node_selector: {} + pod_env_vars: + CONDA_ENVS_PATH: ' /workspace/.envs' + HOME: /workspace + role_bindings: null + volumes: + - access_modes: + - ReadWriteMany + claim_name: calrissian-claim + name: calrissian-volume + persist: false + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /calrissian + name: calrissian-volume + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: true + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: + - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors\ + \ /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram\ + \ 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ + \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\ + # >>> conda initialize >>>\n# !! Contents within this block are managed by 'conda\ + \ init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' 2>\ + \ /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n \ + \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ + \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset\ + \ __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ + \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n" + default_mode: null + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: true + readonly: true + default_url: null + definition: + default: false + description: null + display_name: Code Server Medium + kubespawner_override: + cpu_guarantee: null + cpu_limit: 4 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: eoepca/pde-code-server:develop + mem_guarantee: null + mem_limit: 12G + slug: ellip_studio_coder_slug_m + groups: + - group-a + - group-b + id: profile_studio_coder2 + image_pull_secrets: [] + init_containers: [] + manifests: null + node_selector: {} + pod_env_vars: + CONDA_ENVS_PATH: ' /workspace/.envs' + HOME: /workspace + role_bindings: null + volumes: + - access_modes: + - ReadWriteMany + claim_name: calrissian-claim + name: calrissian-volume + persist: false + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /calrissian + name: calrissian-volume + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: true + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: + - content: 'set -x + + + cd /workspace + + + git clone ''https://github.com/eoap/mastering-app-package.git'' + + + code-server --install-extension ms-python.python + + code-server --install-extension redhat.vscode-yaml + + code-server --install-extension sbg-rabix.benten-cwl + + code-server --install-extension ms-toolsai.jupyter + + + ln -s /workspace/.local/share/code-server/extensions /workspace/extensions + + + exit 0 + + ' + default_mode: '0660' + key: init + mount_path: /opt/init/.init.sh + name: init + persist: false + readonly: true + default_url: null + definition: + default: false + description: This profile is used to demonstrate the use of an init script + display_name: Coder demo init script + kubespawner_override: + cpu_guarantee: 1 + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: eoepca/pde-code-server:develop + mem_guarantee: 4G + mem_limit: 6G + slug: eoepca_demo_init_script + groups: + - group-a + - group-b + id: profile_demo_init_script + image_pull_secrets: [] + init_containers: + - command: + - sh + - -c + - sh /opt/init/.init.sh + image: eoepca/pde-code-server:develop + name: init-file-on-volume + volume_mounts: + - mount_path: /workspace + name: workspace-volume + - mount_path: /opt/init/.init.sh + name: init + sub_path: init + manifests: null + node_selector: {} + pod_env_vars: + CODE_SERVER_WS: /workspace/mastering-app-package + CONDARC: /workspace/.condarc + CONDA_ENVS_PATH: /workspace/.envs + HOME: /workspace + XDG_RUNTIME_DIR: /workspace/.local + role_bindings: null + volumes: + - access_modes: + - ReadWriteMany + claim_name: calrissian-claim + name: calrissian-volume + persist: false + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /calrissian + name: calrissian-volume + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: true + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: [] + default_url: null + definition: + default: false + description: Jupyter Lab with Python 3.11 + display_name: Jupyter Lab + kubespawner_override: + cpu_guarantee: 1 + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: jupyter/scipy-notebook + mem_guarantee: 4G + mem_limit: 6G + slug: eoepca_jupyter_lab + groups: + - group-c + id: profile_jupyter_lab + image_pull_secrets: [] + init_containers: [] + manifests: null + node_selector: {} + pod_env_vars: + HOME: /workspace + XDG_CONFIG_HOME: /workspace/.config + XDG_RUNTIME_DIR: /workspace/.local + role_bindings: null + volumes: + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: true + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: [] + default_url: null + definition: + default: false + description: Jupyter Lab with Python 3.11 - demoes the use of an image pull secret + display_name: Jupyter Lab - profile 2 + kubespawner_override: + cpu_guarantee: 1 + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: jupyter/scipy-notebook + mem_guarantee: 4G + mem_limit: 6G + slug: eoepca_jupyter_lab_2 + groups: + - group-c + id: profile_jupyter_lab_2 + image_pull_secrets: + - data: ewogICAgImF1dGhzIjogewogICAgICAgICJjci50ZXJyYWR1ZS5jb20iOiB7CiAgICAgICAgICAgICJ1c2VybmFtZSI6ICJyb2JvdCRlb2VwY2EtcGx1cy1ybyIsCiAgICAgICAgICAgICJwYXNzd29yZCI6ICJQMlE4TnkyZ0lHODhkZkxveXlLN05QVUZVbHJOekFZSiIsCiAgICAgICAgICAgICJlbWFpbCI6ICJlb2VwY2EtcGx1c0B0ZXJyYWR1ZS5jb20iLAogICAgICAgICAgICAiYXV0aCI6ICJjbTlpYjNRa1pXOWxjR05oTFhCc2RYTXRjbTg2VURKUk9FNTVNbWRKUnpnNFpHWk1iM2w1U3pkT1VGVkdWV3h5VG5wQldVbz0iCiAgICAgICAgfQogICAgfQp9 + name: cr-config + persist: false + init_containers: [] + manifests: null + node_selector: {} + pod_env_vars: + HOME: /workspace + XDG_CONFIG_HOME: /workspace/.config + XDG_RUNTIME_DIR: /workspace/.local + role_bindings: null + volumes: + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: true + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /workspace + name: workspace-volume diff --git a/files/hub/jupyterhub_config.py b/files/hub/jupyterhub_config.py new file mode 100644 index 0000000..56395ea --- /dev/null +++ b/files/hub/jupyterhub_config.py @@ -0,0 +1,179 @@ +import os +import sys + +from tornado.httpclient import AsyncHTTPClient + + +from application_hub_context.app_hub_context import DefaultApplicationHubContext + + +configuration_directory = os.path.dirname(os.path.realpath(__file__)) +sys.path.insert(0, configuration_directory) + +from z2jh import ( + get_config, + get_name, + get_name_env, +) + +config_path = "/usr/local/etc/applicationhub/config.yml" + +namespace_prefix = "jupyter" + + +def custom_options_form(spawner): + + spawner.log.info("Configure profile list") + + namespace = f"{namespace_prefix}-{spawner.user.name}" + + workspace = DefaultApplicationHubContext( + namespace=namespace, + spawner=spawner, + config_path=config_path, + ) + + spawner.profile_list = workspace.get_profile_list() + + return spawner._options_form_default() + + +def pre_spawn_hook(spawner): + + profile_slug = spawner.user_options.get("profile", None) + + env = os.environ["JUPYTERHUB_ENV"].lower() + + spawner.environment["CALRISSIAN_POD_NAME"] = f"jupyter-{spawner.user.name}-{env}" + + spawner.log.info(f"Using profile slug {profile_slug}") + + namespace = f"{namespace_prefix}-{spawner.user.name}" + + workspace = DefaultApplicationHubContext( + namespace=namespace, spawner=spawner, config_path=config_path + ) + + workspace.initialise() + + profile_id = workspace.config_parser.get_profile_by_slug(slug=profile_slug).id + + default_url = workspace.config_parser.get_profile_default_url(profile_id=profile_id) + + if default_url: + spawner.log.info(f"Setting default url to {default_url}") + spawner.default_url = default_url + + +def post_stop_hook(spawner): + + namespace = f"jupyter-{spawner.user.name}" + + workspace = DefaultApplicationHubContext( + namespace=namespace, spawner=spawner, config_path=config_path + ) + spawner.log.info("Dispose in post stop hook") + workspace.dispose() + + +c.JupyterHub.default_url = "spawn" + + +# Configure JupyterHub to use the curl backend for making HTTP requests, +# rather than the pure-python implementations. The default one starts +# being too slow to make a large number of requests to the proxy API +# at the rate required. +AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient") + +c.ConfigurableHTTPProxy.api_url = ( + f'http://{get_name("proxy-api")}:{get_name_env("proxy-api", "_SERVICE_PORT")}' +) +# c.ConfigurableHTTPProxy.should_start = False + +# Don't wait at all before redirecting a spawning user to the progress page +c.JupyterHub.tornado_settings = { + "slow_spawn_timeout": 0, +} + +jupyterhub_env = os.environ["JUPYTERHUB_ENV"].upper() +jupyterhub_hub_host = "hub.jupyter" +jupyterhub_single_user_image = os.environ["JUPYTERHUB_SINGLE_USER_IMAGE_NOTEBOOKS"] + +# Authentication +c.LocalAuthenticator.create_system_users = True +c.Authenticator.admin_users = {"jovyan"} +# Deprecated +c.Authenticator.allowed_users = {"jovyan"} +c.JupyterHub.authenticator_class = "dummy" + +# HTTP Proxy auth token +c.ConfigurableHTTPProxy.auth_token = get_config("proxy.secretToken") +c.JupyterHub.cookie_secret_file = "/srv/jupyterhub/cookie_secret" +# Proxy config +c.JupyterHub.cleanup_servers = False +# Network +c.JupyterHub.allow_named_servers = True +c.JupyterHub.ip = "0.0.0.0" +c.JupyterHub.hub_ip = "0.0.0.0" +c.JupyterHub.hub_connect_ip = jupyterhub_hub_host +# Misc +c.JupyterHub.cleanup_servers = False + +# Culling +c.JupyterHub.services = [ + { + "name": "idle-culler", + "admin": True, + "command": [sys.executable, "-m", "jupyterhub_idle_culler", "--timeout=3600"], + } +] + +# Logs +c.JupyterHub.log_level = "DEBUG" + +# Spawner +c.JupyterHub.spawner_class = "kubespawner.KubeSpawner" +c.KubeSpawner.environment = { + "JUPYTER_ENABLE_LAB": "true", +} + +c.KubeSpawner.uid = 1001 +c.KubeSpawner.fs_gid = 100 +c.KubeSpawner.hub_connect_ip = jupyterhub_hub_host + +# SecurityContext +c.KubeSpawner.privileged = True +c.KubeSpawner.allow_privilege_escalation = True + +# ServiceAccount +c.KubeSpawner.service_account = "default" +c.KubeSpawner.start_timeout = 60 * 15 +c.KubeSpawner.image = jupyterhub_single_user_image +c.KubernetesSpawner.verify_ssl = True +c.KubeSpawner.pod_name_template = ( + "jupyter-{username}-{servername}-" + os.environ["JUPYTERHUB_ENV"].lower() +) + +# Namespace +c.KubeSpawner.namespace = "jupyter" + +# User namespace +c.KubeSpawner.enable_user_namespaces = True + +# Volumes +# volumes are managed by the pre_spawn_hook/post_stop_hook + +# TODO - move this value to the values.yaml file +c.KubeSpawner.image_pull_secrets = ["cr-config"] + +# custom options form +c.KubeSpawner.options_form = custom_options_form + +# hooks +c.KubeSpawner.pre_spawn_hook = pre_spawn_hook +c.KubeSpawner.post_stop_hook = post_stop_hook + +c.JupyterHub.template_paths = [ + "/opt/jupyterhub/template", + "/usr/local/share/jupyterhub/templates", +] diff --git a/files/hub/z2jh.py b/files/hub/z2jh.py new file mode 100644 index 0000000..57e463f --- /dev/null +++ b/files/hub/z2jh.py @@ -0,0 +1,121 @@ +""" +Utility methods for use in jupyterhub_config.py and dynamic subconfigs. + +Methods here can be imported by extraConfig in values.yaml +""" +# from collections import Mapping +from collections.abc import Mapping +from functools import lru_cache +import os + +import yaml + +# memoize so we only load config once +@lru_cache() +def _load_config(): + """Load the Helm chart configuration used to render the Helm templates of + the chart from a mounted k8s Secret, and merge in values from an optionally + mounted secret (hub.existingSecret).""" + + cfg = {} + for source in ("secret/values.yaml", "existing-secret/values.yaml"): + path = f"/usr/local/etc/jupyterhub/{source}" + if os.path.exists(path): + print(f"Loading {path}") + with open(path) as f: + values = yaml.safe_load(f) + cfg = _merge_dictionaries(cfg, values) + else: + print(f"No config at {path}") + return cfg + + +@lru_cache() +def _get_config_value(key): + """Load value from the k8s ConfigMap given a key.""" + + path = f"/usr/local/etc/jupyterhub/config/{key}" + if os.path.exists(path): + with open(path) as f: + return f.read() + else: + raise Exception(f"{path} not found!") + + +@lru_cache() +def get_secret_value(key, default="never-explicitly-set"): + """Load value from the user managed k8s Secret or the default k8s Secret + given a key.""" + + for source in ("existing-secret", "secret"): + path = f"/usr/local/etc/jupyterhub/{source}/{key}" + if os.path.exists(path): + with open(path) as f: + return f.read() + if default != "never-explicitly-set": + return default + raise Exception(f"{key} not found in either k8s Secret!") + + +def get_name(name): + """Returns the fullname of a resource given its short name""" + return _get_config_value(name) + + +def get_name_env(name, suffix=""): + """Returns the fullname of a resource given its short name along with a + suffix, converted to uppercase with dashes replaced with underscores. This + is useful to reference named services associated environment variables, such + as PROXY_PUBLIC_SERVICE_PORT.""" + env_key = _get_config_value(name) + suffix + env_key = env_key.upper().replace("-", "_") + return os.environ[env_key] + + +def _merge_dictionaries(a, b): + """Merge two dictionaries recursively. + + Simplified From https://stackoverflow.com/a/7205107 + """ + merged = a.copy() + for key in b: + if key in a: + if isinstance(a[key], Mapping) and isinstance(b[key], Mapping): + merged[key] = _merge_dictionaries(a[key], b[key]) + else: + merged[key] = b[key] + else: + merged[key] = b[key] + return merged + + +def get_config(key, default=None): + """ + Find a config item of a given name & return it + + Parses everything as YAML, so lists and dicts are available too + + get_config("a.b.c") returns config['a']['b']['c'] + """ + value = _load_config() + # resolve path in yaml + for level in key.split("."): + if not isinstance(value, dict): + # a parent is a scalar or null, + # can't resolve full path + return default + if level not in value: + return default + else: + value = value[level] + return value + + +def set_config_if_not_none(cparent, name, key): + """ + Find a config item of a given name, set the corresponding Jupyter + configuration item if not None + """ + data = get_config(key) + if data is not None: + setattr(cparent, name, data) diff --git a/files/theme/page.html b/files/theme/page.html new file mode 100644 index 0000000..5bec635 --- /dev/null +++ b/files/theme/page.html @@ -0,0 +1,5 @@ +{% extends "templates/page.html" %} + +{% set announcement = 'EOEPCA+ ApplicationHub demonstration instance (super cool)' %} + +{% block title %}ApplicationHub{% endblock %} diff --git a/files/theme/spawn.html b/files/theme/spawn.html new file mode 100644 index 0000000..8bb7896 --- /dev/null +++ b/files/theme/spawn.html @@ -0,0 +1,7 @@ +{% extends "templates/spawn.html" %} + +{% block heading %} +
+

Available EOEPCA+ demonstration applications

+
+{% endblock %} diff --git a/files/theme/spawn_pending.html b/files/theme/spawn_pending.html new file mode 100644 index 0000000..3cc1dfb --- /dev/null +++ b/files/theme/spawn_pending.html @@ -0,0 +1,94 @@ +{% extends "page.html" %} + +{% block main %} + +
+
+
+ {% block message %} +

Your EOEPCA application is starting up.

+

You will be redirected automatically when it's ready for you.

+ {% endblock %} +
+
+ 0% Complete +
+
+

+
+
+
+
+
+ Event log +
+
+
+
+
+ +{% endblock %} + +{% block script %} +{{ `{{ super() }} `}} + +{% endblock %} diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index 5e6fb9e..b97ec51 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -18,8 +18,15 @@ config_path = "/usr/local/etc/jupyterhub/config.yml" -namespace_prefix = "jupyter" +def get_namespace_prefix(): + env = os.environ["JUPYTERHUB_ENV"].lower() # Retrieve the JUPYTERHUB_ENV environment variable + return f"jupyter-{env}" # Dynamically generate the namespace prefix +def get_jupyterhub_hub_host(): + env = os.environ["JUPYTERHUB_HUB_HOST"].lower() + return f"hub.{env}" + +namespace_prefix = get_namespace_prefix() # Create dynamic namespace prefix def custom_options_form(spawner): @@ -44,7 +51,7 @@ def pre_spawn_hook(spawner): env = os.environ["JUPYTERHUB_ENV"].lower() - spawner.environment["CALRISSIAN_POD_NAME"] = f"jupyter-{spawner.user.name}-{env}" + spawner.environment["CALRISSIAN_POD_NAME"] = f"jupyter-{env}-{spawner.user.name}" spawner.log.info(f"Using profile slug {profile_slug}") @@ -67,7 +74,7 @@ def pre_spawn_hook(spawner): def post_stop_hook(spawner): - namespace = f"jupyter-{spawner.user.name}" + namespace = f"{namespace_prefix}-{spawner.user.name}" workspace = DefaultApplicationHubContext( namespace=namespace, spawner=spawner, config_path=config_path @@ -96,7 +103,8 @@ def post_stop_hook(spawner): } jupyterhub_env = os.environ["JUPYTERHUB_ENV"].upper() -jupyterhub_hub_host = "hub.jupyter" +jupyterhub_hub_host = get_jupyterhub_hub_host() + jupyterhub_single_user_image = os.environ["JUPYTERHUB_SINGLE_USER_IMAGE_NOTEBOOKS"] # Authentication @@ -151,11 +159,12 @@ def post_stop_hook(spawner): c.KubeSpawner.image = jupyterhub_single_user_image c.KubernetesSpawner.verify_ssl = True c.KubeSpawner.pod_name_template = ( - "jupyter-{username}-{servername}-" + os.environ["JUPYTERHUB_ENV"].lower() + "jupyter-" + os.environ["JUPYTERHUB_ENV"].lower() + "-{username}" ) # Namespace -c.KubeSpawner.namespace = "jupyter" +# Kubernetes namespace to spawn user pods in. +c.KubeSpawner.user_namespace_template = get_namespace_prefix() + "-{username}" # User namespace c.KubeSpawner.enable_user_namespaces = True diff --git a/jupyterhub/templates/hub/configmap-theme.yaml b/jupyterhub/templates/hub/configmap-theme.yaml index 8d744b7..cfbae26 100644 --- a/jupyterhub/templates/hub/configmap-theme.yaml +++ b/jupyterhub/templates/hub/configmap-theme.yaml @@ -13,7 +13,10 @@ data: z2jh.py: | multi line string content... */}} - {{- (.Files.Glob "files/theme/*").AsConfig | nindent 2 }} + # templates for the hub + page.html: {{ required "A valid .Values.pageTheme entry required!" (tpl (.Values.pageTheme | default (.Files.Get "files/theme/page.html")) . | quote ) }} + spawn_pending.html: {{ required "A valid .Values.spawnPendingTheme entry required!" (tpl (.Values.spawnPendingTheme | default (.Files.Get "files/theme/spawn_pending.html")) . | quote ) }} + spawn.html: {{ required "A valid .Values.spawnTheme entry required!" (tpl (.Values.spawnTheme | default (.Files.Get "files/theme/spawn.html")) . | quote) }} {{- /* Store away a checksum of the hook-image-puller daemonset so future upgrades diff --git a/jupyterhub/templates/hub/configmap.yaml b/jupyterhub/templates/hub/configmap.yaml index 128a6a0..08debe6 100644 --- a/jupyterhub/templates/hub/configmap.yaml +++ b/jupyterhub/templates/hub/configmap.yaml @@ -21,7 +21,11 @@ data: z2jh.py: | multi line string content... */}} - {{- (.Files.Glob "files/hub/*").AsConfig | nindent 2 }} + config.yml: {{ required "A valid .Values.appHubConfig entry required!" (tpl (.Values.appHubConfig | default (.Files.Get "files/hub/config.yml")) . | quote) }} + jupyterhub_config.py: {{ required "A valid .Values.jupHubConfig entry required!" (tpl (.Values.jupHubConfig | default (.Files.Get "files/hub/jupyterhub_config.py")) . | quote) }} + z2jh.py: {{ required "A valid .Values.z2jhConfig entry required!" (tpl (.Values.z2jhConfig | default (.Files.Get "files/hub/z2jh.py")) . | quote) }} + + {{- /* Store away a checksum of the hook-image-puller daemonset so future upgrades diff --git a/jupyterhub/values.schema.json b/jupyterhub/values.schema.json deleted file mode 100644 index cc589df..0000000 --- a/jupyterhub/values.schema.json +++ /dev/null @@ -1 +0,0 @@ -{"$schema": "http://json-schema.org/draft-07/schema#", "type": "object", "additionalProperties": false, "required": ["imagePullSecrets", "hub", "proxy", "singleuser", "ingress", "prePuller", "custom", "cull", "debug", "rbac", "global"], "properties": {"fullnameOverride": {"type": ["string", "null"]}, "nameOverride": {"type": ["string", "null"]}, "imagePullSecret": {"type": "object", "required": ["create"], "if": {"properties": {"create": {"const": true}}}, "then": {"additionalProperties": false, "required": ["registry", "username", "password"], "description": "This is configuration to create a k8s Secret resource of `type:\nkubernetes.io/dockerconfigjson`, with credentials to pull images from a\nprivate image registry. If you opt to do so, it will be available for use\nby all pods in their respective `spec.imagePullSecrets` alongside other\nk8s Secrets defined in `imagePullSecrets` or the pod respective\n`...image.pullSecrets` configuration.\n\nIn other words, using this configuration option can automate both the\notherwise manual creation of a k8s Secret and the otherwise manual\nconfiguration to reference this k8s Secret in all the pods of the Helm\nchart.\n\n```sh\n# you won't need to create a k8s Secret manually...\nkubectl create secret docker-registry image-pull-secret \\\n --docker-server= \\\n --docker-username= \\\n --docker-email= \\\n --docker-password=\n```\n\nIf you just want to let all Pods reference an existing secret, use the\n[`imagePullSecrets`](schema_imagePullSecrets) configuration instead.\n", "properties": {"create": {"type": "boolean", "description": "Toggle the creation of the k8s Secret with provided credentials to\naccess a private image registry.\n"}, "automaticReferenceInjection": {"type": "boolean", "description": "Toggle the automatic reference injection of the created Secret to all\npods' `spec.imagePullSecrets` configuration.\n"}, "registry": {"type": "string", "description": "Name of the private registry you want to create a credential set for.\nIt will default to Docker Hub's image registry.\n\nExamples:\n - https://index.docker.io/v1/\n - quay.io\n - eu.gcr.io\n - alexmorreale.privatereg.net\n"}, "username": {"type": "string", "description": "Name of the user you want to use to connect to your private registry.\n\nFor external gcr.io, you will use the `_json_key`.\n\nExamples:\n - alexmorreale\n - alex@pfc.com\n - _json_key\n"}, "password": {"type": "string", "description": "Password for the private image registry's user.\n\nExamples:\n - plaintextpassword\n - abc123SECRETzyx098\n\nFor gcr.io registries the password will be a big JSON blob for a\nGoogle cloud service account, it should look something like below.\n\n```yaml\npassword: |-\n {\n \"type\": \"service_account\",\n \"project_id\": \"jupyter-se\",\n \"private_key_id\": \"f2ba09118a8d3123b3321bd9a7d6d0d9dc6fdb85\",\n ...\n }\n```\n"}, "email": {"type": ["string", "null"], "description": "Specification of an email is most often not required, but it is\nsupported.\n"}}}}, "imagePullSecrets": {"type": "array"}, "hub": {"type": "object", "additionalProperties": false, "required": ["baseUrl"], "properties": {"revisionHistoryLimit": {"type": ["integer", "null"], "minimum": 0}, "config": {"type": "object", "additionalProperties": true}, "extraFiles": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "object", "additionalProperties": false, "required": ["mountPath"], "oneOf": [{"required": ["data"]}, {"required": ["stringData"]}, {"required": ["binaryData"]}], "properties": {"mountPath": {"type": "string"}, "data": {"type": "object", "additionalProperties": true}, "stringData": {"type": "string"}, "binaryData": {"type": "string"}, "mode": {"type": "number"}}}}}, "baseUrl": {"type": "string"}, "command": {"type": "array"}, "args": {"type": "array"}, "cookieSecret": {"type": ["string", "null"]}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "networkPolicy": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "ingress": {"type": "array"}, "egress": {"type": "array"}, "egressAllowRules": {"type": "object", "additionalProperties": false, "properties": {"cloudMetadataServer": {"type": "boolean"}, "dnsPortsPrivateIPs": {"type": "boolean"}, "nonPrivateIPs": {"type": "boolean"}, "privateIPs": {"type": "boolean"}}}, "interNamespaceAccessLabels": {"enum": ["accept", "ignore"]}, "allowedIngressPorts": {"type": "array"}}}, "db": {"type": "object", "additionalProperties": false, "properties": {"type": {"enum": ["sqlite-pvc", "sqlite-memory", "mysql", "postgres", "other"]}, "pvc": {"type": "object", "additionalProperties": false, "required": ["storage"], "properties": {"annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "selector": {"type": "object", "additionalProperties": true}, "storage": {"type": "string"}, "accessModes": {"type": "array", "items": {"type": ["string", "null"]}}, "storageClassName": {"type": ["string", "null"]}, "subPath": {"type": ["string", "null"]}}}, "upgrade": {"type": ["boolean", "null"]}, "url": {"type": ["string", "null"]}, "password": {"type": ["string", "null"]}}}, "labels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "initContainers": {"type": "array"}, "extraEnv": {"type": ["object", "array"], "additionalProperties": true}, "extraConfig": {"type": "object", "additionalProperties": true}, "fsGid": {"type": ["integer", "null"], "minimum": 0}, "service": {"type": "object", "additionalProperties": false, "properties": {"type": {"enum": ["ClusterIP", "NodePort", "LoadBalancer", "ExternalName"]}, "ports": {"type": "object", "additionalProperties": false, "properties": {"nodePort": {"type": ["integer", "null"], "minimum": 0}}}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "extraPorts": {"type": "array"}, "loadBalancerIP": {"type": ["string", "null"]}}}, "pdb": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "maxUnavailable": {"type": ["integer", "null"]}, "minAvailable": {"type": ["integer", "null"]}}}, "existingSecret": {"type": ["string", "null"]}, "nodeSelector": {"type": "object", "additionalProperties": true}, "tolerations": {"type": "array"}, "activeServerLimit": {"type": ["integer", "null"]}, "allowNamedServers": {"type": ["boolean", "null"]}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "authenticatePrometheus": {"type": ["boolean", "null"]}, "concurrentSpawnLimit": {"type": ["integer", "null"]}, "consecutiveFailureLimit": {"type": ["integer", "null"]}, "podSecurityContext": {"additionalProperties": true}, "containerSecurityContext": {"type": "object", "additionalProperties": true}, "deploymentStrategy": {"type": "object", "additionalProperties": false, "properties": {"rollingUpdate": {"type": ["string", "null"]}, "type": {"type": ["string", "null"]}}}, "extraContainers": {"type": "array"}, "extraVolumeMounts": {"type": "array"}, "extraVolumes": {"type": "array"}, "livenessProbe": {"type": "object", "additionalProperties": true, "required": ["enabled"], "if": {"properties": {"enabled": {"const": true}}}, "then": {"description": "This config option is like the k8s native specification of a\ncontainer probe, except that it also supports an `enabled` boolean\nflag.\n\nSee [the k8s\ndocumentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#probe-v1-core)\nfor more details.\n"}}, "readinessProbe": {"type": "object", "additionalProperties": true, "required": ["enabled"], "if": {"properties": {"enabled": {"const": true}}}, "then": {"description": "This config option is like the k8s native specification of a\ncontainer probe, except that it also supports an `enabled` boolean\nflag.\n\nSee [the k8s\ndocumentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#probe-v1-core)\nfor more details.\n"}}, "namedServerLimitPerUser": {"type": ["integer", "null"]}, "redirectToServer": {"type": ["boolean", "null"]}, "resources": {"type": "object", "additionalProperties": true}, "lifecycle": {"type": "object", "additionalProperties": false, "properties": {"postStart": {"type": "object", "additionalProperties": true}, "preStop": {"type": "object", "additionalProperties": true}}}, "services": {"type": "object", "additionalProperties": true, "properties": {"name": {"type": "string"}, "admin": {"type": "boolean"}, "command": {"type": ["string", "array"]}, "url": {"type": "string"}, "api_token": {"type": ["string", "null"]}, "apiToken": {"type": ["string", "null"]}}}, "loadRoles": {"type": "object", "additionalProperties": true}, "shutdownOnLogout": {"type": ["boolean", "null"]}, "templatePaths": {"type": "array"}, "templateVars": {"type": "object", "additionalProperties": true}, "serviceAccount": {"type": "object", "required": ["create"], "additionalProperties": false, "properties": {"create": {"type": "boolean"}, "name": {"type": ["string", "null"]}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}}}, "extraPodSpec": {"type": "object", "additionalProperties": true}}}, "proxy": {"type": "object", "additionalProperties": false, "properties": {"chp": {"type": "object", "additionalProperties": false, "properties": {"revisionHistoryLimit": {"type": ["integer", "null"], "minimum": 0}, "networkPolicy": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "ingress": {"type": "array"}, "egress": {"type": "array"}, "egressAllowRules": {"type": "object", "additionalProperties": false, "properties": {"cloudMetadataServer": {"type": "boolean"}, "dnsPortsPrivateIPs": {"type": "boolean"}, "nonPrivateIPs": {"type": "boolean"}, "privateIPs": {"type": "boolean"}}}, "interNamespaceAccessLabels": {"enum": ["accept", "ignore"]}, "allowedIngressPorts": {"type": "array"}}}, "extraCommandLineFlags": {"type": "array"}, "extraEnv": {"type": ["object", "array"], "additionalProperties": true}, "pdb": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "maxUnavailable": {"type": ["integer", "null"]}, "minAvailable": {"type": ["integer", "null"]}}}, "nodeSelector": {"type": "object", "additionalProperties": true}, "tolerations": {"type": "array"}, "containerSecurityContext": {"type": "object", "additionalProperties": true}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "livenessProbe": {"type": "object", "additionalProperties": true, "required": ["enabled"], "if": {"properties": {"enabled": {"const": true}}}, "then": {"description": "This config option is like the k8s native specification of a\ncontainer probe, except that it also supports an `enabled` boolean\nflag.\n\nSee [the k8s\ndocumentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#probe-v1-core)\nfor more details.\n"}}, "readinessProbe": {"type": "object", "additionalProperties": true, "required": ["enabled"], "if": {"properties": {"enabled": {"const": true}}}, "then": {"description": "This config option is like the k8s native specification of a\ncontainer probe, except that it also supports an `enabled` boolean\nflag.\n\nSee [the k8s\ndocumentation](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#probe-v1-core)\nfor more details.\n"}}, "resources": {"type": "object", "additionalProperties": true}, "defaultTarget": {"type": ["string", "null"]}, "errorTarget": {"type": ["string", "null"]}, "extraPodSpec": {"type": "object", "additionalProperties": true}}}, "secretToken": {"type": ["string", "null"]}, "service": {"type": "object", "additionalProperties": false, "properties": {"type": {"enum": ["ClusterIP", "NodePort", "LoadBalancer", "ExternalName"]}, "labels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "nodePorts": {"type": "object", "additionalProperties": false, "properties": {"http": {"type": ["integer", "null"]}, "https": {"type": ["integer", "null"]}}}, "disableHttpPort": {"type": "boolean"}, "extraPorts": {"type": "array"}, "loadBalancerIP": {"type": ["string", "null"]}, "loadBalancerSourceRanges": {"type": "array"}}}, "https": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": ["boolean", "null"]}, "type": {"enum": [null, "", "letsencrypt", "manual", "offload", "secret"]}, "letsencrypt": {"type": "object", "additionalProperties": false, "properties": {"contactEmail": {"type": ["string", "null"]}, "acmeServer": {"type": ["string", "null"]}}}, "manual": {"type": "object", "additionalProperties": false, "properties": {"key": {"type": ["string", "null"]}, "cert": {"type": ["string", "null"]}}}, "secret": {"type": "object", "additionalProperties": false, "properties": {"name": {"type": ["string", "null"]}, "key": {"type": ["string", "null"]}, "crt": {"type": ["string", "null"]}}}, "hosts": {"type": "array"}}}, "traefik": {"type": "object", "additionalProperties": false, "properties": {"revisionHistoryLimit": {"type": ["integer", "null"], "minimum": 0}, "labels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "networkPolicy": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "ingress": {"type": "array"}, "egress": {"type": "array"}, "egressAllowRules": {"type": "object", "additionalProperties": false, "properties": {"cloudMetadataServer": {"type": "boolean"}, "dnsPortsPrivateIPs": {"type": "boolean"}, "nonPrivateIPs": {"type": "boolean"}, "privateIPs": {"type": "boolean"}}}, "interNamespaceAccessLabels": {"enum": ["accept", "ignore"]}, "allowedIngressPorts": {"type": "array"}}}, "extraInitContainers": {"type": "array"}, "extraEnv": {"type": ["object", "array"], "additionalProperties": true}, "pdb": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "maxUnavailable": {"type": ["integer", "null"]}, "minAvailable": {"type": ["integer", "null"]}}}, "nodeSelector": {"type": "object", "additionalProperties": true}, "tolerations": {"type": "array"}, "containerSecurityContext": {"type": "object", "additionalProperties": true}, "extraDynamicConfig": {"type": "object", "additionalProperties": true}, "extraPorts": {"type": "array"}, "extraStaticConfig": {"type": "object", "additionalProperties": true}, "extraVolumes": {"type": "array"}, "extraVolumeMounts": {"type": "array"}, "hsts": {"type": "object", "additionalProperties": false, "required": ["includeSubdomains", "maxAge", "preload"], "properties": {"includeSubdomains": {"type": "boolean"}, "maxAge": {"type": "integer"}, "preload": {"type": "boolean"}}}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "resources": {"type": "object", "additionalProperties": true}, "serviceAccount": {"type": "object", "required": ["create"], "additionalProperties": false, "properties": {"create": {"type": "boolean"}, "name": {"type": ["string", "null"]}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}}}, "extraPodSpec": {"type": "object", "additionalProperties": true}}}, "labels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "deploymentStrategy": {"type": "object", "additionalProperties": false, "properties": {"rollingUpdate": {"type": ["string", "null"]}, "type": {"type": ["string", "null"]}}}, "secretSync": {"type": "object", "additionalProperties": false, "properties": {"containerSecurityContext": {"type": "object", "additionalProperties": true}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "resources": {"type": "object", "additionalProperties": true}}}}}, "singleuser": {"type": "object", "additionalProperties": false, "properties": {"networkPolicy": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "ingress": {"type": "array"}, "egress": {"type": "array"}, "egressAllowRules": {"type": "object", "additionalProperties": false, "properties": {"cloudMetadataServer": {"type": "boolean"}, "dnsPortsPrivateIPs": {"type": "boolean"}, "nonPrivateIPs": {"type": "boolean"}, "privateIPs": {"type": "boolean"}}}, "interNamespaceAccessLabels": {"enum": ["accept", "ignore"]}, "allowedIngressPorts": {"type": "array"}}}, "podNameTemplate": {"type": ["string", "null"]}, "cpu": {"type": "object", "additionalProperties": false, "properties": {"limit": {"type": ["number", "null"]}, "guarantee": {"type": ["number", "null"]}}}, "memory": {"type": "object", "additionalProperties": false, "properties": {"limit": {"type": ["number", "string", "null"]}, "guarantee": {"type": ["number", "string", "null"]}}}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "initContainers": {"type": "array"}, "profileList": {"type": "array"}, "extraFiles": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "object", "additionalProperties": false, "required": ["mountPath"], "oneOf": [{"required": ["data"]}, {"required": ["stringData"]}, {"required": ["binaryData"]}], "properties": {"mountPath": {"type": "string"}, "data": {"type": "object", "additionalProperties": true}, "stringData": {"type": "string"}, "binaryData": {"type": "string"}, "mode": {"type": "number"}}}}}, "extraEnv": {"type": ["object", "array"], "additionalProperties": true}, "nodeSelector": {"type": "object", "additionalProperties": true}, "extraTolerations": {"type": "array"}, "extraNodeAffinity": {"type": "object", "additionalProperties": false, "properties": {"required": {"type": "array"}, "preferred": {"type": "array"}}}, "extraPodAffinity": {"type": "object", "additionalProperties": false, "properties": {"required": {"type": "array"}, "preferred": {"type": "array"}}}, "extraPodAntiAffinity": {"type": "object", "additionalProperties": false, "properties": {"required": {"type": "array"}, "preferred": {"type": "array"}}}, "cloudMetadata": {"type": "object", "additionalProperties": false, "properties": {"blockWithIptables": {"type": "boolean"}, "ip": {"type": "string"}}}, "cmd": {"type": ["array", "string", "null"]}, "defaultUrl": {"type": ["string", "null"]}, "events": {"type": ["boolean", "null"]}, "extraAnnotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "extraContainers": {"type": "array"}, "extraLabels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "extraPodConfig": {"type": "object", "additionalProperties": true}, "extraResource": {"type": "object", "additionalProperties": false, "properties": {"guarantees": {"type": "object", "additionalProperties": true}, "limits": {"type": "object", "additionalProperties": true}}}, "fsGid": {"type": ["integer", "null"]}, "lifecycleHooks": {"type": "object", "additionalProperties": false, "properties": {"postStart": {"type": "object", "additionalProperties": true}, "preStop": {"type": "object", "additionalProperties": true}}}, "networkTools": {"type": "object", "additionalProperties": false, "properties": {"image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "resources": {"type": "object", "additionalProperties": true}}}, "serviceAccountName": {"type": ["string", "null"]}, "startTimeout": {"type": ["integer", "null"]}, "storage": {"type": "object", "additionalProperties": false, "required": ["type", "homeMountPath"], "properties": {"capacity": {"type": ["string", "null"]}, "dynamic": {"type": "object", "additionalProperties": false, "properties": {"pvcNameTemplate": {"type": ["string", "null"]}, "storageAccessModes": {"type": "array", "items": {"type": ["string", "null"]}}, "storageClass": {"type": ["string", "null"]}, "volumeNameTemplate": {"type": ["string", "null"]}}}, "extraLabels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "extraVolumeMounts": {"type": "array"}, "extraVolumes": {"type": "array"}, "homeMountPath": {"type": "string"}, "static": {"type": "object", "additionalProperties": false, "properties": {"pvcName": {"type": ["string", "null"]}, "subPath": {"type": ["string", "null"]}}}, "type": {"enum": ["dynamic", "static", "none"]}}}, "allowPrivilegeEscalation": {"type": ["boolean", "null"]}, "uid": {"type": ["integer", "null"]}}}, "scheduling": {"type": "object", "additionalProperties": false, "properties": {"userScheduler": {"type": "object", "additionalProperties": false, "required": ["enabled", "plugins", "pluginConfig", "logLevel"], "properties": {"enabled": {"type": "boolean"}, "revisionHistoryLimit": {"type": ["integer", "null"], "minimum": 0}, "replicas": {"type": "integer"}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "pdb": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "maxUnavailable": {"type": ["integer", "null"]}, "minAvailable": {"type": ["integer", "null"]}}}, "nodeSelector": {"type": "object", "additionalProperties": true}, "tolerations": {"type": "array"}, "labels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "containerSecurityContext": {"type": "object", "additionalProperties": true}, "logLevel": {"type": "integer"}, "plugins": {"type": "object", "additionalProperties": true}, "pluginConfig": {"type": "array"}, "resources": {"type": "object", "additionalProperties": true}, "serviceAccount": {"type": "object", "required": ["create"], "additionalProperties": false, "properties": {"create": {"type": "boolean"}, "name": {"type": ["string", "null"]}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}}}, "extraPodSpec": {"type": "object", "additionalProperties": true}}}, "podPriority": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "globalDefault": {"type": "boolean"}, "defaultPriority": {"type": "integer"}, "imagePullerPriority": {"type": "integer"}, "userPlaceholderPriority": {"type": "integer"}}}, "userPlaceholder": {"type": "object", "additionalProperties": false, "properties": {"enabled": {"type": "boolean"}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "revisionHistoryLimit": {"type": ["integer", "null"], "minimum": 0}, "replicas": {"type": "integer"}, "labels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "resources": {"type": "object", "additionalProperties": true}, "containerSecurityContext": {"type": "object", "additionalProperties": true}}}, "corePods": {"type": "object", "additionalProperties": false, "properties": {"tolerations": {"type": "array"}, "nodeAffinity": {"type": "object", "additionalProperties": false, "properties": {"matchNodePurpose": {"enum": ["ignore", "prefer", "require"]}}}}}, "userPods": {"type": "object", "additionalProperties": false, "properties": {"tolerations": {"type": "array"}, "nodeAffinity": {"type": "object", "additionalProperties": false, "properties": {"matchNodePurpose": {"enum": ["ignore", "prefer", "require"]}}}}}}}, "ingress": {"type": "object", "additionalProperties": false, "required": ["enabled"], "properties": {"enabled": {"type": "boolean"}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "ingressClassName": {"type": ["string", "null"]}, "hosts": {"type": "array"}, "pathSuffix": {"type": ["string", "null"]}, "pathType": {"enum": ["Prefix", "Exact", "ImplementationSpecific"]}, "tls": {"type": "array"}}}, "prePuller": {"type": "object", "additionalProperties": false, "required": ["hook", "continuous"], "properties": {"revisionHistoryLimit": {"type": ["integer", "null"], "minimum": 0}, "labels": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}, "resources": {"type": "object", "additionalProperties": true}, "extraTolerations": {"type": "array"}, "hook": {"type": "object", "additionalProperties": false, "required": ["enabled"], "properties": {"enabled": {"type": "boolean"}, "pullOnlyOnChanges": {"type": "boolean"}, "podSchedulingWaitDuration": {"type": "integer"}, "nodeSelector": {"type": "object", "additionalProperties": true}, "tolerations": {"type": "array"}, "containerSecurityContext": {"type": "object", "additionalProperties": true}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}, "resources": {"type": "object", "additionalProperties": true}, "serviceAccount": {"type": "object", "required": ["create"], "additionalProperties": false, "properties": {"create": {"type": "boolean"}, "name": {"type": ["string", "null"]}, "annotations": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "string"}}}}}}}, "continuous": {"type": "object", "additionalProperties": false, "required": ["enabled"], "properties": {"enabled": {"type": "boolean"}}}, "pullProfileListImages": {"type": "boolean"}, "extraImages": {"type": "object", "additionalProperties": false, "patternProperties": {".*": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}}}}}, "containerSecurityContext": {"type": "object", "additionalProperties": true}, "pause": {"type": "object", "additionalProperties": false, "properties": {"containerSecurityContext": {"type": "object", "additionalProperties": true}, "image": {"type": "object", "additionalProperties": false, "required": ["name", "tag"], "properties": {"name": {"type": "string"}, "tag": {"type": "string"}, "pullPolicy": {"enum": [null, "", "IfNotPresent", "Always", "Never"]}, "pullSecrets": {"type": "array"}}}}}}}, "custom": {"type": "object", "additionalProperties": true}, "cull": {"type": "object", "additionalProperties": false, "required": ["enabled"], "properties": {"enabled": {"type": "boolean"}, "users": {"type": ["boolean", "null"]}, "adminUsers": {"type": ["boolean", "null"]}, "removeNamedServers": {"type": ["boolean", "null"]}, "timeout": {"type": ["integer", "null"]}, "every": {"type": ["integer", "null"]}, "concurrency": {"type": ["integer", "null"]}, "maxAge": {"type": ["integer", "null"]}}}, "debug": {"type": "object", "additionalProperties": false, "required": ["enabled"], "properties": {"enabled": {"type": "boolean"}}}, "rbac": {"type": "object", "additionalProperties": false, "required": ["create"], "properties": {"enabled": {"type": "boolean"}, "create": {"type": "boolean"}}}, "global": {"type": "object", "additionalProperties": true, "properties": {"safeToShowValues": {"type": "boolean"}}}}} diff --git a/skaffold.yaml b/skaffold.yaml index 7cca29e..d4ddbee 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -14,16 +14,22 @@ profiles: namespace: jupyter createNamespace: true valuesFiles: - - jupyterhub/values-minikube.yaml + - demo-values.yaml setValueTemplates: hub.image.name: "{{.IMAGE_REPO_hubimage}}/{{.IMAGE_TAG_hubimage}}@{{.IMAGE_DIGEST_hubimage}}" + setFiles: { + appHubConfig: ./files/hub/config.yml, + pageTheme: ./files/theme/page.html, + spawnPendingTheme: ./files/theme/spawn_pending.html, + spawnTheme: ./files/theme/spawn.html + } + manifests: rawYaml: - sk-k8s/cluster-role-binding.yaml - sk-k8s/script.yaml - sk-k8s/job.yaml - portForward: - resourceType: service resourceName: proxy-public From f86611dadad34990fa87659eb3f64135da704fd1 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 1 Oct 2024 13:56:22 +0200 Subject: [PATCH 13/65] files for demo --- .../config-generator-eoepca-demo.ipynb | 649 +++++++++++++ config-generator/config-generator.ipynb | 23 +- demo-eoepca-values.yaml | 677 ++++++++++++++ demo-values.yaml | 4 +- eoepca-demo/config.yml | 850 ++++++++++++++++++ files/hub/config.yml | 2 +- files/theme/page.html | 2 +- 7 files changed, 2184 insertions(+), 23 deletions(-) create mode 100644 config-generator/config-generator-eoepca-demo.ipynb create mode 100644 demo-eoepca-values.yaml create mode 100644 eoepca-demo/config.yml diff --git a/config-generator/config-generator-eoepca-demo.ipynb b/config-generator/config-generator-eoepca-demo.ipynb new file mode 100644 index 0000000..e2f8885 --- /dev/null +++ b/config-generator/config-generator-eoepca-demo.ipynb @@ -0,0 +1,649 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import yaml\n", + "from models import *\n", + "import os" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Configuration\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "storage_class_rwo = \"managed-nfs-storage\"\n", + "storage_class_rwx = \"managed-nfs-storage\"\n", + "\n", + "workspace_volume_size = \"50Gi\"\n", + "calrissian_volume_size = \"50Gi\"\n" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Volumes\n", + "\n", + "Create the Volumes" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Workspace Volume\n", + "\n", + "The workspace volume is persisted." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "workspace_volume = Volume(\n", + " name=\"workspace-volume\",\n", + " size=workspace_volume_size,\n", + " claim_name=\"workspace-claim\",\n", + " mount_path=\"/workspace\",\n", + " storage_class=storage_class_rwo,\n", + " access_modes=[\"ReadWriteOnce\"],\n", + " volume_mount=VolumeMount(name=\"workspace-volume\", mount_path=\"/workspace\"),\n", + " persist=False,\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Calrissian Volume\n", + "\n", + "This is a RWX volume, not persisted" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "calrissian_volume = Volume(\n", + " name=\"calrissian-volume\",\n", + " claim_name=\"calrissian-claim\",\n", + " size=calrissian_volume_size,\n", + " storage_class=storage_class_rwx,\n", + " access_modes=[\"ReadWriteMany\"],\n", + " volume_mount=VolumeMount(name=\"calrissian-volume\", mount_path=\"/calrissian\"),\n", + " persist=False,\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## ConfigMaps\n", + "\n", + "These configmaps are mounted as files on the pod." + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### bash login\n", + "\n", + "This file is used for the JupyterLab Terminal configuration" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"config-maps/bash-login\", \"r\") as f:\n", + " content = f.read()\n", + "\n", + "bash_login_cm = ConfigMap(\n", + " name=\"bash-login\",\n", + " key=\"bash-login\",\n", + " content=content,\n", + " readonly=True,\n", + " persist=True,\n", + " mount_path=\"/workspace/.bash_login\",\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "#### bash.rc\n" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"config-maps/bash-rc\", \"r\") as f:\n", + " content = f.read()\n", + "bash_rc_cm = ConfigMap(\n", + " name=\"bash-rc\",\n", + " key=\"bash-rc\",\n", + " content=content,\n", + " readonly=True,\n", + " persist=True,\n", + " mount_path=\"/workspace/.bashrc\",\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## bash login\n" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"config-maps/bash-login\", \"r\") as f:\n", + " content = f.read()\n", + "\n", + "bash_login_cm = ConfigMap(\n", + " name=\"bash-login\",\n", + " key=\"bash-login\",\n", + " content=content,\n", + " readonly=True,\n", + " persist=False,\n", + " mount_path=\"/workspace/.bash_login\",\n", + ")" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Profiles" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [], + "source": [ + "profiles = []" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "### Coder" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "coders = {\n", + " \"coder1\": {\n", + " \"display_name\": \"Code Server Small\",\n", + " \"slug\": \"ellip_studio_coder_slug_s\",\n", + " \"cpu_limit\": 2,\n", + " \"mem_limit\": \"8G\",\n", + " },\n", + " \"coder2\": {\n", + " \"display_name\": \"Code Server Medium\",\n", + " \"slug\": \"ellip_studio_coder_slug_m\",\n", + " \"cpu_limit\": 4,\n", + " \"mem_limit\": \"12G\",\n", + " },\n", + "}\n", + "\n", + "for key, value in coders.items():\n", + " coder_definition = ProfileDefinition(\n", + " display_name=value[\"display_name\"],\n", + " slug=value[\"slug\"],\n", + " default=False,\n", + " kubespawner_override=KubespawnerOverride(\n", + " cpu_limit=value[\"cpu_limit\"],\n", + " mem_limit=value[\"mem_limit\"],\n", + " image=\"eoepca/pde-code-server:develop\",\n", + " ),\n", + " )\n", + "\n", + " coder_profile = Profile(\n", + " id=f\"profile_studio_{key}\",\n", + " groups=[\"group-a\", \"group-b\"],\n", + " definition=coder_definition,\n", + " node_selector={},\n", + " volumes=[calrissian_volume, workspace_volume],\n", + " config_maps=[\n", + " bash_rc_cm,\n", + " ],\n", + " pod_env_vars={\n", + " \"HOME\": \"/workspace\",\n", + " \"CONDA_ENVS_PATH\": \"/workspace/.envs\",\n", + " },\n", + " )\n", + "\n", + " profiles.append(coder_profile)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## init.sh script" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"./config-maps/init.sh\", \"r\") as f:\n", + " content = f.read()\n", + "\n", + "init_cm = ConfigMap(\n", + " name=\"init\",\n", + " key=\"init\",\n", + " content=content,\n", + " readonly=True,\n", + " persist=False,\n", + " mount_path=\"/opt/init/.init.sh\",\n", + " default_mode=\"0660\",\n", + ")\n", + "\n", + "\n", + "init_context_volume_mount = InitContainerVolumeMount(\n", + " mount_path=\"/opt/init/.init.sh\", name=\"init\", sub_path=\"init\"\n", + ")\n", + "init_container = InitContainer(\n", + " name=\"init-file-on-volume\",\n", + " image=\"eoepca/pde-code-server:develop\",\n", + " command=[\"sh\", \"-c\", \"sh /opt/init/.init.sh\"],\n", + " volume_mounts=[\n", + " VolumeMount(name=\"workspace-volume\", mount_path=\"/workspace\"),\n", + " init_context_volume_mount,\n", + " ],\n", + ")\n", + "\n", + "eoepca_demo_init_script_profile = Profile(\n", + " id=f\"profile_demo_init_script\",\n", + " groups=[\"group-a\", \"group-b\"],\n", + " definition=ProfileDefinition(\n", + " display_name=\"Coder demo init script\",\n", + " description=\"This profile is used to demonstrate the use of an init script\",\n", + " slug=\"eoepca_demo_init_script\",\n", + " default=False,\n", + " kubespawner_override=KubespawnerOverride(\n", + " cpu_guarantee=1,\n", + " cpu_limit=2,\n", + " mem_guarantee=\"4G\",\n", + " mem_limit=\"6G\",\n", + " image=\"eoepca/pde-code-server:develop\",\n", + " ),\n", + " ),\n", + " node_selector={},\n", + " volumes=[calrissian_volume, workspace_volume],\n", + " config_maps=[init_cm],\n", + " pod_env_vars={\n", + " \"HOME\": \"/workspace\",\n", + " \"CONDA_ENVS_PATH\": \"/workspace/.envs\",\n", + " \"CONDARC\": \"/workspace/.condarc\",\n", + " \"XDG_RUNTIME_DIR\": \"/workspace/.local\",\n", + " \"CODE_SERVER_WS\": \"/workspace/mastering-app-package\",\n", + " },\n", + " init_containers=[init_container],\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [], + "source": [ + "profiles.append(eoepca_demo_init_script_profile)\n", + "\n", + "eoepca_demo_init_script_profile = Profile(\n", + " id=f\"profile_demo_init_script\",\n", + " groups=[\"group-a\", \"group-b\"],\n", + " definition=ProfileDefinition(\n", + " display_name=\"Coder demo init script\",\n", + " description=\"This profile is used to demonstrate the use of an init script\",\n", + " slug=\"eoepca_demo_init_script\",\n", + " default=False,\n", + " kubespawner_override=KubespawnerOverride(\n", + " cpu_guarantee=1,\n", + " cpu_limit=2,\n", + " mem_guarantee=\"4G\",\n", + " mem_limit=\"6G\",\n", + " image=\"eoepca/pde-code-server:develop\",\n", + " ),\n", + " ),\n", + " node_selector={},\n", + " volumes=[calrissian_volume, workspace_volume],\n", + " config_maps=[init_cm],\n", + " pod_env_vars={\n", + " \"HOME\": \"/workspace\",\n", + " \"CONDA_ENVS_PATH\": \"/workspace/.envs\",\n", + " \"CONDARC\": \"/workspace/.condarc\",\n", + " \"XDG_RUNTIME_DIR\": \"/workspace/.local\",\n", + " \"CODE_SERVER_WS\": \"/workspace/mastering-app-package\",\n", + " },\n", + " init_containers=[init_container],\n", + ")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## JupyterLab" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "image = \"jupyter/scipy-notebook\"\n", + "\n", + "\n", + "eoepca_jupyter_lab_profile = Profile(\n", + " id=\"profile_jupyter_lab\",\n", + " groups=[\"group-c\"],\n", + " definition=ProfileDefinition(\n", + " display_name=\"Jupyter Lab\",\n", + " description=\"Jupyter Lab with Python 3.11\",\n", + " slug=\"eoepca_jupyter_lab\",\n", + " default=False,\n", + " kubespawner_override=KubespawnerOverride(\n", + " cpu_guarantee=1,\n", + " cpu_limit=2,\n", + " mem_guarantee=\"4G\",\n", + " mem_limit=\"6G\",\n", + " image=image,\n", + " ),\n", + " ),\n", + " node_selector={},\n", + " volumes=[workspace_volume],\n", + " config_maps=[],\n", + " pod_env_vars={\n", + " \"HOME\": \"/workspace\",\n", + " \"XDG_RUNTIME_DIR\": \"/workspace/.local\",\n", + " \"XDG_CONFIG_HOME\": \"/workspace/.config\",\n", + " },\n", + ")\n", + "\n", + "profiles.append(eoepca_jupyter_lab_profile)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Image pull secret\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [], + "source": [ + "image_pull_secret = ImagePullSecret(\n", + " name=\"cr-config\",\n", + " persist=False,\n", + " data=\"\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "image = \"cr.terradue.com/eoepca-plus/scipy-notebook@sha256:f339a9fa98d3d0c1fa8d7cc850e7f5a46845781f49bee86aacba059669d02d54\"\n", + "\n", + "\n", + "eoepca_jupyter_lab_profile_2 = Profile(\n", + " id=\"profile_jupyter_lab_2\",\n", + " groups=[\"group-c\"],\n", + " definition=ProfileDefinition(\n", + " display_name=\"Jupyter Lab - profile 2\",\n", + " description=\"Jupyter Lab with Python 3.11 private image - demoes the use of an image pull secret\",\n", + " slug=\"eoepca_jupyter_lab_2\",\n", + " default=False,\n", + " kubespawner_override=KubespawnerOverride(\n", + " cpu_guarantee=1,\n", + " cpu_limit=2,\n", + " mem_guarantee=\"4G\",\n", + " mem_limit=\"6G\",\n", + " image=image,\n", + " ),\n", + " ),\n", + " node_selector={},\n", + " volumes=[workspace_volume],\n", + " config_maps=[],\n", + " pod_env_vars={\n", + " \"HOME\": \"/workspace\",\n", + " \"XDG_RUNTIME_DIR\": \"/workspace/.local\",\n", + " \"XDG_CONFIG_HOME\": \"/workspace/.config\",\n", + " },\n", + " image_pull_secrets=[image_pull_secret],\n", + ")\n", + "\n", + "profiles.append(eoepca_jupyter_lab_profile_2)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Stage-in/out" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"./config-maps/init-stac.sh\", \"r\") as f:\n", + " content = f.read()\n", + "\n", + "init_cm = ConfigMap(\n", + " name=\"init\",\n", + " key=\"init\",\n", + " content=content,\n", + " readonly=True,\n", + " persist=False,\n", + " mount_path=\"/opt/init/.init.sh\",\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"manifests/manifest.yaml\", \"r\") as f:\n", + " content = yaml.safe_load_all(f.read())\n", + "\n", + "\n", + "\n", + "localstack_manifest = Manifest(\n", + " name=\"manifests\", key=\"manifests\", readonly=True, persist=False, content=[e for e in content]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [], + "source": [ + "image = \"docker.io/eoepca/pde-code-server@sha256:f57a3d5eabcae667e0db6e84a57b0c07c692c88f0fb5c8f6900ab8d5e38fcd40\"\n", + "\n", + "coder_profile_stac = Profile(\n", + " id=f\"profile_studio_coder_stac\",\n", + " groups=[\"group-a\", \"group-b\"],\n", + " definition=ProfileDefinition(\n", + " display_name=\"Understanding STAC for input/output data modelling\",\n", + " description=\"Understand the role of STAC in input/output data manifests in EO data processing workflows\",\n", + " slug=\"ellip_studio_coder_slug_stac\",\n", + " default=False,\n", + " kubespawner_override=KubespawnerOverride(\n", + " cpu_guarantee=1,\n", + " cpu_limit=2,\n", + " mem_guarantee=\"4G\",\n", + " mem_limit=\"6G\",\n", + " image=image,\n", + " ),\n", + " ),\n", + " node_selector={},\n", + " volumes=[workspace_volume],\n", + " config_maps=[init_cm, bash_rc_cm, bash_login_cm],\n", + " pod_env_vars={\n", + " \"HOME\": \"/workspace\",\n", + " \"CONDA_ENVS_PATH\": \"/workspace/.envs\",\n", + " \"CONDARC\": \"/workspace/.condarc\",\n", + " \"XDG_RUNTIME_DIR\": \"/workspace/.local\",\n", + " \"XDG_RUNTIME_DIR\": \"/workspace/.local\",\n", + " \"XDG_CONFIG_HOME\": \"/workspace/.local\",\n", + " \"XDG_DATA_HOME\": \"/workspace/.local/share/\",\n", + " \"CWLTOOL_OPTIONS\": \"--podman\", \n", + " \"CODE_SERVER_WS\": \"/workspace/stac-eoap\",\n", + " \"AWS_DEFAULT_REGION\": \"us-east-1\",\n", + " \"AWS_ACCESS_KEY_ID\": \"test\",\n", + " \"AWS_SECRET_ACCESS_KEY\": \"test\",\n", + " },\n", + " role_bindings=[],\n", + " init_containers=[init_container],\n", + " image_pull_secrets=[image_pull_secret],\n", + " manifests=[localstack_manifest],\n", + ")\n", + "\n", + "profiles.append(coder_profile_stac)" + ] + }, + { + "attachments": {}, + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Configuration" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "config = Config(\n", + " profiles=profiles\n", + ")\n", + "\n", + "with open(\n", + " \"../eoepca-demo/config.yml\", \"w\"\n", + ") as file:\n", + " yaml.dump(config.dict(), file)" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [], + "source": [ + "# profiles" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.14" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/config-generator/config-generator.ipynb b/config-generator/config-generator.ipynb index 60fefb0..415ee10 100644 --- a/config-generator/config-generator.ipynb +++ b/config-generator/config-generator.ipynb @@ -408,7 +408,7 @@ "image_pull_secret = ImagePullSecret(\n", " name=\"cr-config\",\n", " persist=False,\n", - " data=\"ewogICAgImF1dGhzIjogewogICAgICAgICJjci50ZXJyYWR1ZS5jb20iOiB7CiAgICAgICAgICAgICJ1c2VybmFtZSI6ICJyb2JvdCRlb2VwY2EtcGx1cy1ybyIsCiAgICAgICAgICAgICJwYXNzd29yZCI6ICJQMlE4TnkyZ0lHODhkZkxveXlLN05QVUZVbHJOekFZSiIsCiAgICAgICAgICAgICJlbWFpbCI6ICJlb2VwY2EtcGx1c0B0ZXJyYWR1ZS5jb20iLAogICAgICAgICAgICAiYXV0aCI6ICJjbTlpYjNRa1pXOWxjR05oTFhCc2RYTXRjbTg2VURKUk9FNTVNbWRKUnpnNFpHWk1iM2w1U3pkT1VGVkdWV3h5VG5wQldVbz0iCiAgICAgICAgfQogICAgfQp9\",\n", + " data=\"\",\n", ")" ] }, @@ -477,26 +477,11 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 1, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[Profile(id='profile_studio_coder1', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='Code Server Small', description=None, slug='ellip_studio_coder_slug_s', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=None, mem_limit='8G', mem_guarantee=None, image='eoepca/pde-code-server:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[ConfigMap(name='bash-rc', key='bash-rc', mount_path='/workspace/.bashrc', default_mode=None, readonly=True, content='alias ll=\"ls -l\"\\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\\nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\\n. /home/jovyan/.bashrc\\n# >>> conda initialize >>>\\n# !! Contents within this block are managed by \\'conda init\\' !!\\n__conda_setup=\"$(\\'/opt/conda/bin/conda\\' \\'shell.bash\\' \\'hook\\' 2> /dev/null)\"\\nif [ $? -eq 0 ]; then\\n eval \"$__conda_setup\"\\nelse\\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/conda.sh\"\\n else\\n export PATH=\"/srv/conda/bin:$PATH\"\\n fi\\nfi\\nunset __conda_setup\\n\\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/mamba.sh\"\\nfi\\n# <<< conda initialize <<<\\n', persist=True)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteMany'], volume_mount=VolumeMount(name='calrissian-volume', mount_path='/calrissian'), persist=False), Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': ' /workspace/.envs'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[], manifests=None),\n", - " Profile(id='profile_studio_coder2', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='Code Server Medium', description=None, slug='ellip_studio_coder_slug_m', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=4, cpu_guarantee=None, mem_limit='12G', mem_guarantee=None, image='eoepca/pde-code-server:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[ConfigMap(name='bash-rc', key='bash-rc', mount_path='/workspace/.bashrc', default_mode=None, readonly=True, content='alias ll=\"ls -l\"\\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\\nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\\n. /home/jovyan/.bashrc\\n# >>> conda initialize >>>\\n# !! Contents within this block are managed by \\'conda init\\' !!\\n__conda_setup=\"$(\\'/opt/conda/bin/conda\\' \\'shell.bash\\' \\'hook\\' 2> /dev/null)\"\\nif [ $? -eq 0 ]; then\\n eval \"$__conda_setup\"\\nelse\\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/conda.sh\"\\n else\\n export PATH=\"/srv/conda/bin:$PATH\"\\n fi\\nfi\\nunset __conda_setup\\n\\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/mamba.sh\"\\nfi\\n# <<< conda initialize <<<\\n', persist=True)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteMany'], volume_mount=VolumeMount(name='calrissian-volume', mount_path='/calrissian'), persist=False), Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': ' /workspace/.envs'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[], manifests=None),\n", - " Profile(id='profile_demo_init_script', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='Coder demo init script', description='This profile is used to demonstrate the use of an init script', slug='eoepca_demo_init_script', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='eoepca/pde-code-server:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[ConfigMap(name='init', key='init', mount_path='/opt/init/.init.sh', default_mode='0660', readonly=True, content=\"set -x\\n\\ncd /workspace\\n\\ngit clone 'https://github.com/eoap/mastering-app-package.git'\\n\\ncode-server --install-extension ms-python.python\\ncode-server --install-extension redhat.vscode-yaml\\ncode-server --install-extension sbg-rabix.benten-cwl\\ncode-server --install-extension ms-toolsai.jupyter\\n\\nln -s /workspace/.local/share/code-server/extensions /workspace/extensions\\n\\nexit 0\\n\", persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteMany'], volume_mount=VolumeMount(name='calrissian-volume', mount_path='/calrissian'), persist=False), Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': '/workspace/.envs', 'CONDARC': '/workspace/.condarc', 'XDG_RUNTIME_DIR': '/workspace/.local', 'CODE_SERVER_WS': '/workspace/mastering-app-package'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[InitContainer(name='init-file-on-volume', image='eoepca/pde-code-server:develop', command=['sh', '-c', 'sh /opt/init/.init.sh'], volume_mounts=[VolumeMount(name='workspace-volume', mount_path='/workspace'), InitContainerVolumeMount(name='init', mount_path='/opt/init/.init.sh', sub_path='init')])], manifests=None),\n", - " Profile(id='profile_jupyter_lab', groups=['group-c'], definition=ProfileDefinition(display_name='Jupyter Lab', description='Jupyter Lab with Python 3.11', slug='eoepca_jupyter_lab', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='jupyter/scipy-notebook', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'XDG_RUNTIME_DIR': '/workspace/.local', 'XDG_CONFIG_HOME': '/workspace/.config'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[], manifests=None),\n", - " Profile(id='profile_jupyter_lab_2', groups=['group-c'], definition=ProfileDefinition(display_name='Jupyter Lab - profile 2', description='Jupyter Lab with Python 3.11 - demoes the use of an image pull secret', slug='eoepca_jupyter_lab_2', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='jupyter/scipy-notebook', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'XDG_RUNTIME_DIR': '/workspace/.local', 'XDG_CONFIG_HOME': '/workspace/.config'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[ImagePullSecret(name='cr-config', persist=False, data='ewogICAgImF1dGhzIjogewogICAgICAgICJjci50ZXJyYWR1ZS5jb20iOiB7CiAgICAgICAgICAgICJ1c2VybmFtZSI6ICJyb2JvdCRlb2VwY2EtcGx1cy1ybyIsCiAgICAgICAgICAgICJwYXNzd29yZCI6ICJQMlE4TnkyZ0lHODhkZkxveXlLN05QVUZVbHJOekFZSiIsCiAgICAgICAgICAgICJlbWFpbCI6ICJlb2VwY2EtcGx1c0B0ZXJyYWR1ZS5jb20iLAogICAgICAgICAgICAiYXV0aCI6ICJjbTlpYjNRa1pXOWxjR05oTFhCc2RYTXRjbTg2VURKUk9FNTVNbWRKUnpnNFpHWk1iM2w1U3pkT1VGVkdWV3h5VG5wQldVbz0iCiAgICAgICAgfQogICAgfQp9')], init_containers=[], manifests=None)]" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ - "profiles" + "# profiles" ] }, { diff --git a/demo-eoepca-values.yaml b/demo-eoepca-values.yaml new file mode 100644 index 0000000..6551ff4 --- /dev/null +++ b/demo-eoepca-values.yaml @@ -0,0 +1,677 @@ + +# fullnameOverride and nameOverride distinguishes blank strings, null values, +# and non-blank strings. For more details, see the configuration reference. +fullnameOverride: "" +nameOverride: + +# custom can contain anything you want to pass to the hub pod, as all passed +# Helm template values will be made available there. +custom: {} + +# imagePullSecret is configuration to create a k8s Secret that Helm chart's pods +# can get credentials from to pull their images. +imagePullSecret: + create: false + automaticReferenceInjection: true + registry: + username: + password: + email: +# imagePullSecrets is configuration to reference the k8s Secret resources the +# Helm chart's pods can get credentials from to pull their images. +imagePullSecrets: [] + +# hub relates to the hub pod, responsible for running JupyterHub, its configured +# Authenticator class KubeSpawner, and its configured Proxy class +# ConfigurableHTTPProxy. KubeSpawner creates the user pods, and +# ConfigurableHTTPProxy speaks with the actual ConfigurableHTTPProxy server in +# the proxy pod. +hub: + revisionHistoryLimit: + config: + JupyterHub: + admin_access: true + authenticator_class: dummy + service: + type: ClusterIP + annotations: {} + ports: + nodePort: + extraPorts: [] + loadBalancerIP: + baseUrl: / + cookieSecret: + initContainers: [] + nodeSelector: {} + tolerations: [] + concurrentSpawnLimit: 64 + consecutiveFailureLimit: 5 + activeServerLimit: + deploymentStrategy: + ## type: Recreate + ## - sqlite-pvc backed hubs require the Recreate deployment strategy as a + ## typical PVC storage can only be bound to one pod at the time. + ## - JupyterHub isn't designed to support being run in parallell. More work + ## needs to be done in JupyterHub itself for a fully highly available (HA) + ## deployment of JupyterHub on k8s is to be possible. + type: Recreate + db: + type: sqlite-pvc + upgrade: + pvc: + annotations: {} + selector: {} + accessModes: + - ReadWriteOnce + storage: 10Gi + subPath: + storageClassName: standard + url: + password: + labels: {} + annotations: {} + command: [] + args: [] + extraConfig: {} + extraFiles: {} + extraEnv: { + JUPYTERHUB_ENV: "eoepca", + JUPYTERHUB_SINGLE_USER_IMAGE_NOTEBOOKS: "", + } + extraContainers: [] + extraVolumes: [] + # - name: application-hub-config + # configMap: + # name: application-hub-jupyter-config + # defaultMode: 0744 + # - name: application-hub-config-theme + # configMap: + # name: application-hub-jupyter-config-theme + # defaultMode: 0744 + extraVolumeMounts: [] + # - name: application-hub-config + # mountPath: /usr/local/etc/applicationhub + # - name: application-hub-config-theme + # mountPath: /opt/jupyterhub/template/ + + image: + name: hubimage + tag: "dev" + pullPolicy: + pullSecrets: [] + resources: {} + podSecurityContext: + fsGroup: 1000 + containerSecurityContext: + runAsUser: 1000 + runAsGroup: 1000 + allowPrivilegeEscalation: true + lifecycle: {} + loadRoles: {} + services: {} + pdb: + enabled: false + maxUnavailable: + minAvailable: 1 + networkPolicy: + enabled: false + ingress: [] + egress: [] + egressAllowRules: + cloudMetadataServer: true + dnsPortsPrivateIPs: true + nonPrivateIPs: true + privateIPs: true + interNamespaceAccessLabels: ignore + allowedIngressPorts: [] + allowNamedServers: true + namedServerLimitPerUser: + authenticatePrometheus: + redirectToServer: + shutdownOnLogout: + templatePaths: [] + templateVars: {} + livenessProbe: + # The livenessProbe's aim to give JupyterHub sufficient time to startup but + # be able to restart if it becomes unresponsive for ~5 min. + enabled: true + initialDelaySeconds: 300 + periodSeconds: 10 + failureThreshold: 30 + timeoutSeconds: 3 + readinessProbe: + # The readinessProbe's aim is to provide a successful startup indication, + # but following that never become unready before its livenessProbe fail and + # restarts it if needed. To become unready following startup serves no + # purpose as there are no other pod to fallback to in our non-HA deployment. + enabled: true + initialDelaySeconds: 0 + periodSeconds: 2 + failureThreshold: 1000 + timeoutSeconds: 1 + existingSecret: + serviceAccount: + create: true + name: + annotations: {} + extraPodSpec: {} + +rbac: + create: true + +# proxy relates to the proxy pod, the proxy-public service, and the autohttps +# pod and proxy-http service. +proxy: + secretToken: '8af21006c7c3dc381c5d3b4b27e2c99e6311d5fc243fqbf9a14646020197d67c' + annotations: {} + deploymentStrategy: + ## type: Recreate + ## - JupyterHub's interaction with the CHP proxy becomes a lot more robust + ## with this configuration. To understand this, consider that JupyterHub + ## during startup will interact a lot with the k8s service to reach a + ## ready proxy pod. If the hub pod during a helm upgrade is restarting + ## directly while the proxy pod is making a rolling upgrade, the hub pod + ## could end up running a sequence of interactions with the old proxy pod + ## and finishing up the sequence of interactions with the new proxy pod. + ## As CHP proxy pods carry individual state this is very error prone. One + ## outcome when not using Recreate as a strategy has been that user pods + ## have been deleted by the hub pod because it considered them unreachable + ## as it only configured the old proxy pod but not the new before trying + ## to reach them. + type: Recreate + ## rollingUpdate: + ## - WARNING: + ## This is required to be set explicitly blank! Without it being + ## explicitly blank, k8s will let eventual old values under rollingUpdate + ## remain and then the Deployment becomes invalid and a helm upgrade would + ## fail with an error like this: + ## + ## UPGRADE FAILED + ## Error: Deployment.apps "proxy" is invalid: spec.strategy.rollingUpdate: Forbidden: may not be specified when strategy `type` is 'Recreate' + ## Error: UPGRADE FAILED: Deployment.apps "proxy" is invalid: spec.strategy.rollingUpdate: Forbidden: may not be specified when strategy `type` is 'Recreate' + rollingUpdate: + # service relates to the proxy-public service + service: + type: ClusterIP + labels: {} + annotations: {} + nodePorts: + http: + https: + disableHttpPort: false + extraPorts: [] + loadBalancerIP: + loadBalancerSourceRanges: [] + # chp relates to the proxy pod, which is responsible for routing traffic based + # on dynamic configuration sent from JupyterHub to CHP's REST API. + chp: + revisionHistoryLimit: + containerSecurityContext: + runAsUser: 65534 # nobody user + runAsGroup: 65534 # nobody group + allowPrivilegeEscalation: false + image: + name: jupyterhub/configurable-http-proxy + # tag is automatically bumped to new patch versions by the + # watch-dependencies.yaml workflow. + # + tag: "4.5.3" # https://github.com/jupyterhub/configurable-http-proxy/releases + pullPolicy: + pullSecrets: [] + extraCommandLineFlags: [] + livenessProbe: + enabled: true + initialDelaySeconds: 60 + periodSeconds: 10 + failureThreshold: 30 + timeoutSeconds: 3 + readinessProbe: + enabled: true + initialDelaySeconds: 0 + periodSeconds: 2 + failureThreshold: 1000 + timeoutSeconds: 1 + resources: {} + defaultTarget: + errorTarget: + extraEnv: {} + nodeSelector: {} + tolerations: [] + networkPolicy: + enabled: false + ingress: [] + egress: [] + egressAllowRules: + cloudMetadataServer: true + dnsPortsPrivateIPs: true + nonPrivateIPs: true + privateIPs: true + interNamespaceAccessLabels: ignore + allowedIngressPorts: [http, https] + pdb: + enabled: false + maxUnavailable: + minAvailable: 1 + extraPodSpec: {} + # traefik relates to the autohttps pod, which is responsible for TLS + # termination when proxy.https.type=letsencrypt. + traefik: + revisionHistoryLimit: + containerSecurityContext: + runAsUser: 65534 # nobody user + runAsGroup: 65534 # nobody group + allowPrivilegeEscalation: false + image: + name: traefik + # tag is automatically bumped to new patch versions by the + # watch-dependencies.yaml workflow. + # + tag: "v2.8.4" # ref: https://hub.docker.com/_/traefik?tab=tags + pullPolicy: + pullSecrets: [] + hsts: + includeSubdomains: false + preload: false + maxAge: 15724800 # About 6 months + resources: {} + labels: {} + extraInitContainers: [] + extraEnv: {} + extraVolumes: [] + extraVolumeMounts: [] + extraStaticConfig: {} + extraDynamicConfig: {} + nodeSelector: {} + tolerations: [] + extraPorts: [] + networkPolicy: + enabled: false + ingress: [] + egress: [] + egressAllowRules: + cloudMetadataServer: true + dnsPortsPrivateIPs: true + nonPrivateIPs: true + privateIPs: true + interNamespaceAccessLabels: ignore + allowedIngressPorts: [http, https] + pdb: + enabled: false + maxUnavailable: + minAvailable: 1 + serviceAccount: + create: true + name: + annotations: {} + extraPodSpec: {} + secretSync: + containerSecurityContext: + runAsUser: 65534 # nobody user + runAsGroup: 65534 # nobody group + allowPrivilegeEscalation: false + image: + name: jupyterhub/k8s-secret-sync + tag: "2.0.0" + pullPolicy: + pullSecrets: [] + resources: {} + labels: {} + https: + enabled: false + type: letsencrypt + #type: letsencrypt, manual, offload, secret + letsencrypt: + contactEmail: + # Specify custom server here (https://acme-staging-v02.api.letsencrypt.org/directory) to hit staging LE + acmeServer: https://acme-v02.api.letsencrypt.org/directory + manual: + key: + cert: + secret: + name: + key: tls.key + crt: tls.crt + hosts: [] + +# singleuser relates to the configuration of KubeSpawner which runs in the hub +# pod, and its spawning of user pods such as jupyter-myusername. +singleuser: + podNameTemplate: + extraTolerations: [] + nodeSelector: {"k8s.scaleway.com/pool-name": "processing-node-pool-dev"} + extraNodeAffinity: + required: [] + preferred: [] + extraPodAffinity: + required: [] + preferred: [] + extraPodAntiAffinity: + required: [] + preferred: [] + networkTools: + image: + name: jupyterhub/k8s-network-tools + tag: "2.0.0" + pullPolicy: + pullSecrets: [] + resources: {} + cloudMetadata: + # block set to true will append a privileged initContainer using the + # iptables to block the sensitive metadata server at the provided ip. + blockWithIptables: true + ip: 169.254.169.254 + networkPolicy: + enabled: false + ingress: [] + egress: [] + egressAllowRules: + cloudMetadataServer: false + dnsPortsPrivateIPs: true + nonPrivateIPs: true + privateIPs: false + interNamespaceAccessLabels: ignore + allowedIngressPorts: [] + events: true + extraAnnotations: {} + extraLabels: + hub.jupyter.org/network-access-hub: "true" + extraFiles: {} + extraEnv: {} + lifecycleHooks: {} + initContainers: [] + extraContainers: [] + allowPrivilegeEscalation: false + uid: 1000 + fsGid: 100 + serviceAccountName: + storage: + type: dynamic + extraLabels: {} + extraVolumes: [] + extraVolumeMounts: [] + static: + pvcName: + subPath: "{username}" + capacity: 10Gi + homeMountPath: /home/jovyan + dynamic: + storageClass: standard + pvcNameTemplate: claim-{username}{servername} + volumeNameTemplate: volume-{username}{servername} + storageAccessModes: [ReadWriteOnce] + image: + name: jupyterhub/k8s-singleuser-sample + tag: "2.0.0" + pullPolicy: + pullSecrets: [] + startTimeout: 6000 + cpu: + limit: + guarantee: + memory: + limit: + guarantee: 1G + extraResource: + limits: {} + guarantees: {} + cmd: jupyterhub-singleuser + defaultUrl: + extraPodConfig: {} + profileList: [] + +# scheduling relates to the user-scheduler pods and user-placeholder pods. +scheduling: + userScheduler: + enabled: true + revisionHistoryLimit: + replicas: 2 + logLevel: 4 + # plugins are configured on the user-scheduler to make us score how we + # schedule user pods in a way to help us schedule on the most busy node. By + # doing this, we help scale down more effectively. It isn't obvious how to + # enable/disable scoring plugins, and configure them, to accomplish this. + # + # plugins ref: https://kubernetes.io/docs/reference/scheduling/config/#scheduling-plugins-1 + # migration ref: https://kubernetes.io/docs/reference/scheduling/config/#scheduler-configuration-migrations + # + plugins: + score: + # These scoring plugins are enabled by default according to + # https://kubernetes.io/docs/reference/scheduling/config/#scheduling-plugins + # 2022-02-22. + # + # Enabled with high priority: + # - NodeAffinity + # - InterPodAffinity + # - NodeResourcesFit + # - ImageLocality + # Remains enabled with low default priority: + # - TaintToleration + # - PodTopologySpread + # - VolumeBinding + # Disabled for scoring: + # - NodeResourcesBalancedAllocation + # + disabled: + # We disable these plugins (with regards to scoring) to not interfere + # or complicate our use of NodeResourcesFit. + - name: NodeResourcesBalancedAllocation + # Disable plugins to be allowed to enable them again with a different + # weight and avoid an error. + - name: NodeAffinity + - name: InterPodAffinity + - name: NodeResourcesFit + - name: ImageLocality + enabled: + - name: NodeAffinity + weight: 14631 + - name: InterPodAffinity + weight: 1331 + - name: NodeResourcesFit + weight: 121 + - name: ImageLocality + weight: 11 + pluginConfig: + # Here we declare that we should optimize pods to fit based on a + # MostAllocated strategy instead of the default LeastAllocated. + - name: NodeResourcesFit + args: + scoringStrategy: + resources: + - name: cpu + weight: 1 + - name: memory + weight: 1 + type: MostAllocated + containerSecurityContext: + runAsUser: 65534 # nobody user + runAsGroup: 65534 # nobody group + allowPrivilegeEscalation: false + image: + # IMPORTANT: Bumping the minor version of this binary should go hand in + # hand with an inspection of the user-scheduelrs RBAC resources + # that we have forked in + # templates/scheduling/user-scheduler/rbac.yaml. + # + # Debugging advice: + # + # - Is configuration of kube-scheduler broken in + # templates/scheduling/user-scheduler/configmap.yaml? + # + # - Is the kube-scheduler binary's compatibility to work + # against a k8s api-server that is too new or too old? + # + # - You can update the GitHub workflow that runs tests to + # include "deploy/user-scheduler" in the k8s namespace report + # and reduce the user-scheduler deployments replicas to 1 in + # dev-config.yaml to get relevant logs from the user-scheduler + # pods. Inspect the "Kubernetes namespace report" action! + # + # - Typical failures are that kube-scheduler fails to search for + # resources via its "informers", and won't start trying to + # schedule pods before they succeed which may require + # additional RBAC permissions or that the k8s api-server is + # aware of the resources. + # + # - If "successfully acquired lease" can be seen in the logs, it + # is a good sign kube-scheduler is ready to schedule pods. + # + name: k8s.gcr.io/kube-scheduler + # tag is automatically bumped to new patch versions by the + # watch-dependencies.yaml workflow. The minor version is pinned in the + # workflow, and should be updated there if a minor version bump is done + # here. + # + tag: "v1.23.10" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md + pullPolicy: + pullSecrets: [] + nodeSelector: {} + tolerations: [] + labels: {} + annotations: {} + pdb: + enabled: true + maxUnavailable: 1 + minAvailable: + resources: {} + serviceAccount: + create: true + name: + annotations: {} + extraPodSpec: {} + podPriority: + enabled: false + globalDefault: false + defaultPriority: 0 + imagePullerPriority: -5 + userPlaceholderPriority: -10 + userPlaceholder: + enabled: true + image: + name: k8s.gcr.io/pause + # tag is automatically bumped to new patch versions by the + # watch-dependencies.yaml workflow. + # + # If you update this, also update prePuller.pause.image.tag + # + tag: "3.8" + pullPolicy: + pullSecrets: [] + revisionHistoryLimit: + replicas: 0 + labels: {} + annotations: {} + containerSecurityContext: + runAsUser: 65534 # nobody user + runAsGroup: 65534 # nobody group + allowPrivilegeEscalation: false + resources: {} + corePods: + tolerations: + - key: hub.jupyter.org/dedicated + operator: Equal + value: core + effect: NoSchedule + - key: hub.jupyter.org_dedicated + operator: Equal + value: core + effect: NoSchedule + nodeAffinity: + matchNodePurpose: prefer + userPods: + tolerations: + - key: hub.jupyter.org/dedicated + operator: Equal + value: user + effect: NoSchedule + - key: hub.jupyter.org_dedicated + operator: Equal + value: user + effect: NoSchedule + nodeAffinity: + matchNodePurpose: prefer + +# prePuller relates to the hook|continuous-image-puller DaemonsSets +prePuller: + revisionHistoryLimit: + labels: {} + annotations: {} + resources: {} + containerSecurityContext: + runAsUser: 65534 # nobody user + runAsGroup: 65534 # nobody group + allowPrivilegeEscalation: false + extraTolerations: [] + # hook relates to the hook-image-awaiter Job and hook-image-puller DaemonSet + hook: + enabled: true + pullOnlyOnChanges: true + # image and the configuration below relates to the hook-image-awaiter Job + image: + name: jupyterhub/k8s-image-awaiter + tag: "2.0.0" + pullPolicy: + pullSecrets: [] + containerSecurityContext: + runAsUser: 65534 # nobody user + runAsGroup: 65534 # nobody group + allowPrivilegeEscalation: false + podSchedulingWaitDuration: 10 + nodeSelector: {} + tolerations: [] + resources: {} + serviceAccount: + create: true + name: + annotations: {} + continuous: + enabled: true + pullProfileListImages: true + extraImages: {} + pause: + containerSecurityContext: + runAsUser: 65534 # nobody user + runAsGroup: 65534 # nobody group + allowPrivilegeEscalation: false + image: + name: k8s.gcr.io/pause + # tag is automatically bumped to new patch versions by the + # watch-dependencies.yaml workflow. + # + # If you update this, also update scheduling.userPlaceholder.image.tag + # + tag: "3.8" + pullPolicy: + pullSecrets: [] + +ingress: + enabled: false + annotations: {} + ingressClassName: + hosts: [] + pathSuffix: + pathType: Prefix + tls: [] + +# cull relates to the jupyterhub-idle-culler service, responsible for evicting +# inactive singleuser pods. +# +# The configuration below, except for enabled, corresponds to command-line flags +# for jupyterhub-idle-culler as documented here: +# https://github.com/jupyterhub/jupyterhub-idle-culler#as-a-standalone-script +# +cull: + enabled: true + users: false # --cull-users + adminUsers: true # --cull-admin-users + removeNamedServers: false # --remove-named-servers + timeout: 3600 # --timeout + every: 600 # --cull-every + concurrency: 10 # --concurrency + maxAge: 0 # --max-age + +debug: + enabled: false + +global: + safeToShowValues: false diff --git a/demo-values.yaml b/demo-values.yaml index f3a60c1..6551ff4 100644 --- a/demo-values.yaml +++ b/demo-values.yaml @@ -75,7 +75,7 @@ hub: extraConfig: {} extraFiles: {} extraEnv: { - JUPYTERHUB_ENV: "dev", + JUPYTERHUB_ENV: "eoepca", JUPYTERHUB_SINGLE_USER_IMAGE_NOTEBOOKS: "", } extraContainers: [] @@ -192,7 +192,7 @@ proxy: rollingUpdate: # service relates to the proxy-public service service: - type: LoadBalancer + type: ClusterIP labels: {} annotations: {} nodePorts: diff --git a/eoepca-demo/config.yml b/eoepca-demo/config.yml new file mode 100644 index 0000000..c3dbc6e --- /dev/null +++ b/eoepca-demo/config.yml @@ -0,0 +1,850 @@ +profiles: +- config_maps: + - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors\ + \ /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram\ + \ 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ + \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\ + \nalias aws=\"aws --endpoint-url=http://localstack:4566\"\n\n# >>> conda initialize\ + \ >>>\n# !! Contents within this block are managed by 'conda init' !!\n__conda_setup=\"\ + $('/opt/conda/bin/conda' 'shell.bash' 'hook' 2> /dev/null)\"\nif [ $? -eq 0\ + \ ]; then\n eval \"$__conda_setup\"\nelse\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\"\ + \ ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\n else\n \ + \ export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset __conda_setup\n\n\ + if [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n . \"/opt/conda/etc/profile.d/mamba.sh\"\ + \nfi\n# <<< conda initialize <<<\n" + default_mode: null + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: true + readonly: true + default_url: null + definition: + default: false + description: null + display_name: Code Server Small + kubespawner_override: + cpu_guarantee: null + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: eoepca/pde-code-server:develop + mem_guarantee: null + mem_limit: 8G + slug: ellip_studio_coder_slug_s + groups: + - group-a + - group-b + id: profile_studio_coder1 + image_pull_secrets: [] + init_containers: [] + manifests: null + node_selector: {} + pod_env_vars: + CONDA_ENVS_PATH: /workspace/.envs + HOME: /workspace + role_bindings: null + volumes: + - access_modes: + - ReadWriteMany + claim_name: calrissian-claim + name: calrissian-volume + persist: false + size: 50Gi + storage_class: managed-nfs-storage + volume_mount: + mount_path: /calrissian + name: calrissian-volume + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: false + size: 50Gi + storage_class: managed-nfs-storage + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: + - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors\ + \ /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram\ + \ 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ + \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\ + \nalias aws=\"aws --endpoint-url=http://localstack:4566\"\n\n# >>> conda initialize\ + \ >>>\n# !! Contents within this block are managed by 'conda init' !!\n__conda_setup=\"\ + $('/opt/conda/bin/conda' 'shell.bash' 'hook' 2> /dev/null)\"\nif [ $? -eq 0\ + \ ]; then\n eval \"$__conda_setup\"\nelse\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\"\ + \ ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\n else\n \ + \ export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset __conda_setup\n\n\ + if [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n . \"/opt/conda/etc/profile.d/mamba.sh\"\ + \nfi\n# <<< conda initialize <<<\n" + default_mode: null + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: true + readonly: true + default_url: null + definition: + default: false + description: null + display_name: Code Server Medium + kubespawner_override: + cpu_guarantee: null + cpu_limit: 4 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: eoepca/pde-code-server:develop + mem_guarantee: null + mem_limit: 12G + slug: ellip_studio_coder_slug_m + groups: + - group-a + - group-b + id: profile_studio_coder2 + image_pull_secrets: [] + init_containers: [] + manifests: null + node_selector: {} + pod_env_vars: + CONDA_ENVS_PATH: /workspace/.envs + HOME: /workspace + role_bindings: null + volumes: + - access_modes: + - ReadWriteMany + claim_name: calrissian-claim + name: calrissian-volume + persist: false + size: 50Gi + storage_class: managed-nfs-storage + volume_mount: + mount_path: /calrissian + name: calrissian-volume + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: false + size: 50Gi + storage_class: managed-nfs-storage + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: + - content: 'set -x + + + cd /workspace + + + git clone ''https://github.com/eoap/mastering-app-package.git'' + + + code-server --install-extension ms-python.python + + code-server --install-extension redhat.vscode-yaml + + code-server --install-extension sbg-rabix.benten-cwl + + code-server --install-extension ms-toolsai.jupyter + + + ln -s /workspace/.local/share/code-server/extensions /workspace/extensions + + + exit 0 + + ' + default_mode: '0660' + key: init + mount_path: /opt/init/.init.sh + name: init + persist: false + readonly: true + default_url: null + definition: + default: false + description: This profile is used to demonstrate the use of an init script + display_name: Coder demo init script + kubespawner_override: + cpu_guarantee: 1 + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: eoepca/pde-code-server:develop + mem_guarantee: 4G + mem_limit: 6G + slug: eoepca_demo_init_script + groups: + - group-a + - group-b + id: profile_demo_init_script + image_pull_secrets: [] + init_containers: + - command: + - sh + - -c + - sh /opt/init/.init.sh + image: eoepca/pde-code-server:develop + name: init-file-on-volume + volume_mounts: + - mount_path: /workspace + name: workspace-volume + - mount_path: /opt/init/.init.sh + name: init + sub_path: init + manifests: null + node_selector: {} + pod_env_vars: + CODE_SERVER_WS: /workspace/mastering-app-package + CONDARC: /workspace/.condarc + CONDA_ENVS_PATH: /workspace/.envs + HOME: /workspace + XDG_RUNTIME_DIR: /workspace/.local + role_bindings: null + volumes: + - access_modes: + - ReadWriteMany + claim_name: calrissian-claim + name: calrissian-volume + persist: false + size: 50Gi + storage_class: managed-nfs-storage + volume_mount: + mount_path: /calrissian + name: calrissian-volume + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: false + size: 50Gi + storage_class: managed-nfs-storage + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: [] + default_url: null + definition: + default: false + description: Jupyter Lab with Python 3.11 + display_name: Jupyter Lab + kubespawner_override: + cpu_guarantee: 1 + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: jupyter/scipy-notebook + mem_guarantee: 4G + mem_limit: 6G + slug: eoepca_jupyter_lab + groups: + - group-c + id: profile_jupyter_lab + image_pull_secrets: [] + init_containers: [] + manifests: null + node_selector: {} + pod_env_vars: + HOME: /workspace + XDG_CONFIG_HOME: /workspace/.config + XDG_RUNTIME_DIR: /workspace/.local + role_bindings: null + volumes: + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: false + size: 50Gi + storage_class: managed-nfs-storage + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: [] + default_url: null + definition: + default: false + description: Jupyter Lab with Python 3.11 private image - demoes the use of an + image pull secret + display_name: Jupyter Lab - profile 2 + kubespawner_override: + cpu_guarantee: 1 + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: cr.terradue.com/eoepca-plus/scipy-notebook@sha256:f339a9fa98d3d0c1fa8d7cc850e7f5a46845781f49bee86aacba059669d02d54 + mem_guarantee: 4G + mem_limit: 6G + slug: eoepca_jupyter_lab_2 + groups: + - group-c + id: profile_jupyter_lab_2 + image_pull_secrets: + - data: null + name: cr-config + persist: false + init_containers: [] + manifests: null + node_selector: {} + pod_env_vars: + HOME: /workspace + XDG_CONFIG_HOME: /workspace/.config + XDG_RUNTIME_DIR: /workspace/.local + role_bindings: null + volumes: + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: false + size: 50Gi + storage_class: managed-nfs-storage + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: + - content: "set -x \n\ncd /workspace\n\ngit clone 'https://github.com/eoap/stac-eoap.git'\n\ + \ncode-server --install-extension ms-python.python \ncode-server --install-extension\ + \ redhat.vscode-yaml\ncode-server --install-extension sbg-rabix.benten-cwl\n\ + code-server --install-extension ms-toolsai.jupyter\n\nln -s /workspace/.local/share/code-server/extensions\ + \ /workspace/extensions\n\nmkdir -p /workspace/User/\n\necho '{\"workbench.colorTheme\"\ + : \"Visual Studio Dark\"}' > /workspace/User/settings.json\n\npython -m venv\ + \ /workspace/.venv\nsource /workspace/.venv/bin/activate\n/workspace/.venv/bin/python\ + \ -m pip install --no-cache-dir stactools rasterio requests stac-asset click-logging\ + \ tabulate tqdm pystac-client ipykernel loguru scikit-image rio_stac boto3==1.35.23\n\ + \n/workspace/.venv/bin/python -m pip install --index-url https://test.pypi.org/simple\ + \ cwl-wrapper\n\n/workspace/.venv/bin/python -m ipykernel install --user --name\ + \ stac_env --display-name \"Python (STAC)\"\n\nexport AWS_DEFAULT_REGION=\"\ + us-east-1\"\nexport AWS_ACCESS_KEY_ID=\"test\"\nexport AWS_SECRET_ACCESS_KEY=\"\ + test\"\naws s3 mb s3://results --endpoint-url=http://localstack:4566\n\nexit\ + \ 0" + default_mode: null + key: init + mount_path: /opt/init/.init.sh + name: init + persist: false + readonly: true + - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors\ + \ /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram\ + \ 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ + \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\ + \nalias aws=\"aws --endpoint-url=http://localstack:4566\"\n\n# >>> conda initialize\ + \ >>>\n# !! Contents within this block are managed by 'conda init' !!\n__conda_setup=\"\ + $('/opt/conda/bin/conda' 'shell.bash' 'hook' 2> /dev/null)\"\nif [ $? -eq 0\ + \ ]; then\n eval \"$__conda_setup\"\nelse\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\"\ + \ ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\n else\n \ + \ export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset __conda_setup\n\n\ + if [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n . \"/opt/conda/etc/profile.d/mamba.sh\"\ + \nfi\n# <<< conda initialize <<<\n" + default_mode: null + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: true + readonly: true + - content: 'source /workspace/.bashrc + + ' + default_mode: null + key: bash-login + mount_path: /workspace/.bash_login + name: bash-login + persist: false + readonly: true + default_url: null + definition: + default: false + description: Understand the role of STAC in input/output data manifests in EO + data processing workflows + display_name: Understanding STAC for input/output data modelling + kubespawner_override: + cpu_guarantee: 1 + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: docker.io/eoepca/pde-code-server@sha256:f57a3d5eabcae667e0db6e84a57b0c07c692c88f0fb5c8f6900ab8d5e38fcd40 + mem_guarantee: 4G + mem_limit: 6G + slug: ellip_studio_coder_slug_stac + groups: + - group-a + - group-b + id: profile_studio_coder_stac + image_pull_secrets: + - data: null + name: cr-config + persist: false + init_containers: + - command: + - sh + - -c + - sh /opt/init/.init.sh + image: eoepca/pde-code-server:develop + name: init-file-on-volume + volume_mounts: + - mount_path: /workspace + name: workspace-volume + - mount_path: /opt/init/.init.sh + name: init + sub_path: init + manifests: + - content: + - apiVersion: v1 + kind: ServiceAccount + metadata: + name: localstack + - apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + name: localstack + rules: + - apiGroups: + - '' + resources: + - pods + verbs: + - '*' + - apiGroups: + - '' + resources: + - pods/log + verbs: + - get + - apiGroups: + - '' + resources: + - pods/exec + verbs: + - get + - create + - apiGroups: + - '' + resources: + - services + verbs: + - get + - list + - apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + name: localstack + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: localstack + subjects: + - kind: ServiceAccount + name: localstack + - apiVersion: v1 + kind: Service + metadata: + name: localstack + spec: + ports: + - name: edge + port: 4566 + targetPort: 4566 + - name: external-service-port-4510 + port: 4510 + targetPort: ext-svc-4510 + - name: external-service-port-4511 + port: 4511 + targetPort: ext-svc-4511 + - name: external-service-port-4512 + port: 4512 + targetPort: ext-svc-4512 + - name: external-service-port-4513 + port: 4513 + targetPort: ext-svc-4513 + - name: external-service-port-4514 + port: 4514 + targetPort: ext-svc-4514 + - name: external-service-port-4515 + port: 4515 + targetPort: ext-svc-4515 + - name: external-service-port-4516 + port: 4516 + targetPort: ext-svc-4516 + - name: external-service-port-4517 + port: 4517 + targetPort: ext-svc-4517 + - name: external-service-port-4518 + port: 4518 + targetPort: ext-svc-4518 + - name: external-service-port-4519 + port: 4519 + targetPort: ext-svc-4519 + - name: external-service-port-4520 + port: 4520 + targetPort: ext-svc-4520 + - name: external-service-port-4521 + port: 4521 + targetPort: ext-svc-4521 + - name: external-service-port-4522 + port: 4522 + targetPort: ext-svc-4522 + - name: external-service-port-4523 + port: 4523 + targetPort: ext-svc-4523 + - name: external-service-port-4524 + port: 4524 + targetPort: ext-svc-4524 + - name: external-service-port-4525 + port: 4525 + targetPort: ext-svc-4525 + - name: external-service-port-4526 + port: 4526 + targetPort: ext-svc-4526 + - name: external-service-port-4527 + port: 4527 + targetPort: ext-svc-4527 + - name: external-service-port-4528 + port: 4528 + targetPort: ext-svc-4528 + - name: external-service-port-4529 + port: 4529 + targetPort: ext-svc-4529 + - name: external-service-port-4530 + port: 4530 + targetPort: ext-svc-4530 + - name: external-service-port-4531 + port: 4531 + targetPort: ext-svc-4531 + - name: external-service-port-4532 + port: 4532 + targetPort: ext-svc-4532 + - name: external-service-port-4533 + port: 4533 + targetPort: ext-svc-4533 + - name: external-service-port-4534 + port: 4534 + targetPort: ext-svc-4534 + - name: external-service-port-4535 + port: 4535 + targetPort: ext-svc-4535 + - name: external-service-port-4536 + port: 4536 + targetPort: ext-svc-4536 + - name: external-service-port-4537 + port: 4537 + targetPort: ext-svc-4537 + - name: external-service-port-4538 + port: 4538 + targetPort: ext-svc-4538 + - name: external-service-port-4539 + port: 4539 + targetPort: ext-svc-4539 + - name: external-service-port-4540 + port: 4540 + targetPort: ext-svc-4540 + - name: external-service-port-4541 + port: 4541 + targetPort: ext-svc-4541 + - name: external-service-port-4542 + port: 4542 + targetPort: ext-svc-4542 + - name: external-service-port-4543 + port: 4543 + targetPort: ext-svc-4543 + - name: external-service-port-4544 + port: 4544 + targetPort: ext-svc-4544 + - name: external-service-port-4545 + port: 4545 + targetPort: ext-svc-4545 + - name: external-service-port-4546 + port: 4546 + targetPort: ext-svc-4546 + - name: external-service-port-4547 + port: 4547 + targetPort: ext-svc-4547 + - name: external-service-port-4548 + port: 4548 + targetPort: ext-svc-4548 + - name: external-service-port-4549 + port: 4549 + targetPort: ext-svc-4549 + - name: external-service-port-4550 + port: 4550 + targetPort: ext-svc-4550 + - name: external-service-port-4551 + port: 4551 + targetPort: ext-svc-4551 + - name: external-service-port-4552 + port: 4552 + targetPort: ext-svc-4552 + - name: external-service-port-4553 + port: 4553 + targetPort: ext-svc-4553 + - name: external-service-port-4554 + port: 4554 + targetPort: ext-svc-4554 + - name: external-service-port-4555 + port: 4555 + targetPort: ext-svc-4555 + - name: external-service-port-4556 + port: 4556 + targetPort: ext-svc-4556 + - name: external-service-port-4557 + port: 4557 + targetPort: ext-svc-4557 + - name: external-service-port-4558 + port: 4558 + targetPort: ext-svc-4558 + - name: external-service-port-4559 + port: 4559 + targetPort: ext-svc-4559 + selector: + app.kubernetes.io/instance: localstack + app.kubernetes.io/name: localstack + type: ClusterIP + - apiVersion: apps/v1 + kind: Deployment + metadata: + name: localstack + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: localstack + app.kubernetes.io/name: localstack + strategy: + type: RollingUpdate + template: + metadata: + labels: + app.kubernetes.io/instance: localstack + app.kubernetes.io/name: localstack + spec: + containers: + - env: + - name: DEBUG + value: '0' + - name: EXTERNAL_SERVICE_PORTS_START + value: '4510' + - name: EXTERNAL_SERVICE_PORTS_END + value: '4560' + - name: LOCALSTACK_K8S_SERVICE_NAME + value: localstack + - name: LOCALSTACK_K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: LAMBDA_RUNTIME_EXECUTOR + value: docker + - name: LAMBDA_K8S_IMAGE_PREFIX + value: localstack/lambda- + - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT + value: '60' + - name: OVERRIDE_IN_DOCKER + value: '1' + image: localstack/localstack:latest + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + path: /_localstack/health + port: edge + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: localstack + ports: + - containerPort: 4566 + name: edge + protocol: TCP + - containerPort: 4510 + name: ext-svc-4510 + protocol: TCP + - containerPort: 4511 + name: ext-svc-4511 + protocol: TCP + - containerPort: 4512 + name: ext-svc-4512 + protocol: TCP + - containerPort: 4513 + name: ext-svc-4513 + protocol: TCP + - containerPort: 4514 + name: ext-svc-4514 + protocol: TCP + - containerPort: 4515 + name: ext-svc-4515 + protocol: TCP + - containerPort: 4516 + name: ext-svc-4516 + protocol: TCP + - containerPort: 4517 + name: ext-svc-4517 + protocol: TCP + - containerPort: 4518 + name: ext-svc-4518 + protocol: TCP + - containerPort: 4519 + name: ext-svc-4519 + protocol: TCP + - containerPort: 4520 + name: ext-svc-4520 + protocol: TCP + - containerPort: 4521 + name: ext-svc-4521 + protocol: TCP + - containerPort: 4522 + name: ext-svc-4522 + protocol: TCP + - containerPort: 4523 + name: ext-svc-4523 + protocol: TCP + - containerPort: 4524 + name: ext-svc-4524 + protocol: TCP + - containerPort: 4525 + name: ext-svc-4525 + protocol: TCP + - containerPort: 4526 + name: ext-svc-4526 + protocol: TCP + - containerPort: 4527 + name: ext-svc-4527 + protocol: TCP + - containerPort: 4528 + name: ext-svc-4528 + protocol: TCP + - containerPort: 4529 + name: ext-svc-4529 + protocol: TCP + - containerPort: 4530 + name: ext-svc-4530 + protocol: TCP + - containerPort: 4531 + name: ext-svc-4531 + protocol: TCP + - containerPort: 4532 + name: ext-svc-4532 + protocol: TCP + - containerPort: 4533 + name: ext-svc-4533 + protocol: TCP + - containerPort: 4534 + name: ext-svc-4534 + protocol: TCP + - containerPort: 4535 + name: ext-svc-4535 + protocol: TCP + - containerPort: 4536 + name: ext-svc-4536 + protocol: TCP + - containerPort: 4537 + name: ext-svc-4537 + protocol: TCP + - containerPort: 4538 + name: ext-svc-4538 + protocol: TCP + - containerPort: 4539 + name: ext-svc-4539 + protocol: TCP + - containerPort: 4540 + name: ext-svc-4540 + protocol: TCP + - containerPort: 4541 + name: ext-svc-4541 + protocol: TCP + - containerPort: 4542 + name: ext-svc-4542 + protocol: TCP + - containerPort: 4543 + name: ext-svc-4543 + protocol: TCP + - containerPort: 4544 + name: ext-svc-4544 + protocol: TCP + - containerPort: 4545 + name: ext-svc-4545 + protocol: TCP + - containerPort: 4546 + name: ext-svc-4546 + protocol: TCP + - containerPort: 4547 + name: ext-svc-4547 + protocol: TCP + - containerPort: 4548 + name: ext-svc-4548 + protocol: TCP + - containerPort: 4549 + name: ext-svc-4549 + protocol: TCP + - containerPort: 4550 + name: ext-svc-4550 + protocol: TCP + - containerPort: 4551 + name: ext-svc-4551 + protocol: TCP + - containerPort: 4552 + name: ext-svc-4552 + protocol: TCP + - containerPort: 4553 + name: ext-svc-4553 + protocol: TCP + - containerPort: 4554 + name: ext-svc-4554 + protocol: TCP + - containerPort: 4555 + name: ext-svc-4555 + protocol: TCP + - containerPort: 4556 + name: ext-svc-4556 + protocol: TCP + - containerPort: 4557 + name: ext-svc-4557 + protocol: TCP + - containerPort: 4558 + name: ext-svc-4558 + protocol: TCP + - containerPort: 4559 + name: ext-svc-4559 + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /_localstack/health + port: edge + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: {} + securityContext: {} + securityContext: {} + serviceAccountName: localstack + volumes: [] + key: manifests + name: manifests + persist: false + node_selector: {} + pod_env_vars: + AWS_ACCESS_KEY_ID: test + AWS_DEFAULT_REGION: us-east-1 + AWS_SECRET_ACCESS_KEY: test + CODE_SERVER_WS: /workspace/stac-eoap + CONDARC: /workspace/.condarc + CONDA_ENVS_PATH: /workspace/.envs + CWLTOOL_OPTIONS: --podman + HOME: /workspace + XDG_CONFIG_HOME: /workspace/.local + XDG_DATA_HOME: /workspace/.local/share/ + XDG_RUNTIME_DIR: /workspace/.local + role_bindings: [] + volumes: + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: false + size: 50Gi + storage_class: managed-nfs-storage + volume_mount: + mount_path: /workspace + name: workspace-volume diff --git a/files/hub/config.yml b/files/hub/config.yml index 70a6dbe..a42e463 100644 --- a/files/hub/config.yml +++ b/files/hub/config.yml @@ -279,7 +279,7 @@ profiles: - group-c id: profile_jupyter_lab_2 image_pull_secrets: - - data: ewogICAgImF1dGhzIjogewogICAgICAgICJjci50ZXJyYWR1ZS5jb20iOiB7CiAgICAgICAgICAgICJ1c2VybmFtZSI6ICJyb2JvdCRlb2VwY2EtcGx1cy1ybyIsCiAgICAgICAgICAgICJwYXNzd29yZCI6ICJQMlE4TnkyZ0lHODhkZkxveXlLN05QVUZVbHJOekFZSiIsCiAgICAgICAgICAgICJlbWFpbCI6ICJlb2VwY2EtcGx1c0B0ZXJyYWR1ZS5jb20iLAogICAgICAgICAgICAiYXV0aCI6ICJjbTlpYjNRa1pXOWxjR05oTFhCc2RYTXRjbTg2VURKUk9FNTVNbWRKUnpnNFpHWk1iM2w1U3pkT1VGVkdWV3h5VG5wQldVbz0iCiAgICAgICAgfQogICAgfQp9 + - data: null name: cr-config persist: false init_containers: [] diff --git a/files/theme/page.html b/files/theme/page.html index 5bec635..6ee147a 100644 --- a/files/theme/page.html +++ b/files/theme/page.html @@ -1,5 +1,5 @@ {% extends "templates/page.html" %} -{% set announcement = 'EOEPCA+ ApplicationHub demonstration instance (super cool)' %} +{% set announcement = 'EOEPCA+ ApplicationHub demonstration instance' %} {% block title %}ApplicationHub{% endblock %} From 3efa2d930d861f315bf338a9e5a9c060815d425d Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 1 Oct 2024 14:00:03 +0200 Subject: [PATCH 14/65] removes secret --- .../config-generator-eoepca-demo.ipynb | 79 ++++++++++++-- config-generator/config-generator.ipynb | 22 +++- eoepca-demo/config.yml | 102 +++++++++++++++++- 3 files changed, 189 insertions(+), 14 deletions(-) diff --git a/config-generator/config-generator-eoepca-demo.ipynb b/config-generator/config-generator-eoepca-demo.ipynb index e2f8885..582d83e 100644 --- a/config-generator/config-generator-eoepca-demo.ipynb +++ b/config-generator/config-generator-eoepca-demo.ipynb @@ -132,7 +132,7 @@ " key=\"bash-login\",\n", " content=content,\n", " readonly=True,\n", - " persist=True,\n", + " persist=False,\n", " mount_path=\"/workspace/.bash_login\",\n", ")" ] @@ -158,7 +158,7 @@ " key=\"bash-rc\",\n", " content=content,\n", " readonly=True,\n", - " persist=True,\n", + " persist=False,\n", " mount_path=\"/workspace/.bashrc\",\n", ")" ] @@ -445,7 +445,7 @@ "outputs": [], "source": [ "image = \"cr.terradue.com/eoepca-plus/scipy-notebook@sha256:f339a9fa98d3d0c1fa8d7cc850e7f5a46845781f49bee86aacba059669d02d54\"\n", - "\n", + "image = \"eoepca/iat-jupyterlab:develop\"\n", "\n", "eoepca_jupyter_lab_profile_2 = Profile(\n", " id=\"profile_jupyter_lab_2\",\n", @@ -519,6 +519,13 @@ ")" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## e-learning" + ] + }, { "cell_type": "code", "execution_count": 17, @@ -533,7 +540,7 @@ " definition=ProfileDefinition(\n", " display_name=\"Understanding STAC for input/output data modelling\",\n", " description=\"Understand the role of STAC in input/output data manifests in EO data processing workflows\",\n", - " slug=\"ellip_studio_coder_slug_stac\",\n", + " slug=\"eoepca_coder_slug_stac\",\n", " default=False,\n", " kubespawner_override=KubespawnerOverride(\n", " cpu_guarantee=1,\n", @@ -569,6 +576,66 @@ "profiles.append(coder_profile_stac)" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## QGIS" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"./config-maps/init-qgis.sh\", \"r\") as f:\n", + " content = f.read()\n", + "\n", + "init_qgis_cm = ConfigMap(\n", + " name=\"init\",\n", + " key=\"init\",\n", + " content=content,\n", + " readonly=True,\n", + " persist=False,\n", + " mount_path=\"/opt/init/.init.sh\",\n", + ")\n", + "\n", + "image = \"eoepca/iga-remote-desktop-qgis:1.1.2\"\n", + "\n", + "qgis_profile = Profile(\n", + " id=\"profile_studio_desktop_qgis\",\n", + " groups=[\"group-a\", \"group-b\"],\n", + " definition=ProfileDefinition(\n", + " display_name=\"QGIS on a Remote Desktop\",\n", + " description=\"Spatial visualization and decision-making tools for everyone\",\n", + " slug=\"eoepca_desktop_qgis\",\n", + " default=False,\n", + " kubespawner_override=KubespawnerOverride(\n", + " cpu_limit=2,\n", + " mem_limit=\"2G\",\n", + " image=image,\n", + " ),\n", + " ),\n", + " node_selector={},\n", + " volumes=[workspace_volume],\n", + " config_maps=[init_qgis_cm, bash_rc_cm, bash_login_cm],\n", + " pod_env_vars={\n", + " \"HOME\": \"/workspace\",\n", + " \"AWS_REGION\": \"fr-par\",\n", + " \"AWS_S3_ENDPOINT\": \"s3.fr-par.scw.cloud\",\n", + " \"AWS_SECRET_ACCESS_KEY\": \"a9a7dc0c-8b0a-47b1-a311-b134b258dbb3\",\n", + " \"AWS_VIRTUAL_HOSTING\": \"true\",\n", + " \"AWS_ACCESS_KEY_ID\": \"SCWMRGD5GA0Y68XRKFW8\",\n", + " \"AWS_VIRTUAL_HOSTING\": \"true\",\n", + " },\n", + " default_url=\"desktop\",\n", + " init_containers=[init_container]\n", + " )\n", + "\n", + "profiles.append(qgis_profile)" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -579,7 +646,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 19, "metadata": {}, "outputs": [], "source": [ @@ -595,7 +662,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 20, "metadata": {}, "outputs": [], "source": [ diff --git a/config-generator/config-generator.ipynb b/config-generator/config-generator.ipynb index 415ee10..41a5ba5 100644 --- a/config-generator/config-generator.ipynb +++ b/config-generator/config-generator.ipynb @@ -197,13 +197,13 @@ "coders = {\n", " \"coder1\": {\n", " \"display_name\": \"Code Server Small\",\n", - " \"slug\": \"ellip_studio_coder_slug_s\",\n", + " \"slug\": \"eoepca_coder_slug_s\",\n", " \"cpu_limit\": 2,\n", " \"mem_limit\": \"8G\",\n", " },\n", " \"coder2\": {\n", " \"display_name\": \"Code Server Medium\",\n", - " \"slug\": \"ellip_studio_coder_slug_m\",\n", + " \"slug\": \"eoepca_coder_slug_m\",\n", " \"cpu_limit\": 4,\n", " \"mem_limit\": \"12G\",\n", " },\n", @@ -409,6 +409,7 @@ " name=\"cr-config\",\n", " persist=False,\n", " data=\"\",\n", + " data=\"\",\n", ")" ] }, @@ -479,7 +480,22 @@ "cell_type": "code", "execution_count": 1, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "[Profile(id='profile_studio_coder1', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='Code Server Small', description=None, slug='ellip_studio_coder_slug_s', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=None, mem_limit='8G', mem_guarantee=None, image='eoepca/pde-code-server:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[ConfigMap(name='bash-rc', key='bash-rc', mount_path='/workspace/.bashrc', default_mode=None, readonly=True, content='alias ll=\"ls -l\"\\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\\nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\\n. /home/jovyan/.bashrc\\n# >>> conda initialize >>>\\n# !! Contents within this block are managed by \\'conda init\\' !!\\n__conda_setup=\"$(\\'/opt/conda/bin/conda\\' \\'shell.bash\\' \\'hook\\' 2> /dev/null)\"\\nif [ $? -eq 0 ]; then\\n eval \"$__conda_setup\"\\nelse\\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/conda.sh\"\\n else\\n export PATH=\"/srv/conda/bin:$PATH\"\\n fi\\nfi\\nunset __conda_setup\\n\\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/mamba.sh\"\\nfi\\n# <<< conda initialize <<<\\n', persist=True)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteMany'], volume_mount=VolumeMount(name='calrissian-volume', mount_path='/calrissian'), persist=False), Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': ' /workspace/.envs'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[], manifests=None),\n", + " Profile(id='profile_studio_coder2', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='Code Server Medium', description=None, slug='ellip_studio_coder_slug_m', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=4, cpu_guarantee=None, mem_limit='12G', mem_guarantee=None, image='eoepca/pde-code-server:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[ConfigMap(name='bash-rc', key='bash-rc', mount_path='/workspace/.bashrc', default_mode=None, readonly=True, content='alias ll=\"ls -l\"\\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\\nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\\n. /home/jovyan/.bashrc\\n# >>> conda initialize >>>\\n# !! Contents within this block are managed by \\'conda init\\' !!\\n__conda_setup=\"$(\\'/opt/conda/bin/conda\\' \\'shell.bash\\' \\'hook\\' 2> /dev/null)\"\\nif [ $? -eq 0 ]; then\\n eval \"$__conda_setup\"\\nelse\\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/conda.sh\"\\n else\\n export PATH=\"/srv/conda/bin:$PATH\"\\n fi\\nfi\\nunset __conda_setup\\n\\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/mamba.sh\"\\nfi\\n# <<< conda initialize <<<\\n', persist=True)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteMany'], volume_mount=VolumeMount(name='calrissian-volume', mount_path='/calrissian'), persist=False), Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': ' /workspace/.envs'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[], manifests=None),\n", + " Profile(id='profile_demo_init_script', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='Coder demo init script', description='This profile is used to demonstrate the use of an init script', slug='eoepca_demo_init_script', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='eoepca/pde-code-server:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[ConfigMap(name='init', key='init', mount_path='/opt/init/.init.sh', default_mode='0660', readonly=True, content=\"set -x\\n\\ncd /workspace\\n\\ngit clone 'https://github.com/eoap/mastering-app-package.git'\\n\\ncode-server --install-extension ms-python.python\\ncode-server --install-extension redhat.vscode-yaml\\ncode-server --install-extension sbg-rabix.benten-cwl\\ncode-server --install-extension ms-toolsai.jupyter\\n\\nln -s /workspace/.local/share/code-server/extensions /workspace/extensions\\n\\nexit 0\\n\", persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteMany'], volume_mount=VolumeMount(name='calrissian-volume', mount_path='/calrissian'), persist=False), Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': '/workspace/.envs', 'CONDARC': '/workspace/.condarc', 'XDG_RUNTIME_DIR': '/workspace/.local', 'CODE_SERVER_WS': '/workspace/mastering-app-package'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[InitContainer(name='init-file-on-volume', image='eoepca/pde-code-server:develop', command=['sh', '-c', 'sh /opt/init/.init.sh'], volume_mounts=[VolumeMount(name='workspace-volume', mount_path='/workspace'), InitContainerVolumeMount(name='init', mount_path='/opt/init/.init.sh', sub_path='init')])], manifests=None),\n", + " Profile(id='profile_jupyter_lab', groups=['group-c'], definition=ProfileDefinition(display_name='Jupyter Lab', description='Jupyter Lab with Python 3.11', slug='eoepca_jupyter_lab', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='jupyter/scipy-notebook', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'XDG_RUNTIME_DIR': '/workspace/.local', 'XDG_CONFIG_HOME': '/workspace/.config'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[], manifests=None),\n", + " Profile(id='profile_jupyter_lab_2', groups=['group-c'], definition=ProfileDefinition(display_name='Jupyter Lab - profile 2', description='Jupyter Lab with Python 3.11 - demoes the use of an image pull secret', slug='eoepca_jupyter_lab_2', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='jupyter/scipy-notebook', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'XDG_RUNTIME_DIR': '/workspace/.local', 'XDG_CONFIG_HOME': '/workspace/.config'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[ImagePullSecret(name='cr-config', persist=False, data='ewogICAgImF1dGhzIjogewogICAgICAgICJjci50ZXJyYWR1ZS5jb20iOiB7CiAgICAgICAgICAgICJ1c2VybmFtZSI6ICJyb2JvdCRlb2VwY2EtcGx1cy1ybyIsCiAgICAgICAgICAgICJwYXNzd29yZCI6ICJQMlE4TnkyZ0lHODhkZkxveXlLN05QVUZVbHJOekFZSiIsCiAgICAgICAgICAgICJlbWFpbCI6ICJlb2VwY2EtcGx1c0B0ZXJyYWR1ZS5jb20iLAogICAgICAgICAgICAiYXV0aCI6ICJjbTlpYjNRa1pXOWxjR05oTFhCc2RYTXRjbTg2VURKUk9FNTVNbWRKUnpnNFpHWk1iM2w1U3pkT1VGVkdWV3h5VG5wQldVbz0iCiAgICAgICAgfQogICAgfQp9')], init_containers=[], manifests=None)]" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "# profiles" ] diff --git a/eoepca-demo/config.yml b/eoepca-demo/config.yml index c3dbc6e..61b05c9 100644 --- a/eoepca-demo/config.yml +++ b/eoepca-demo/config.yml @@ -16,7 +16,7 @@ profiles: key: bash-rc mount_path: /workspace/.bashrc name: bash-rc - persist: true + persist: false readonly: true default_url: null definition: @@ -82,7 +82,7 @@ profiles: key: bash-rc mount_path: /workspace/.bashrc name: bash-rc - persist: true + persist: false readonly: true default_url: null definition: @@ -274,7 +274,7 @@ profiles: cpu_limit: 2 extra_resource_guarantees: {} extra_resource_limits: {} - image: cr.terradue.com/eoepca-plus/scipy-notebook@sha256:f339a9fa98d3d0c1fa8d7cc850e7f5a46845781f49bee86aacba059669d02d54 + image: eoepca/iat-jupyterlab:develop mem_guarantee: 4G mem_limit: 6G slug: eoepca_jupyter_lab_2 @@ -283,6 +283,7 @@ profiles: id: profile_jupyter_lab_2 image_pull_secrets: - data: null + - data: '' name: cr-config persist: false init_containers: [] @@ -342,7 +343,7 @@ profiles: key: bash-rc mount_path: /workspace/.bashrc name: bash-rc - persist: true + persist: false readonly: true - content: 'source /workspace/.bashrc @@ -367,7 +368,7 @@ profiles: image: docker.io/eoepca/pde-code-server@sha256:f57a3d5eabcae667e0db6e84a57b0c07c692c88f0fb5c8f6900ab8d5e38fcd40 mem_guarantee: 4G mem_limit: 6G - slug: ellip_studio_coder_slug_stac + slug: eoepca_coder_slug_stac groups: - group-a - group-b @@ -848,3 +849,94 @@ profiles: volume_mount: mount_path: /workspace name: workspace-volume +- config_maps: + - content: "mkdir -p /workspace/.config/autostart\n\n\ncat < /workspace/.config/autostart/qgis.desktop\ + \ \n[Desktop Entry]\nEncoding=UTF-8\nVersion=0.9.4\nType=Application\nName=qgis\n\ + Comment=qgis\nExec=qgis\nOnlyShowIn=XFCE;\nRunHook=0\nStartupNotify=false\n\ + Terminal=false\nHidden=false\nEOF" + default_mode: null + key: init + mount_path: /opt/init/.init.sh + name: init + persist: false + readonly: true + - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors\ + \ /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram\ + \ 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ + \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\ + \nalias aws=\"aws --endpoint-url=http://localstack:4566\"\n\n# >>> conda initialize\ + \ >>>\n# !! Contents within this block are managed by 'conda init' !!\n__conda_setup=\"\ + $('/opt/conda/bin/conda' 'shell.bash' 'hook' 2> /dev/null)\"\nif [ $? -eq 0\ + \ ]; then\n eval \"$__conda_setup\"\nelse\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\"\ + \ ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\n else\n \ + \ export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset __conda_setup\n\n\ + if [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n . \"/opt/conda/etc/profile.d/mamba.sh\"\ + \nfi\n# <<< conda initialize <<<\n" + default_mode: null + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: false + readonly: true + - content: 'source /workspace/.bashrc + + ' + default_mode: null + key: bash-login + mount_path: /workspace/.bash_login + name: bash-login + persist: false + readonly: true + default_url: desktop + definition: + default: false + description: Spatial visualization and decision-making tools for everyone + display_name: QGIS on a Remote Desktop + kubespawner_override: + cpu_guarantee: null + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: eoepca/iga-remote-desktop-qgis:1.1.2 + mem_guarantee: null + mem_limit: 2G + slug: eoepca_desktop_qgis + groups: + - group-a + - group-b + id: profile_studio_desktop_qgis + image_pull_secrets: [] + init_containers: + - command: + - sh + - -c + - sh /opt/init/.init.sh + image: eoepca/pde-code-server:develop + name: init-file-on-volume + volume_mounts: + - mount_path: /workspace + name: workspace-volume + - mount_path: /opt/init/.init.sh + name: init + sub_path: init + manifests: null + node_selector: {} + pod_env_vars: + AWS_ACCESS_KEY_ID: SCWMRGD5GA0Y68XRKFW8 + AWS_REGION: fr-par + AWS_S3_ENDPOINT: s3.fr-par.scw.cloud + AWS_SECRET_ACCESS_KEY: a9a7dc0c-8b0a-47b1-a311-b134b258dbb3 + AWS_VIRTUAL_HOSTING: 'true' + HOME: /workspace + role_bindings: null + volumes: + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: false + size: 50Gi + storage_class: managed-nfs-storage + volume_mount: + mount_path: /workspace + name: workspace-volume From ce8b1c9cf909b2ee58153316b8828a2411e07b7f Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Wed, 2 Oct 2024 06:35:37 +0200 Subject: [PATCH 15/65] adds flag to check namespace check and creation --- application_hub_context/app_hub_context.py | 22 +++++++++++++++++----- jupyterhub/files/hub/jupyterhub_config.py | 2 +- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index ea46391..68d134b 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -581,6 +581,14 @@ def unapply_manifests(self, manifest_content): class DefaultApplicationHubContext(ApplicationHubContext): + + def __init__(self, namespace, spawner, config_path: str, kubeconfig_file: TextIO = None, skip_namespace_check=False, **kwargs): + + # skip_namespace_check is a flag to skip the namespace check and creation + self.skip_namespace_check = skip_namespace_check + + super().__init__(namespace, spawner, config_path, kubeconfig_file, **kwargs) + def get_profile_list(self): return self.config_parser.get_profiles() @@ -655,10 +663,14 @@ def initialise(self): # process the config maps config_maps = self.config_parser.get_profile_config_maps(profile_id=profile_id) - # check the namespace - if not self.is_namespace_created(): - self.spawner.log.info(f"Creating namespace {self.namespace}") - self.create_namespace() + if not self.skip_namespace_check: + self.spawner.log.info(f"Checking namespace {self.namespace}") + # check the namespace + if not self.is_namespace_created(): + self.spawner.log.info(f"Creating namespace {self.namespace}") + self.create_namespace() + else: + self.spawner.log.info(f"Skipping namespace check") if config_maps: for config_map in config_maps: @@ -698,7 +710,7 @@ def initialise(self): ] ) self.spawner.log.info( - f"Mounted configmap {config_map.name} (key {config_map.key}) mode {int(config_map.default_mode, 8)}" # noqa: E501 + f"Mounted configmap {config_map.name} (key {config_map.key})" # noqa: E501 ) except Exception as err: self.spawner.log.error(f"Unexpected {err=}, {type(err)=}") diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py index b97ec51..10d0f0d 100644 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ b/jupyterhub/files/hub/jupyterhub_config.py @@ -58,7 +58,7 @@ def pre_spawn_hook(spawner): namespace = f"{namespace_prefix}-{spawner.user.name}" workspace = DefaultApplicationHubContext( - namespace=namespace, spawner=spawner, config_path=config_path + namespace=namespace, spawner=spawner, config_path=config_path, skip_namespace_check=True ) workspace.initialise() From 59fd4080afebf90ed6eae13745c8d1140f0959df Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Wed, 2 Oct 2024 06:41:52 +0200 Subject: [PATCH 16/65] updated config for demos --- config-generator/config-maps/init-qgis.sh | 17 + files/hub/config.yml | 4 +- jupyterhub/files/hub/config.yml | 2144 +++++++++++++++++++-- skaffold.yaml | 31 + 4 files changed, 2046 insertions(+), 150 deletions(-) create mode 100644 config-generator/config-maps/init-qgis.sh diff --git a/config-generator/config-maps/init-qgis.sh b/config-generator/config-maps/init-qgis.sh new file mode 100644 index 0000000..738dc2e --- /dev/null +++ b/config-generator/config-maps/init-qgis.sh @@ -0,0 +1,17 @@ +mkdir -p /workspace/.config/autostart + + +cat < /workspace/.config/autostart/qgis.desktop +[Desktop Entry] +Encoding=UTF-8 +Version=0.9.4 +Type=Application +Name=qgis +Comment=qgis +Exec=qgis +OnlyShowIn=XFCE; +RunHook=0 +StartupNotify=false +Terminal=false +Hidden=false +EOF \ No newline at end of file diff --git a/files/hub/config.yml b/files/hub/config.yml index a42e463..c6f7db9 100644 --- a/files/hub/config.yml +++ b/files/hub/config.yml @@ -30,7 +30,7 @@ profiles: image: eoepca/pde-code-server:develop mem_guarantee: null mem_limit: 8G - slug: ellip_studio_coder_slug_s + slug: eoepca_coder_slug_s groups: - group-a - group-b @@ -95,7 +95,7 @@ profiles: image: eoepca/pde-code-server:develop mem_guarantee: null mem_limit: 12G - slug: ellip_studio_coder_slug_m + slug: eoepca_coder_slug_m groups: - group-a - group-b diff --git a/jupyterhub/files/hub/config.yml b/jupyterhub/files/hub/config.yml index f2f679b..6aa3fea 100644 --- a/jupyterhub/files/hub/config.yml +++ b/jupyterhub/files/hub/config.yml @@ -1,113 +1,836 @@ profiles: - config_maps: - - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors\ - \ /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram\ - \ 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ - \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\ - # >>> conda initialize >>>\n# !! Contents within this block are managed by 'conda\ - \ init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' 2>\ - \ /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n \ - \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ - \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset\ - \ __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ - \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n" + - content: "set -x \n\ncd /workspace\n\ngit clone 'https://github.com/eoap/cwl-eoap.git'\n + \ncode-server --install-extension ms-python.python \ncode-server --install-extension + redhat.vscode-yaml\ncode-server --install-extension sbg-rabix.benten-cwl\ncode-server + --install-extension ms-toolsai.jupyter\n\nln -s /workspace/.local/share/code-server/extensions + /workspace/extensions\n\nmkdir -p /workspace/User/\n\necho '{\"workbench.colorTheme\"\ + : \"Visual Studio Dark\"}' > /workspace/User/settings.json\n\npython -m venv + /workspace/.venv\nsource /workspace/.venv/bin/activate\n/workspace/.venv/bin/python + -m pip install --no-cache-dir stactools rasterio requests stac-asset click-logging + tabulate tqdm pystac-client ipykernel loguru scikit-image rio_stac\n\n/workspace/.venv/bin/python + -m ipykernel install --user --name stac_env --display-name \"Python (STAC)\"\ + \nexit 0" + default_mode: null + key: init + mount_path: /opt/init/.init.sh + name: init + persist: false + readonly: true + - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors + /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram + 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ + \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc + \n# >>> conda initialize >>>\n# !! Contents within this block are managed by + 'conda init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' + 2> /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n\ + \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ + \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset + __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ + \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n\n + export PATH=/workspace/.venv/bin:$PATH" default_mode: null key: bash-rc mount_path: /workspace/.bashrc name: bash-rc - persist: true + persist: false + readonly: true + - content: source /workspace/.bashrc + default_mode: null + key: bash-login + mount_path: /workspace/.bash_login + name: bash-login + persist: false readonly: true default_url: null definition: default: false - description: null - display_name: Code Server Small + description: Common Workflow Language (CWL) as a solution for EO Application Packaging + display_name: CWL as a solution for EO Application Portability kubespawner_override: - cpu_guarantee: null + cpu_guarantee: 1 cpu_limit: 2 extra_resource_guarantees: {} extra_resource_limits: {} - image: eoepca/pde-code-server:develop - mem_guarantee: null - mem_limit: 8G - slug: ellip_studio_coder_slug_s + image: + cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 + mem_guarantee: 4G + mem_limit: 6G + slug: ellip_studio_coder_slug_cwl groups: - - group-a - - group-b - id: profile_studio_coder1 - image_pull_secrets: [] - init_containers: [] - manifests: null - node_selector: {} + - bids23-coder + - bids23-fair + - cwl-eoap + id: profile_studio_coder_cwl_eoap + image_pull_secrets: + - data: + eyJhdXRocyI6eyJjci50ZXJyYWR1ZS5jb20iOnsidXNlcm5hbWUiOiJyb2JvdCRlbGxpcC1zdHVkaW8iLCJwYXNzd29yZCI6IjIzRlpXSm5PeEtad05RVGVYeWNzb1VnNmxVbTZIeXhTIiwiZW1haWwiOiJlbGxpcC1zdHVkaW9AdGVycmFkdWUuY29tIiwiYXV0aCI6ImNtOWliM1FrWld4c2FYQXRjM1IxWkdsdk9qSXpSbHBYU201UGVFdGFkMDVSVkdWWWVXTnpiMVZuTm14VmJUWkllWGhUIn19fQ== + name: cr-config + persist: false + init_containers: + - command: + - sh + - -c + - sh /opt/init/.init.sh + image: + cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 + name: init-file-on-volume + volume_mounts: + - mount_path: /workspace + name: workspace-volume + - mount_path: /opt/init/.init.sh + name: init + sub_path: init + manifests: + - content: "---\n# Source: localstack/templates/serviceaccount.yaml\napiVersion: + v1\nkind: ServiceAccount\nmetadata:\n name: localstack\n# ---\n# # Source: + localstack/templates/role.yaml\n# apiVersion: rbac.authorization.k8s.io/v1\n + # kind: Role\n# metadata:\n# name: localstack\n# rules:\n# - apiGroups: [\"\ + \"] # \"\" indicates the core API group\n# resources: [\"pods\"]\n# verbs: + [\"*\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/log\"]\n# verbs: [\"\ + get\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/exec\"]\n# verbs: [\"\ + get\", \"create\"]\n# - apiGroups: [\"\"]\n# resources: [\"services\"]\n#\ + \ verbs: [\"get\", \"list\"]\n# ---\n# # Source: localstack/templates/rolebinding.yaml\n + # apiVersion: rbac.authorization.k8s.io/v1\n# kind: RoleBinding\n# metadata:\n\ + # name: localstack\n# subjects:\n# # You can specify more than one \"subject\"\ + \n# - kind: ServiceAccount\n# name: localstack\n# roleRef:\n# kind: Role\n\ + # name: localstack\n# apiGroup: rbac.authorization.k8s.io\n---\n# Source: + localstack/templates/service.yaml\napiVersion: v1\nkind: Service\nmetadata:\n\ + \ name: localstack\n \n \nspec:\n type: ClusterIP\n ports:\n - name: + edge\n port: 4566\n targetPort: 4566\n - name: \"external-service-port-4510\"\ + \n port: 4510\n targetPort: \"ext-svc-4510\"\n - name: \"external-service-port-4511\"\ + \n port: 4511\n targetPort: \"ext-svc-4511\"\n - name: \"external-service-port-4512\"\ + \n port: 4512\n targetPort: \"ext-svc-4512\"\n - name: \"external-service-port-4513\"\ + \n port: 4513\n targetPort: \"ext-svc-4513\"\n - name: \"external-service-port-4514\"\ + \n port: 4514\n targetPort: \"ext-svc-4514\"\n - name: \"external-service-port-4515\"\ + \n port: 4515\n targetPort: \"ext-svc-4515\"\n - name: \"external-service-port-4516\"\ + \n port: 4516\n targetPort: \"ext-svc-4516\"\n - name: \"external-service-port-4517\"\ + \n port: 4517\n targetPort: \"ext-svc-4517\"\n - name: \"external-service-port-4518\"\ + \n port: 4518\n targetPort: \"ext-svc-4518\"\n - name: \"external-service-port-4519\"\ + \n port: 4519\n targetPort: \"ext-svc-4519\"\n - name: \"external-service-port-4520\"\ + \n port: 4520\n targetPort: \"ext-svc-4520\"\n - name: \"external-service-port-4521\"\ + \n port: 4521\n targetPort: \"ext-svc-4521\"\n - name: \"external-service-port-4522\"\ + \n port: 4522\n targetPort: \"ext-svc-4522\"\n - name: \"external-service-port-4523\"\ + \n port: 4523\n targetPort: \"ext-svc-4523\"\n - name: \"external-service-port-4524\"\ + \n port: 4524\n targetPort: \"ext-svc-4524\"\n - name: \"external-service-port-4525\"\ + \n port: 4525\n targetPort: \"ext-svc-4525\"\n - name: \"external-service-port-4526\"\ + \n port: 4526\n targetPort: \"ext-svc-4526\"\n - name: \"external-service-port-4527\"\ + \n port: 4527\n targetPort: \"ext-svc-4527\"\n - name: \"external-service-port-4528\"\ + \n port: 4528\n targetPort: \"ext-svc-4528\"\n - name: \"external-service-port-4529\"\ + \n port: 4529\n targetPort: \"ext-svc-4529\"\n - name: \"external-service-port-4530\"\ + \n port: 4530\n targetPort: \"ext-svc-4530\"\n - name: \"external-service-port-4531\"\ + \n port: 4531\n targetPort: \"ext-svc-4531\"\n - name: \"external-service-port-4532\"\ + \n port: 4532\n targetPort: \"ext-svc-4532\"\n - name: \"external-service-port-4533\"\ + \n port: 4533\n targetPort: \"ext-svc-4533\"\n - name: \"external-service-port-4534\"\ + \n port: 4534\n targetPort: \"ext-svc-4534\"\n - name: \"external-service-port-4535\"\ + \n port: 4535\n targetPort: \"ext-svc-4535\"\n - name: \"external-service-port-4536\"\ + \n port: 4536\n targetPort: \"ext-svc-4536\"\n - name: \"external-service-port-4537\"\ + \n port: 4537\n targetPort: \"ext-svc-4537\"\n - name: \"external-service-port-4538\"\ + \n port: 4538\n targetPort: \"ext-svc-4538\"\n - name: \"external-service-port-4539\"\ + \n port: 4539\n targetPort: \"ext-svc-4539\"\n - name: \"external-service-port-4540\"\ + \n port: 4540\n targetPort: \"ext-svc-4540\"\n - name: \"external-service-port-4541\"\ + \n port: 4541\n targetPort: \"ext-svc-4541\"\n - name: \"external-service-port-4542\"\ + \n port: 4542\n targetPort: \"ext-svc-4542\"\n - name: \"external-service-port-4543\"\ + \n port: 4543\n targetPort: \"ext-svc-4543\"\n - name: \"external-service-port-4544\"\ + \n port: 4544\n targetPort: \"ext-svc-4544\"\n - name: \"external-service-port-4545\"\ + \n port: 4545\n targetPort: \"ext-svc-4545\"\n - name: \"external-service-port-4546\"\ + \n port: 4546\n targetPort: \"ext-svc-4546\"\n - name: \"external-service-port-4547\"\ + \n port: 4547\n targetPort: \"ext-svc-4547\"\n - name: \"external-service-port-4548\"\ + \n port: 4548\n targetPort: \"ext-svc-4548\"\n - name: \"external-service-port-4549\"\ + \n port: 4549\n targetPort: \"ext-svc-4549\"\n - name: \"external-service-port-4550\"\ + \n port: 4550\n targetPort: \"ext-svc-4550\"\n - name: \"external-service-port-4551\"\ + \n port: 4551\n targetPort: \"ext-svc-4551\"\n - name: \"external-service-port-4552\"\ + \n port: 4552\n targetPort: \"ext-svc-4552\"\n - name: \"external-service-port-4553\"\ + \n port: 4553\n targetPort: \"ext-svc-4553\"\n - name: \"external-service-port-4554\"\ + \n port: 4554\n targetPort: \"ext-svc-4554\"\n - name: \"external-service-port-4555\"\ + \n port: 4555\n targetPort: \"ext-svc-4555\"\n - name: \"external-service-port-4556\"\ + \n port: 4556\n targetPort: \"ext-svc-4556\"\n - name: \"external-service-port-4557\"\ + \n port: 4557\n targetPort: \"ext-svc-4557\"\n - name: \"external-service-port-4558\"\ + \n port: 4558\n targetPort: \"ext-svc-4558\"\n - name: \"external-service-port-4559\"\ + \n port: 4559\n targetPort: \"ext-svc-4559\"\n selector:\n app.kubernetes.io/name: + localstack\n app.kubernetes.io/instance: localstack\n---\n# Source: localstack/templates/deployment.yaml\n + apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: localstack\n \n\ + spec:\n replicas: 1\n strategy:\n type: RollingUpdate\n selector:\n \ + \ matchLabels:\n app.kubernetes.io/name: localstack\n app.kubernetes.io/instance: + localstack\n template:\n metadata:\n labels:\n app.kubernetes.io/name: + localstack\n app.kubernetes.io/instance: localstack\n spec:\n \ + \ serviceAccountName: localstack\n securityContext:\n {}\n \ + \ containers:\n - name: localstack\n securityContext:\n \ + \ {}\n image: \"localstack/localstack:latest\"\n imagePullPolicy: + IfNotPresent\n ports:\n - name: edge\n containerPort: + 4566\n protocol: TCP\n - name: \"ext-svc-4510\"\n \ + \ containerPort: 4510\n protocol: TCP\n - + name: \"ext-svc-4511\"\n containerPort: 4511\n protocol: + TCP\n - name: \"ext-svc-4512\"\n containerPort: 4512\n\ + \ protocol: TCP\n - name: \"ext-svc-4513\"\n \ + \ containerPort: 4513\n protocol: TCP\n - name: + \"ext-svc-4514\"\n containerPort: 4514\n protocol: + TCP\n - name: \"ext-svc-4515\"\n containerPort: 4515\n\ + \ protocol: TCP\n - name: \"ext-svc-4516\"\n \ + \ containerPort: 4516\n protocol: TCP\n - name: + \"ext-svc-4517\"\n containerPort: 4517\n protocol: + TCP\n - name: \"ext-svc-4518\"\n containerPort: 4518\n\ + \ protocol: TCP\n - name: \"ext-svc-4519\"\n \ + \ containerPort: 4519\n protocol: TCP\n - name: + \"ext-svc-4520\"\n containerPort: 4520\n protocol: + TCP\n - name: \"ext-svc-4521\"\n containerPort: 4521\n\ + \ protocol: TCP\n - name: \"ext-svc-4522\"\n \ + \ containerPort: 4522\n protocol: TCP\n - name: + \"ext-svc-4523\"\n containerPort: 4523\n protocol: + TCP\n - name: \"ext-svc-4524\"\n containerPort: 4524\n\ + \ protocol: TCP\n - name: \"ext-svc-4525\"\n \ + \ containerPort: 4525\n protocol: TCP\n - name: + \"ext-svc-4526\"\n containerPort: 4526\n protocol: + TCP\n - name: \"ext-svc-4527\"\n containerPort: 4527\n\ + \ protocol: TCP\n - name: \"ext-svc-4528\"\n \ + \ containerPort: 4528\n protocol: TCP\n - name: + \"ext-svc-4529\"\n containerPort: 4529\n protocol: + TCP\n - name: \"ext-svc-4530\"\n containerPort: 4530\n\ + \ protocol: TCP\n - name: \"ext-svc-4531\"\n \ + \ containerPort: 4531\n protocol: TCP\n - name: + \"ext-svc-4532\"\n containerPort: 4532\n protocol: + TCP\n - name: \"ext-svc-4533\"\n containerPort: 4533\n\ + \ protocol: TCP\n - name: \"ext-svc-4534\"\n \ + \ containerPort: 4534\n protocol: TCP\n - name: + \"ext-svc-4535\"\n containerPort: 4535\n protocol: + TCP\n - name: \"ext-svc-4536\"\n containerPort: 4536\n\ + \ protocol: TCP\n - name: \"ext-svc-4537\"\n \ + \ containerPort: 4537\n protocol: TCP\n - name: + \"ext-svc-4538\"\n containerPort: 4538\n protocol: + TCP\n - name: \"ext-svc-4539\"\n containerPort: 4539\n\ + \ protocol: TCP\n - name: \"ext-svc-4540\"\n \ + \ containerPort: 4540\n protocol: TCP\n - name: + \"ext-svc-4541\"\n containerPort: 4541\n protocol: + TCP\n - name: \"ext-svc-4542\"\n containerPort: 4542\n\ + \ protocol: TCP\n - name: \"ext-svc-4543\"\n \ + \ containerPort: 4543\n protocol: TCP\n - name: + \"ext-svc-4544\"\n containerPort: 4544\n protocol: + TCP\n - name: \"ext-svc-4545\"\n containerPort: 4545\n\ + \ protocol: TCP\n - name: \"ext-svc-4546\"\n \ + \ containerPort: 4546\n protocol: TCP\n - name: + \"ext-svc-4547\"\n containerPort: 4547\n protocol: + TCP\n - name: \"ext-svc-4548\"\n containerPort: 4548\n\ + \ protocol: TCP\n - name: \"ext-svc-4549\"\n \ + \ containerPort: 4549\n protocol: TCP\n - name: + \"ext-svc-4550\"\n containerPort: 4550\n protocol: + TCP\n - name: \"ext-svc-4551\"\n containerPort: 4551\n\ + \ protocol: TCP\n - name: \"ext-svc-4552\"\n \ + \ containerPort: 4552\n protocol: TCP\n - name: + \"ext-svc-4553\"\n containerPort: 4553\n protocol: + TCP\n - name: \"ext-svc-4554\"\n containerPort: 4554\n\ + \ protocol: TCP\n - name: \"ext-svc-4555\"\n \ + \ containerPort: 4555\n protocol: TCP\n - name: + \"ext-svc-4556\"\n containerPort: 4556\n protocol: + TCP\n - name: \"ext-svc-4557\"\n containerPort: 4557\n\ + \ protocol: TCP\n - name: \"ext-svc-4558\"\n \ + \ containerPort: 4558\n protocol: TCP\n - name: + \"ext-svc-4559\"\n containerPort: 4559\n protocol: + TCP\n livenessProbe:\n failureThreshold: 3\n \ + \ initialDelaySeconds: 0\n periodSeconds: 10\n successThreshold: + 1\n timeoutSeconds: 1\n httpGet:\n path: + /_localstack/health\n port: edge\n readinessProbe:\n \ + \ failureThreshold: 3\n initialDelaySeconds: 0\n \ + \ periodSeconds: 10\n successThreshold: 1\n timeoutSeconds: + 1\n httpGet:\n path: /_localstack/health\n \ + \ port: edge\n resources: {}\n env:\n - name: + DEBUG\n value: \"0\"\n - name: EXTERNAL_SERVICE_PORTS_START\n\ + \ value: \"4510\"\n - name: EXTERNAL_SERVICE_PORTS_END\n\ + \ value: \"4560\"\n - name: LOCALSTACK_K8S_SERVICE_NAME\n\ + \ value: localstack\n - name: LOCALSTACK_K8S_NAMESPACE\n\ + \ valueFrom:\n fieldRef:\n fieldPath: + metadata.namespace\n - name: LAMBDA_RUNTIME_EXECUTOR\n \ + \ value: \"docker\"\n - name: LAMBDA_K8S_IMAGE_PREFIX\n \ + \ value: \"localstack/lambda-\"\n - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT\n\ + \ value: \"60\"\n - name: OVERRIDE_IN_DOCKER\n \ + \ value: \"1\"\n volumes: []\n" + key: manifests + name: manifests + persist: false + node_selector: + k8s.scaleway.com/pool-name: processing-node-pool pod_env_vars: - CONDA_ENVS_PATH: ' /workspace/.envs' + CODE_SERVER_WS: /workspace/cwl-eoap + CONDARC: /workspace/.condarc + CONDA_ENVS_PATH: /workspace/.envs + CWLTOOL_OPTIONS: --podman HOME: /workspace - role_bindings: null + XDG_CONFIG_HOME: /workspace/.local + XDG_DATA_HOME: /workspace/.local/share/ + XDG_RUNTIME_DIR: /workspace/.local + role_bindings: [] volumes: - access_modes: - - ReadWriteMany - claim_name: calrissian-claim - name: calrissian-volume + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume persist: false - size: 50Gi - storage_class: standard + size: 10Gi + storage_class: scw-bssd volume_mount: - mount_path: /calrissian - name: calrissian-volume + mount_path: /workspace + name: workspace-volume +- config_maps: + - content: "set -x \n\ncd /workspace\n\ngit clone 'https://github.com/eoap/stac-eoap.git'\n + \ncode-server --install-extension ms-python.python \ncode-server --install-extension + redhat.vscode-yaml\ncode-server --install-extension sbg-rabix.benten-cwl\ncode-server + --install-extension ms-toolsai.jupyter\n\nln -s /workspace/.local/share/code-server/extensions + /workspace/extensions\n\nmkdir -p /workspace/User/\n\necho '{\"workbench.colorTheme\"\ + : \"Visual Studio Dark\"}' > /workspace/User/settings.json\n\npython -m venv + /workspace/.venv\nsource /workspace/.venv/bin/activate\n/workspace/.venv/bin/python + -m pip install --no-cache-dir stactools rasterio requests stac-asset click-logging + tabulate tqdm pystac-client ipykernel loguru scikit-image rio_stac\n\n/workspace/.venv/bin/python + -m ipykernel install --user --name stac_env --display-name \"Python (STAC)\"\ + \nexit 0" + default_mode: null + key: init + mount_path: /opt/init/.init.sh + name: init + persist: false + readonly: true + - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors + /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram + 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ + \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc + \n# >>> conda initialize >>>\n# !! Contents within this block are managed by + 'conda init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' + 2> /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n\ + \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ + \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset + __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ + \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n\n + export PATH=/workspace/.venv/bin:$PATH" + default_mode: null + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: false + readonly: true + - content: source /workspace/.bashrc + default_mode: null + key: bash-login + mount_path: /workspace/.bash_login + name: bash-login + persist: false + readonly: true + default_url: null + definition: + default: false + description: Understand the role of STAC in input/output data manifests in EO + data processing workflows + display_name: Understanding STAC for input/output data modelling + kubespawner_override: + cpu_guarantee: 1 + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: + cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 + mem_guarantee: 4G + mem_limit: 6G + slug: ellip_studio_coder_slug_stac + groups: + - bids23-coder + - bids23-fair + - stac + id: profile_studio_coder_stac + image_pull_secrets: + - data: + eyJhdXRocyI6eyJjci50ZXJyYWR1ZS5jb20iOnsidXNlcm5hbWUiOiJyb2JvdCRlbGxpcC1zdHVkaW8iLCJwYXNzd29yZCI6IjIzRlpXSm5PeEtad05RVGVYeWNzb1VnNmxVbTZIeXhTIiwiZW1haWwiOiJlbGxpcC1zdHVkaW9AdGVycmFkdWUuY29tIiwiYXV0aCI6ImNtOWliM1FrWld4c2FYQXRjM1IxWkdsdk9qSXpSbHBYU201UGVFdGFkMDVSVkdWWWVXTnpiMVZuTm14VmJUWkllWGhUIn19fQ== + name: cr-config + persist: false + init_containers: + - command: + - sh + - -c + - sh /opt/init/.init.sh + image: + cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 + name: init-file-on-volume + volume_mounts: + - mount_path: /workspace + name: workspace-volume + - mount_path: /opt/init/.init.sh + name: init + sub_path: init + manifests: + - content: "---\n# Source: localstack/templates/serviceaccount.yaml\napiVersion: + v1\nkind: ServiceAccount\nmetadata:\n name: localstack\n# ---\n# # Source: + localstack/templates/role.yaml\n# apiVersion: rbac.authorization.k8s.io/v1\n + # kind: Role\n# metadata:\n# name: localstack\n# rules:\n# - apiGroups: [\"\ + \"] # \"\" indicates the core API group\n# resources: [\"pods\"]\n# verbs: + [\"*\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/log\"]\n# verbs: [\"\ + get\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/exec\"]\n# verbs: [\"\ + get\", \"create\"]\n# - apiGroups: [\"\"]\n# resources: [\"services\"]\n#\ + \ verbs: [\"get\", \"list\"]\n# ---\n# # Source: localstack/templates/rolebinding.yaml\n + # apiVersion: rbac.authorization.k8s.io/v1\n# kind: RoleBinding\n# metadata:\n\ + # name: localstack\n# subjects:\n# # You can specify more than one \"subject\"\ + \n# - kind: ServiceAccount\n# name: localstack\n# roleRef:\n# kind: Role\n\ + # name: localstack\n# apiGroup: rbac.authorization.k8s.io\n---\n# Source: + localstack/templates/service.yaml\napiVersion: v1\nkind: Service\nmetadata:\n\ + \ name: localstack\n \n \nspec:\n type: ClusterIP\n ports:\n - name: + edge\n port: 4566\n targetPort: 4566\n - name: \"external-service-port-4510\"\ + \n port: 4510\n targetPort: \"ext-svc-4510\"\n - name: \"external-service-port-4511\"\ + \n port: 4511\n targetPort: \"ext-svc-4511\"\n - name: \"external-service-port-4512\"\ + \n port: 4512\n targetPort: \"ext-svc-4512\"\n - name: \"external-service-port-4513\"\ + \n port: 4513\n targetPort: \"ext-svc-4513\"\n - name: \"external-service-port-4514\"\ + \n port: 4514\n targetPort: \"ext-svc-4514\"\n - name: \"external-service-port-4515\"\ + \n port: 4515\n targetPort: \"ext-svc-4515\"\n - name: \"external-service-port-4516\"\ + \n port: 4516\n targetPort: \"ext-svc-4516\"\n - name: \"external-service-port-4517\"\ + \n port: 4517\n targetPort: \"ext-svc-4517\"\n - name: \"external-service-port-4518\"\ + \n port: 4518\n targetPort: \"ext-svc-4518\"\n - name: \"external-service-port-4519\"\ + \n port: 4519\n targetPort: \"ext-svc-4519\"\n - name: \"external-service-port-4520\"\ + \n port: 4520\n targetPort: \"ext-svc-4520\"\n - name: \"external-service-port-4521\"\ + \n port: 4521\n targetPort: \"ext-svc-4521\"\n - name: \"external-service-port-4522\"\ + \n port: 4522\n targetPort: \"ext-svc-4522\"\n - name: \"external-service-port-4523\"\ + \n port: 4523\n targetPort: \"ext-svc-4523\"\n - name: \"external-service-port-4524\"\ + \n port: 4524\n targetPort: \"ext-svc-4524\"\n - name: \"external-service-port-4525\"\ + \n port: 4525\n targetPort: \"ext-svc-4525\"\n - name: \"external-service-port-4526\"\ + \n port: 4526\n targetPort: \"ext-svc-4526\"\n - name: \"external-service-port-4527\"\ + \n port: 4527\n targetPort: \"ext-svc-4527\"\n - name: \"external-service-port-4528\"\ + \n port: 4528\n targetPort: \"ext-svc-4528\"\n - name: \"external-service-port-4529\"\ + \n port: 4529\n targetPort: \"ext-svc-4529\"\n - name: \"external-service-port-4530\"\ + \n port: 4530\n targetPort: \"ext-svc-4530\"\n - name: \"external-service-port-4531\"\ + \n port: 4531\n targetPort: \"ext-svc-4531\"\n - name: \"external-service-port-4532\"\ + \n port: 4532\n targetPort: \"ext-svc-4532\"\n - name: \"external-service-port-4533\"\ + \n port: 4533\n targetPort: \"ext-svc-4533\"\n - name: \"external-service-port-4534\"\ + \n port: 4534\n targetPort: \"ext-svc-4534\"\n - name: \"external-service-port-4535\"\ + \n port: 4535\n targetPort: \"ext-svc-4535\"\n - name: \"external-service-port-4536\"\ + \n port: 4536\n targetPort: \"ext-svc-4536\"\n - name: \"external-service-port-4537\"\ + \n port: 4537\n targetPort: \"ext-svc-4537\"\n - name: \"external-service-port-4538\"\ + \n port: 4538\n targetPort: \"ext-svc-4538\"\n - name: \"external-service-port-4539\"\ + \n port: 4539\n targetPort: \"ext-svc-4539\"\n - name: \"external-service-port-4540\"\ + \n port: 4540\n targetPort: \"ext-svc-4540\"\n - name: \"external-service-port-4541\"\ + \n port: 4541\n targetPort: \"ext-svc-4541\"\n - name: \"external-service-port-4542\"\ + \n port: 4542\n targetPort: \"ext-svc-4542\"\n - name: \"external-service-port-4543\"\ + \n port: 4543\n targetPort: \"ext-svc-4543\"\n - name: \"external-service-port-4544\"\ + \n port: 4544\n targetPort: \"ext-svc-4544\"\n - name: \"external-service-port-4545\"\ + \n port: 4545\n targetPort: \"ext-svc-4545\"\n - name: \"external-service-port-4546\"\ + \n port: 4546\n targetPort: \"ext-svc-4546\"\n - name: \"external-service-port-4547\"\ + \n port: 4547\n targetPort: \"ext-svc-4547\"\n - name: \"external-service-port-4548\"\ + \n port: 4548\n targetPort: \"ext-svc-4548\"\n - name: \"external-service-port-4549\"\ + \n port: 4549\n targetPort: \"ext-svc-4549\"\n - name: \"external-service-port-4550\"\ + \n port: 4550\n targetPort: \"ext-svc-4550\"\n - name: \"external-service-port-4551\"\ + \n port: 4551\n targetPort: \"ext-svc-4551\"\n - name: \"external-service-port-4552\"\ + \n port: 4552\n targetPort: \"ext-svc-4552\"\n - name: \"external-service-port-4553\"\ + \n port: 4553\n targetPort: \"ext-svc-4553\"\n - name: \"external-service-port-4554\"\ + \n port: 4554\n targetPort: \"ext-svc-4554\"\n - name: \"external-service-port-4555\"\ + \n port: 4555\n targetPort: \"ext-svc-4555\"\n - name: \"external-service-port-4556\"\ + \n port: 4556\n targetPort: \"ext-svc-4556\"\n - name: \"external-service-port-4557\"\ + \n port: 4557\n targetPort: \"ext-svc-4557\"\n - name: \"external-service-port-4558\"\ + \n port: 4558\n targetPort: \"ext-svc-4558\"\n - name: \"external-service-port-4559\"\ + \n port: 4559\n targetPort: \"ext-svc-4559\"\n selector:\n app.kubernetes.io/name: + localstack\n app.kubernetes.io/instance: localstack\n---\n# Source: localstack/templates/deployment.yaml\n + apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: localstack\n \n\ + spec:\n replicas: 1\n strategy:\n type: RollingUpdate\n selector:\n \ + \ matchLabels:\n app.kubernetes.io/name: localstack\n app.kubernetes.io/instance: + localstack\n template:\n metadata:\n labels:\n app.kubernetes.io/name: + localstack\n app.kubernetes.io/instance: localstack\n spec:\n \ + \ serviceAccountName: localstack\n securityContext:\n {}\n \ + \ containers:\n - name: localstack\n securityContext:\n \ + \ {}\n image: \"localstack/localstack:latest\"\n imagePullPolicy: + IfNotPresent\n ports:\n - name: edge\n containerPort: + 4566\n protocol: TCP\n - name: \"ext-svc-4510\"\n \ + \ containerPort: 4510\n protocol: TCP\n - + name: \"ext-svc-4511\"\n containerPort: 4511\n protocol: + TCP\n - name: \"ext-svc-4512\"\n containerPort: 4512\n\ + \ protocol: TCP\n - name: \"ext-svc-4513\"\n \ + \ containerPort: 4513\n protocol: TCP\n - name: + \"ext-svc-4514\"\n containerPort: 4514\n protocol: + TCP\n - name: \"ext-svc-4515\"\n containerPort: 4515\n\ + \ protocol: TCP\n - name: \"ext-svc-4516\"\n \ + \ containerPort: 4516\n protocol: TCP\n - name: + \"ext-svc-4517\"\n containerPort: 4517\n protocol: + TCP\n - name: \"ext-svc-4518\"\n containerPort: 4518\n\ + \ protocol: TCP\n - name: \"ext-svc-4519\"\n \ + \ containerPort: 4519\n protocol: TCP\n - name: + \"ext-svc-4520\"\n containerPort: 4520\n protocol: + TCP\n - name: \"ext-svc-4521\"\n containerPort: 4521\n\ + \ protocol: TCP\n - name: \"ext-svc-4522\"\n \ + \ containerPort: 4522\n protocol: TCP\n - name: + \"ext-svc-4523\"\n containerPort: 4523\n protocol: + TCP\n - name: \"ext-svc-4524\"\n containerPort: 4524\n\ + \ protocol: TCP\n - name: \"ext-svc-4525\"\n \ + \ containerPort: 4525\n protocol: TCP\n - name: + \"ext-svc-4526\"\n containerPort: 4526\n protocol: + TCP\n - name: \"ext-svc-4527\"\n containerPort: 4527\n\ + \ protocol: TCP\n - name: \"ext-svc-4528\"\n \ + \ containerPort: 4528\n protocol: TCP\n - name: + \"ext-svc-4529\"\n containerPort: 4529\n protocol: + TCP\n - name: \"ext-svc-4530\"\n containerPort: 4530\n\ + \ protocol: TCP\n - name: \"ext-svc-4531\"\n \ + \ containerPort: 4531\n protocol: TCP\n - name: + \"ext-svc-4532\"\n containerPort: 4532\n protocol: + TCP\n - name: \"ext-svc-4533\"\n containerPort: 4533\n\ + \ protocol: TCP\n - name: \"ext-svc-4534\"\n \ + \ containerPort: 4534\n protocol: TCP\n - name: + \"ext-svc-4535\"\n containerPort: 4535\n protocol: + TCP\n - name: \"ext-svc-4536\"\n containerPort: 4536\n\ + \ protocol: TCP\n - name: \"ext-svc-4537\"\n \ + \ containerPort: 4537\n protocol: TCP\n - name: + \"ext-svc-4538\"\n containerPort: 4538\n protocol: + TCP\n - name: \"ext-svc-4539\"\n containerPort: 4539\n\ + \ protocol: TCP\n - name: \"ext-svc-4540\"\n \ + \ containerPort: 4540\n protocol: TCP\n - name: + \"ext-svc-4541\"\n containerPort: 4541\n protocol: + TCP\n - name: \"ext-svc-4542\"\n containerPort: 4542\n\ + \ protocol: TCP\n - name: \"ext-svc-4543\"\n \ + \ containerPort: 4543\n protocol: TCP\n - name: + \"ext-svc-4544\"\n containerPort: 4544\n protocol: + TCP\n - name: \"ext-svc-4545\"\n containerPort: 4545\n\ + \ protocol: TCP\n - name: \"ext-svc-4546\"\n \ + \ containerPort: 4546\n protocol: TCP\n - name: + \"ext-svc-4547\"\n containerPort: 4547\n protocol: + TCP\n - name: \"ext-svc-4548\"\n containerPort: 4548\n\ + \ protocol: TCP\n - name: \"ext-svc-4549\"\n \ + \ containerPort: 4549\n protocol: TCP\n - name: + \"ext-svc-4550\"\n containerPort: 4550\n protocol: + TCP\n - name: \"ext-svc-4551\"\n containerPort: 4551\n\ + \ protocol: TCP\n - name: \"ext-svc-4552\"\n \ + \ containerPort: 4552\n protocol: TCP\n - name: + \"ext-svc-4553\"\n containerPort: 4553\n protocol: + TCP\n - name: \"ext-svc-4554\"\n containerPort: 4554\n\ + \ protocol: TCP\n - name: \"ext-svc-4555\"\n \ + \ containerPort: 4555\n protocol: TCP\n - name: + \"ext-svc-4556\"\n containerPort: 4556\n protocol: + TCP\n - name: \"ext-svc-4557\"\n containerPort: 4557\n\ + \ protocol: TCP\n - name: \"ext-svc-4558\"\n \ + \ containerPort: 4558\n protocol: TCP\n - name: + \"ext-svc-4559\"\n containerPort: 4559\n protocol: + TCP\n livenessProbe:\n failureThreshold: 3\n \ + \ initialDelaySeconds: 0\n periodSeconds: 10\n successThreshold: + 1\n timeoutSeconds: 1\n httpGet:\n path: + /_localstack/health\n port: edge\n readinessProbe:\n \ + \ failureThreshold: 3\n initialDelaySeconds: 0\n \ + \ periodSeconds: 10\n successThreshold: 1\n timeoutSeconds: + 1\n httpGet:\n path: /_localstack/health\n \ + \ port: edge\n resources: {}\n env:\n - name: + DEBUG\n value: \"0\"\n - name: EXTERNAL_SERVICE_PORTS_START\n\ + \ value: \"4510\"\n - name: EXTERNAL_SERVICE_PORTS_END\n\ + \ value: \"4560\"\n - name: LOCALSTACK_K8S_SERVICE_NAME\n\ + \ value: localstack\n - name: LOCALSTACK_K8S_NAMESPACE\n\ + \ valueFrom:\n fieldRef:\n fieldPath: + metadata.namespace\n - name: LAMBDA_RUNTIME_EXECUTOR\n \ + \ value: \"docker\"\n - name: LAMBDA_K8S_IMAGE_PREFIX\n \ + \ value: \"localstack/lambda-\"\n - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT\n\ + \ value: \"60\"\n - name: OVERRIDE_IN_DOCKER\n \ + \ value: \"1\"\n volumes: []\n" + key: manifests + name: manifests + persist: false + node_selector: + k8s.scaleway.com/pool-name: processing-node-pool + pod_env_vars: + AWS_ACCESS_KEY_ID: test + AWS_DEFAULT_REGION: us-east-1 + AWS_SECRET_ACCESS_KEY: test + CODE_SERVER_WS: /workspace/stac-eoap + CONDARC: /workspace/.condarc + CONDA_ENVS_PATH: /workspace/.envs + CWLTOOL_OPTIONS: --podman + HOME: /workspace + XDG_CONFIG_HOME: /workspace/.local + XDG_DATA_HOME: /workspace/.local/share/ + XDG_RUNTIME_DIR: /workspace/.local + role_bindings: [] + volumes: - access_modes: - ReadWriteOnce claim_name: workspace-claim name: workspace-volume - persist: true - size: 50Gi - storage_class: standard + persist: false + size: 10Gi + storage_class: scw-bssd volume_mount: mount_path: /workspace name: workspace-volume - config_maps: - - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors\ - \ /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram\ - \ 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ - \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\ - # >>> conda initialize >>>\n# !! Contents within this block are managed by 'conda\ - \ init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' 2>\ - \ /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n \ - \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ - \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset\ - \ __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ - \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n" + - content: "set -x \n\ncd /workspace\n\ngit clone 'https://github.com/eoap/quickwin-inline-code.git'\n + \ncode-server --install-extension ms-python.python \ncode-server --install-extension + redhat.vscode-yaml\ncode-server --install-extension sbg-rabix.benten-cwl\ncode-server + --install-extension ms-toolsai.jupyter\n\npip install -r /workspace/quickwin-inline-code/requirements.txt\n + \nln -s /workspace/.local/share/code-server/extensions /workspace/extensions\n + \nmkdir -p /workspace/User/\n\necho '{\"workbench.colorTheme\": \"Visual Studio + Dark\"}' > /workspace/User/settings.json\n\nexit 0" + default_mode: null + key: init + mount_path: /opt/init/.init.sh + name: init + persist: false + readonly: true + - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors + /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram + 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ + \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc + \n# >>> conda initialize >>>\n# !! Contents within this block are managed by + 'conda init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' + 2> /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n\ + \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ + \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset + __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ + \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n\n + export PATH=/workspace/.venv/bin:$PATH" default_mode: null key: bash-rc mount_path: /workspace/.bashrc name: bash-rc - persist: true + persist: false + readonly: true + - content: source /workspace/.bashrc + default_mode: null + key: bash-login + mount_path: /workspace/.bash_login + name: bash-login + persist: false readonly: true default_url: null definition: default: false - description: null - display_name: Code Server Medium + description: This tutorial is designed for developers, scientists, and Earth observation + enthusiasts who want to get started with the EO Application Package. + display_name: Quickwin - An Application Package with inline Python code kubespawner_override: - cpu_guarantee: null - cpu_limit: 4 + cpu_guarantee: 1 + cpu_limit: 2 extra_resource_guarantees: {} extra_resource_limits: {} - image: eoepca/pde-code-server:develop - mem_guarantee: null - mem_limit: 12G - slug: ellip_studio_coder_slug_m + image: + cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 + mem_guarantee: 4G + mem_limit: 6G + slug: ellip_studio_coder_slug_inline groups: - - group-a - - group-b - id: profile_studio_coder2 - image_pull_secrets: [] - init_containers: [] - manifests: null - node_selector: {} + - future + - inline-code + id: profile_studio_coder_inline + image_pull_secrets: + - data: + eyJhdXRocyI6eyJjci50ZXJyYWR1ZS5jb20iOnsidXNlcm5hbWUiOiJyb2JvdCRlbGxpcC1zdHVkaW8iLCJwYXNzd29yZCI6IjIzRlpXSm5PeEtad05RVGVYeWNzb1VnNmxVbTZIeXhTIiwiZW1haWwiOiJlbGxpcC1zdHVkaW9AdGVycmFkdWUuY29tIiwiYXV0aCI6ImNtOWliM1FrWld4c2FYQXRjM1IxWkdsdk9qSXpSbHBYU201UGVFdGFkMDVSVkdWWWVXTnpiMVZuTm14VmJUWkllWGhUIn19fQ== + name: cr-config + persist: false + init_containers: + - command: + - sh + - -c + - sh /opt/init/.init.sh + image: + cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 + name: init-file-on-volume + volume_mounts: + - mount_path: /workspace + name: workspace-volume + - mount_path: /opt/init/.init.sh + name: init + sub_path: init + manifests: + - content: "---\n# Source: localstack/templates/serviceaccount.yaml\napiVersion: + v1\nkind: ServiceAccount\nmetadata:\n name: localstack\n# ---\n# # Source: + localstack/templates/role.yaml\n# apiVersion: rbac.authorization.k8s.io/v1\n + # kind: Role\n# metadata:\n# name: localstack\n# rules:\n# - apiGroups: [\"\ + \"] # \"\" indicates the core API group\n# resources: [\"pods\"]\n# verbs: + [\"*\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/log\"]\n# verbs: [\"\ + get\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/exec\"]\n# verbs: [\"\ + get\", \"create\"]\n# - apiGroups: [\"\"]\n# resources: [\"services\"]\n#\ + \ verbs: [\"get\", \"list\"]\n# ---\n# # Source: localstack/templates/rolebinding.yaml\n + # apiVersion: rbac.authorization.k8s.io/v1\n# kind: RoleBinding\n# metadata:\n\ + # name: localstack\n# subjects:\n# # You can specify more than one \"subject\"\ + \n# - kind: ServiceAccount\n# name: localstack\n# roleRef:\n# kind: Role\n\ + # name: localstack\n# apiGroup: rbac.authorization.k8s.io\n---\n# Source: + localstack/templates/service.yaml\napiVersion: v1\nkind: Service\nmetadata:\n\ + \ name: localstack\n \n \nspec:\n type: ClusterIP\n ports:\n - name: + edge\n port: 4566\n targetPort: 4566\n - name: \"external-service-port-4510\"\ + \n port: 4510\n targetPort: \"ext-svc-4510\"\n - name: \"external-service-port-4511\"\ + \n port: 4511\n targetPort: \"ext-svc-4511\"\n - name: \"external-service-port-4512\"\ + \n port: 4512\n targetPort: \"ext-svc-4512\"\n - name: \"external-service-port-4513\"\ + \n port: 4513\n targetPort: \"ext-svc-4513\"\n - name: \"external-service-port-4514\"\ + \n port: 4514\n targetPort: \"ext-svc-4514\"\n - name: \"external-service-port-4515\"\ + \n port: 4515\n targetPort: \"ext-svc-4515\"\n - name: \"external-service-port-4516\"\ + \n port: 4516\n targetPort: \"ext-svc-4516\"\n - name: \"external-service-port-4517\"\ + \n port: 4517\n targetPort: \"ext-svc-4517\"\n - name: \"external-service-port-4518\"\ + \n port: 4518\n targetPort: \"ext-svc-4518\"\n - name: \"external-service-port-4519\"\ + \n port: 4519\n targetPort: \"ext-svc-4519\"\n - name: \"external-service-port-4520\"\ + \n port: 4520\n targetPort: \"ext-svc-4520\"\n - name: \"external-service-port-4521\"\ + \n port: 4521\n targetPort: \"ext-svc-4521\"\n - name: \"external-service-port-4522\"\ + \n port: 4522\n targetPort: \"ext-svc-4522\"\n - name: \"external-service-port-4523\"\ + \n port: 4523\n targetPort: \"ext-svc-4523\"\n - name: \"external-service-port-4524\"\ + \n port: 4524\n targetPort: \"ext-svc-4524\"\n - name: \"external-service-port-4525\"\ + \n port: 4525\n targetPort: \"ext-svc-4525\"\n - name: \"external-service-port-4526\"\ + \n port: 4526\n targetPort: \"ext-svc-4526\"\n - name: \"external-service-port-4527\"\ + \n port: 4527\n targetPort: \"ext-svc-4527\"\n - name: \"external-service-port-4528\"\ + \n port: 4528\n targetPort: \"ext-svc-4528\"\n - name: \"external-service-port-4529\"\ + \n port: 4529\n targetPort: \"ext-svc-4529\"\n - name: \"external-service-port-4530\"\ + \n port: 4530\n targetPort: \"ext-svc-4530\"\n - name: \"external-service-port-4531\"\ + \n port: 4531\n targetPort: \"ext-svc-4531\"\n - name: \"external-service-port-4532\"\ + \n port: 4532\n targetPort: \"ext-svc-4532\"\n - name: \"external-service-port-4533\"\ + \n port: 4533\n targetPort: \"ext-svc-4533\"\n - name: \"external-service-port-4534\"\ + \n port: 4534\n targetPort: \"ext-svc-4534\"\n - name: \"external-service-port-4535\"\ + \n port: 4535\n targetPort: \"ext-svc-4535\"\n - name: \"external-service-port-4536\"\ + \n port: 4536\n targetPort: \"ext-svc-4536\"\n - name: \"external-service-port-4537\"\ + \n port: 4537\n targetPort: \"ext-svc-4537\"\n - name: \"external-service-port-4538\"\ + \n port: 4538\n targetPort: \"ext-svc-4538\"\n - name: \"external-service-port-4539\"\ + \n port: 4539\n targetPort: \"ext-svc-4539\"\n - name: \"external-service-port-4540\"\ + \n port: 4540\n targetPort: \"ext-svc-4540\"\n - name: \"external-service-port-4541\"\ + \n port: 4541\n targetPort: \"ext-svc-4541\"\n - name: \"external-service-port-4542\"\ + \n port: 4542\n targetPort: \"ext-svc-4542\"\n - name: \"external-service-port-4543\"\ + \n port: 4543\n targetPort: \"ext-svc-4543\"\n - name: \"external-service-port-4544\"\ + \n port: 4544\n targetPort: \"ext-svc-4544\"\n - name: \"external-service-port-4545\"\ + \n port: 4545\n targetPort: \"ext-svc-4545\"\n - name: \"external-service-port-4546\"\ + \n port: 4546\n targetPort: \"ext-svc-4546\"\n - name: \"external-service-port-4547\"\ + \n port: 4547\n targetPort: \"ext-svc-4547\"\n - name: \"external-service-port-4548\"\ + \n port: 4548\n targetPort: \"ext-svc-4548\"\n - name: \"external-service-port-4549\"\ + \n port: 4549\n targetPort: \"ext-svc-4549\"\n - name: \"external-service-port-4550\"\ + \n port: 4550\n targetPort: \"ext-svc-4550\"\n - name: \"external-service-port-4551\"\ + \n port: 4551\n targetPort: \"ext-svc-4551\"\n - name: \"external-service-port-4552\"\ + \n port: 4552\n targetPort: \"ext-svc-4552\"\n - name: \"external-service-port-4553\"\ + \n port: 4553\n targetPort: \"ext-svc-4553\"\n - name: \"external-service-port-4554\"\ + \n port: 4554\n targetPort: \"ext-svc-4554\"\n - name: \"external-service-port-4555\"\ + \n port: 4555\n targetPort: \"ext-svc-4555\"\n - name: \"external-service-port-4556\"\ + \n port: 4556\n targetPort: \"ext-svc-4556\"\n - name: \"external-service-port-4557\"\ + \n port: 4557\n targetPort: \"ext-svc-4557\"\n - name: \"external-service-port-4558\"\ + \n port: 4558\n targetPort: \"ext-svc-4558\"\n - name: \"external-service-port-4559\"\ + \n port: 4559\n targetPort: \"ext-svc-4559\"\n selector:\n app.kubernetes.io/name: + localstack\n app.kubernetes.io/instance: localstack\n---\n# Source: localstack/templates/deployment.yaml\n + apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: localstack\n \n\ + spec:\n replicas: 1\n strategy:\n type: RollingUpdate\n selector:\n \ + \ matchLabels:\n app.kubernetes.io/name: localstack\n app.kubernetes.io/instance: + localstack\n template:\n metadata:\n labels:\n app.kubernetes.io/name: + localstack\n app.kubernetes.io/instance: localstack\n spec:\n \ + \ serviceAccountName: localstack\n securityContext:\n {}\n \ + \ containers:\n - name: localstack\n securityContext:\n \ + \ {}\n image: \"localstack/localstack:latest\"\n imagePullPolicy: + IfNotPresent\n ports:\n - name: edge\n containerPort: + 4566\n protocol: TCP\n - name: \"ext-svc-4510\"\n \ + \ containerPort: 4510\n protocol: TCP\n - + name: \"ext-svc-4511\"\n containerPort: 4511\n protocol: + TCP\n - name: \"ext-svc-4512\"\n containerPort: 4512\n\ + \ protocol: TCP\n - name: \"ext-svc-4513\"\n \ + \ containerPort: 4513\n protocol: TCP\n - name: + \"ext-svc-4514\"\n containerPort: 4514\n protocol: + TCP\n - name: \"ext-svc-4515\"\n containerPort: 4515\n\ + \ protocol: TCP\n - name: \"ext-svc-4516\"\n \ + \ containerPort: 4516\n protocol: TCP\n - name: + \"ext-svc-4517\"\n containerPort: 4517\n protocol: + TCP\n - name: \"ext-svc-4518\"\n containerPort: 4518\n\ + \ protocol: TCP\n - name: \"ext-svc-4519\"\n \ + \ containerPort: 4519\n protocol: TCP\n - name: + \"ext-svc-4520\"\n containerPort: 4520\n protocol: + TCP\n - name: \"ext-svc-4521\"\n containerPort: 4521\n\ + \ protocol: TCP\n - name: \"ext-svc-4522\"\n \ + \ containerPort: 4522\n protocol: TCP\n - name: + \"ext-svc-4523\"\n containerPort: 4523\n protocol: + TCP\n - name: \"ext-svc-4524\"\n containerPort: 4524\n\ + \ protocol: TCP\n - name: \"ext-svc-4525\"\n \ + \ containerPort: 4525\n protocol: TCP\n - name: + \"ext-svc-4526\"\n containerPort: 4526\n protocol: + TCP\n - name: \"ext-svc-4527\"\n containerPort: 4527\n\ + \ protocol: TCP\n - name: \"ext-svc-4528\"\n \ + \ containerPort: 4528\n protocol: TCP\n - name: + \"ext-svc-4529\"\n containerPort: 4529\n protocol: + TCP\n - name: \"ext-svc-4530\"\n containerPort: 4530\n\ + \ protocol: TCP\n - name: \"ext-svc-4531\"\n \ + \ containerPort: 4531\n protocol: TCP\n - name: + \"ext-svc-4532\"\n containerPort: 4532\n protocol: + TCP\n - name: \"ext-svc-4533\"\n containerPort: 4533\n\ + \ protocol: TCP\n - name: \"ext-svc-4534\"\n \ + \ containerPort: 4534\n protocol: TCP\n - name: + \"ext-svc-4535\"\n containerPort: 4535\n protocol: + TCP\n - name: \"ext-svc-4536\"\n containerPort: 4536\n\ + \ protocol: TCP\n - name: \"ext-svc-4537\"\n \ + \ containerPort: 4537\n protocol: TCP\n - name: + \"ext-svc-4538\"\n containerPort: 4538\n protocol: + TCP\n - name: \"ext-svc-4539\"\n containerPort: 4539\n\ + \ protocol: TCP\n - name: \"ext-svc-4540\"\n \ + \ containerPort: 4540\n protocol: TCP\n - name: + \"ext-svc-4541\"\n containerPort: 4541\n protocol: + TCP\n - name: \"ext-svc-4542\"\n containerPort: 4542\n\ + \ protocol: TCP\n - name: \"ext-svc-4543\"\n \ + \ containerPort: 4543\n protocol: TCP\n - name: + \"ext-svc-4544\"\n containerPort: 4544\n protocol: + TCP\n - name: \"ext-svc-4545\"\n containerPort: 4545\n\ + \ protocol: TCP\n - name: \"ext-svc-4546\"\n \ + \ containerPort: 4546\n protocol: TCP\n - name: + \"ext-svc-4547\"\n containerPort: 4547\n protocol: + TCP\n - name: \"ext-svc-4548\"\n containerPort: 4548\n\ + \ protocol: TCP\n - name: \"ext-svc-4549\"\n \ + \ containerPort: 4549\n protocol: TCP\n - name: + \"ext-svc-4550\"\n containerPort: 4550\n protocol: + TCP\n - name: \"ext-svc-4551\"\n containerPort: 4551\n\ + \ protocol: TCP\n - name: \"ext-svc-4552\"\n \ + \ containerPort: 4552\n protocol: TCP\n - name: + \"ext-svc-4553\"\n containerPort: 4553\n protocol: + TCP\n - name: \"ext-svc-4554\"\n containerPort: 4554\n\ + \ protocol: TCP\n - name: \"ext-svc-4555\"\n \ + \ containerPort: 4555\n protocol: TCP\n - name: + \"ext-svc-4556\"\n containerPort: 4556\n protocol: + TCP\n - name: \"ext-svc-4557\"\n containerPort: 4557\n\ + \ protocol: TCP\n - name: \"ext-svc-4558\"\n \ + \ containerPort: 4558\n protocol: TCP\n - name: + \"ext-svc-4559\"\n containerPort: 4559\n protocol: + TCP\n livenessProbe:\n failureThreshold: 3\n \ + \ initialDelaySeconds: 0\n periodSeconds: 10\n successThreshold: + 1\n timeoutSeconds: 1\n httpGet:\n path: + /_localstack/health\n port: edge\n readinessProbe:\n \ + \ failureThreshold: 3\n initialDelaySeconds: 0\n \ + \ periodSeconds: 10\n successThreshold: 1\n timeoutSeconds: + 1\n httpGet:\n path: /_localstack/health\n \ + \ port: edge\n resources: {}\n env:\n - name: + DEBUG\n value: \"0\"\n - name: EXTERNAL_SERVICE_PORTS_START\n\ + \ value: \"4510\"\n - name: EXTERNAL_SERVICE_PORTS_END\n\ + \ value: \"4560\"\n - name: LOCALSTACK_K8S_SERVICE_NAME\n\ + \ value: localstack\n - name: LOCALSTACK_K8S_NAMESPACE\n\ + \ valueFrom:\n fieldRef:\n fieldPath: + metadata.namespace\n - name: LAMBDA_RUNTIME_EXECUTOR\n \ + \ value: \"docker\"\n - name: LAMBDA_K8S_IMAGE_PREFIX\n \ + \ value: \"localstack/lambda-\"\n - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT\n\ + \ value: \"60\"\n - name: OVERRIDE_IN_DOCKER\n \ + \ value: \"1\"\n volumes: []\n" + key: manifests + name: manifests + persist: false + node_selector: + k8s.scaleway.com/pool-name: processing-node-pool pod_env_vars: - CONDA_ENVS_PATH: ' /workspace/.envs' + CODE_SERVER_WS: /workspace/quickwin-inline-code + CONDARC: /workspace/.condarc + CONDA_ENVS_PATH: /workspace/.envs + CWLTOOL_OPTIONS: --podman HOME: /workspace - role_bindings: null + XDG_CONFIG_HOME: /workspace/.local + XDG_DATA_HOME: /workspace/.local/share/ + XDG_RUNTIME_DIR: /workspace/.local + role_bindings: + - name: log-reader-role-binding + persist: false + role: + api_groups: + - '' + name: log-reader-role + resources: + - pods + - pods/log + verbs: + - get + - list + - watch + subjects: + - kind: ServiceAccount + name: default + - name: pod-reader-role-binding + persist: false + role: + api_groups: + - '' + name: pod-manager-role + resources: + - pods + verbs: + - create + - patch + - delete + - list + - watch + subjects: + - kind: ServiceAccount + name: default volumes: - access_modes: - ReadWriteMany @@ -115,7 +838,7 @@ profiles: name: calrissian-volume persist: false size: 50Gi - storage_class: standard + storage_class: openebs-kernel-nfs-scw volume_mount: mount_path: /calrissian name: calrissian-volume @@ -123,68 +846,89 @@ profiles: - ReadWriteOnce claim_name: workspace-claim name: workspace-volume - persist: true - size: 50Gi - storage_class: standard + persist: false + size: 10Gi + storage_class: scw-bssd volume_mount: mount_path: /workspace name: workspace-volume - config_maps: - - content: 'set -x - - - cd /workspace - - - git clone ''https://github.com/eoap/mastering-app-package.git'' - - - code-server --install-extension ms-python.python - - code-server --install-extension redhat.vscode-yaml - - code-server --install-extension sbg-rabix.benten-cwl - - code-server --install-extension ms-toolsai.jupyter - - - ln -s /workspace/.local/share/code-server/extensions /workspace/extensions - - - exit 0 - - ' - default_mode: '0660' + - content: "set -x \n\ncd /workspace\n\ngit clone 'https://github.com/eoap/quickwin.git'\n + \ncode-server --install-extension ms-python.python \ncode-server --install-extension + redhat.vscode-yaml\ncode-server --install-extension sbg-rabix.benten-cwl\ncode-server + --install-extension ms-toolsai.jupyter\n\nln -s /workspace/.local/share/code-server/extensions + /workspace/extensions\n\nmkdir -p /workspace/User/\necho '{\"workbench.colorTheme\"\ + : \"Visual Studio Dark\"}' > /workspace/User/settings.json\n\npython -m venv + /workspace/.venv\nsource /workspace/.venv/bin/activate\n/workspace/.venv/bin/python + -m pip install --no-cache-dir rasterio click pystac loguru pyproj shapely scikit-image + pystac rio_stac ipykernel stactools[validate]\n/workspace/.venv/bin/python -m + ipykernel install --user --name quickwin_env --display-name \"Python (Quickwin)\"\ + \n\nexit 0" + default_mode: null key: init mount_path: /opt/init/.init.sh name: init persist: false readonly: true + - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors + /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram + 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ + \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc + \n# >>> conda initialize >>>\n# !! Contents within this block are managed by + 'conda init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' + 2> /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n\ + \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ + \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset + __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ + \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n\n + export PATH=/workspace/.venv/bin:$PATH" + default_mode: null + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: false + readonly: true + - content: source /workspace/.bashrc + default_mode: null + key: bash-login + mount_path: /workspace/.bash_login + name: bash-login + persist: false + readonly: true default_url: null definition: default: false - description: This profile is used to demonstrate the use of an init script - display_name: Coder demo init script + description: This tutorial is designed for developers, scientists, and Earth observation + enthusiasts who want to get started with the EO Application Package. + display_name: Getting started on Earth Observation Application Packaging with + CWL kubespawner_override: cpu_guarantee: 1 cpu_limit: 2 extra_resource_guarantees: {} extra_resource_limits: {} - image: eoepca/pde-code-server:develop + image: + cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 mem_guarantee: 4G mem_limit: 6G - slug: eoepca_demo_init_script + slug: ellip_studio_coder_slug_quickwin groups: - - group-a - - group-b - id: profile_demo_init_script - image_pull_secrets: [] + - bids23-coder + - bids23-fair + - quickwin + id: profile_studio_coder_quickwin + image_pull_secrets: + - data: + eyJhdXRocyI6eyJjci50ZXJyYWR1ZS5jb20iOnsidXNlcm5hbWUiOiJyb2JvdCRlbGxpcC1zdHVkaW8iLCJwYXNzd29yZCI6IjIzRlpXSm5PeEtad05RVGVYeWNzb1VnNmxVbTZIeXhTIiwiZW1haWwiOiJlbGxpcC1zdHVkaW9AdGVycmFkdWUuY29tIiwiYXV0aCI6ImNtOWliM1FrWld4c2FYQXRjM1IxWkdsdk9qSXpSbHBYU201UGVFdGFkMDVSVkdWWWVXTnpiMVZuTm14VmJUWkllWGhUIn19fQ== + name: cr-config + persist: false init_containers: - command: - sh - -c - sh /opt/init/.init.sh - image: eoepca/pde-code-server:develop + image: + cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 name: init-file-on-volume volume_mounts: - mount_path: /workspace @@ -192,15 +936,565 @@ profiles: - mount_path: /opt/init/.init.sh name: init sub_path: init - manifests: null - node_selector: {} + manifests: + - content: "---\n# Source: localstack/templates/serviceaccount.yaml\napiVersion: + v1\nkind: ServiceAccount\nmetadata:\n name: localstack\n# ---\n# # Source: + localstack/templates/role.yaml\n# apiVersion: rbac.authorization.k8s.io/v1\n + # kind: Role\n# metadata:\n# name: localstack\n# rules:\n# - apiGroups: [\"\ + \"] # \"\" indicates the core API group\n# resources: [\"pods\"]\n# verbs: + [\"*\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/log\"]\n# verbs: [\"\ + get\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/exec\"]\n# verbs: [\"\ + get\", \"create\"]\n# - apiGroups: [\"\"]\n# resources: [\"services\"]\n#\ + \ verbs: [\"get\", \"list\"]\n# ---\n# # Source: localstack/templates/rolebinding.yaml\n + # apiVersion: rbac.authorization.k8s.io/v1\n# kind: RoleBinding\n# metadata:\n\ + # name: localstack\n# subjects:\n# # You can specify more than one \"subject\"\ + \n# - kind: ServiceAccount\n# name: localstack\n# roleRef:\n# kind: Role\n\ + # name: localstack\n# apiGroup: rbac.authorization.k8s.io\n---\n# Source: + localstack/templates/service.yaml\napiVersion: v1\nkind: Service\nmetadata:\n\ + \ name: localstack\n \n \nspec:\n type: ClusterIP\n ports:\n - name: + edge\n port: 4566\n targetPort: 4566\n - name: \"external-service-port-4510\"\ + \n port: 4510\n targetPort: \"ext-svc-4510\"\n - name: \"external-service-port-4511\"\ + \n port: 4511\n targetPort: \"ext-svc-4511\"\n - name: \"external-service-port-4512\"\ + \n port: 4512\n targetPort: \"ext-svc-4512\"\n - name: \"external-service-port-4513\"\ + \n port: 4513\n targetPort: \"ext-svc-4513\"\n - name: \"external-service-port-4514\"\ + \n port: 4514\n targetPort: \"ext-svc-4514\"\n - name: \"external-service-port-4515\"\ + \n port: 4515\n targetPort: \"ext-svc-4515\"\n - name: \"external-service-port-4516\"\ + \n port: 4516\n targetPort: \"ext-svc-4516\"\n - name: \"external-service-port-4517\"\ + \n port: 4517\n targetPort: \"ext-svc-4517\"\n - name: \"external-service-port-4518\"\ + \n port: 4518\n targetPort: \"ext-svc-4518\"\n - name: \"external-service-port-4519\"\ + \n port: 4519\n targetPort: \"ext-svc-4519\"\n - name: \"external-service-port-4520\"\ + \n port: 4520\n targetPort: \"ext-svc-4520\"\n - name: \"external-service-port-4521\"\ + \n port: 4521\n targetPort: \"ext-svc-4521\"\n - name: \"external-service-port-4522\"\ + \n port: 4522\n targetPort: \"ext-svc-4522\"\n - name: \"external-service-port-4523\"\ + \n port: 4523\n targetPort: \"ext-svc-4523\"\n - name: \"external-service-port-4524\"\ + \n port: 4524\n targetPort: \"ext-svc-4524\"\n - name: \"external-service-port-4525\"\ + \n port: 4525\n targetPort: \"ext-svc-4525\"\n - name: \"external-service-port-4526\"\ + \n port: 4526\n targetPort: \"ext-svc-4526\"\n - name: \"external-service-port-4527\"\ + \n port: 4527\n targetPort: \"ext-svc-4527\"\n - name: \"external-service-port-4528\"\ + \n port: 4528\n targetPort: \"ext-svc-4528\"\n - name: \"external-service-port-4529\"\ + \n port: 4529\n targetPort: \"ext-svc-4529\"\n - name: \"external-service-port-4530\"\ + \n port: 4530\n targetPort: \"ext-svc-4530\"\n - name: \"external-service-port-4531\"\ + \n port: 4531\n targetPort: \"ext-svc-4531\"\n - name: \"external-service-port-4532\"\ + \n port: 4532\n targetPort: \"ext-svc-4532\"\n - name: \"external-service-port-4533\"\ + \n port: 4533\n targetPort: \"ext-svc-4533\"\n - name: \"external-service-port-4534\"\ + \n port: 4534\n targetPort: \"ext-svc-4534\"\n - name: \"external-service-port-4535\"\ + \n port: 4535\n targetPort: \"ext-svc-4535\"\n - name: \"external-service-port-4536\"\ + \n port: 4536\n targetPort: \"ext-svc-4536\"\n - name: \"external-service-port-4537\"\ + \n port: 4537\n targetPort: \"ext-svc-4537\"\n - name: \"external-service-port-4538\"\ + \n port: 4538\n targetPort: \"ext-svc-4538\"\n - name: \"external-service-port-4539\"\ + \n port: 4539\n targetPort: \"ext-svc-4539\"\n - name: \"external-service-port-4540\"\ + \n port: 4540\n targetPort: \"ext-svc-4540\"\n - name: \"external-service-port-4541\"\ + \n port: 4541\n targetPort: \"ext-svc-4541\"\n - name: \"external-service-port-4542\"\ + \n port: 4542\n targetPort: \"ext-svc-4542\"\n - name: \"external-service-port-4543\"\ + \n port: 4543\n targetPort: \"ext-svc-4543\"\n - name: \"external-service-port-4544\"\ + \n port: 4544\n targetPort: \"ext-svc-4544\"\n - name: \"external-service-port-4545\"\ + \n port: 4545\n targetPort: \"ext-svc-4545\"\n - name: \"external-service-port-4546\"\ + \n port: 4546\n targetPort: \"ext-svc-4546\"\n - name: \"external-service-port-4547\"\ + \n port: 4547\n targetPort: \"ext-svc-4547\"\n - name: \"external-service-port-4548\"\ + \n port: 4548\n targetPort: \"ext-svc-4548\"\n - name: \"external-service-port-4549\"\ + \n port: 4549\n targetPort: \"ext-svc-4549\"\n - name: \"external-service-port-4550\"\ + \n port: 4550\n targetPort: \"ext-svc-4550\"\n - name: \"external-service-port-4551\"\ + \n port: 4551\n targetPort: \"ext-svc-4551\"\n - name: \"external-service-port-4552\"\ + \n port: 4552\n targetPort: \"ext-svc-4552\"\n - name: \"external-service-port-4553\"\ + \n port: 4553\n targetPort: \"ext-svc-4553\"\n - name: \"external-service-port-4554\"\ + \n port: 4554\n targetPort: \"ext-svc-4554\"\n - name: \"external-service-port-4555\"\ + \n port: 4555\n targetPort: \"ext-svc-4555\"\n - name: \"external-service-port-4556\"\ + \n port: 4556\n targetPort: \"ext-svc-4556\"\n - name: \"external-service-port-4557\"\ + \n port: 4557\n targetPort: \"ext-svc-4557\"\n - name: \"external-service-port-4558\"\ + \n port: 4558\n targetPort: \"ext-svc-4558\"\n - name: \"external-service-port-4559\"\ + \n port: 4559\n targetPort: \"ext-svc-4559\"\n selector:\n app.kubernetes.io/name: + localstack\n app.kubernetes.io/instance: localstack\n---\n# Source: localstack/templates/deployment.yaml\n + apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: localstack\n \n\ + spec:\n replicas: 1\n strategy:\n type: RollingUpdate\n selector:\n \ + \ matchLabels:\n app.kubernetes.io/name: localstack\n app.kubernetes.io/instance: + localstack\n template:\n metadata:\n labels:\n app.kubernetes.io/name: + localstack\n app.kubernetes.io/instance: localstack\n spec:\n \ + \ serviceAccountName: localstack\n securityContext:\n {}\n \ + \ containers:\n - name: localstack\n securityContext:\n \ + \ {}\n image: \"localstack/localstack:latest\"\n imagePullPolicy: + IfNotPresent\n ports:\n - name: edge\n containerPort: + 4566\n protocol: TCP\n - name: \"ext-svc-4510\"\n \ + \ containerPort: 4510\n protocol: TCP\n - + name: \"ext-svc-4511\"\n containerPort: 4511\n protocol: + TCP\n - name: \"ext-svc-4512\"\n containerPort: 4512\n\ + \ protocol: TCP\n - name: \"ext-svc-4513\"\n \ + \ containerPort: 4513\n protocol: TCP\n - name: + \"ext-svc-4514\"\n containerPort: 4514\n protocol: + TCP\n - name: \"ext-svc-4515\"\n containerPort: 4515\n\ + \ protocol: TCP\n - name: \"ext-svc-4516\"\n \ + \ containerPort: 4516\n protocol: TCP\n - name: + \"ext-svc-4517\"\n containerPort: 4517\n protocol: + TCP\n - name: \"ext-svc-4518\"\n containerPort: 4518\n\ + \ protocol: TCP\n - name: \"ext-svc-4519\"\n \ + \ containerPort: 4519\n protocol: TCP\n - name: + \"ext-svc-4520\"\n containerPort: 4520\n protocol: + TCP\n - name: \"ext-svc-4521\"\n containerPort: 4521\n\ + \ protocol: TCP\n - name: \"ext-svc-4522\"\n \ + \ containerPort: 4522\n protocol: TCP\n - name: + \"ext-svc-4523\"\n containerPort: 4523\n protocol: + TCP\n - name: \"ext-svc-4524\"\n containerPort: 4524\n\ + \ protocol: TCP\n - name: \"ext-svc-4525\"\n \ + \ containerPort: 4525\n protocol: TCP\n - name: + \"ext-svc-4526\"\n containerPort: 4526\n protocol: + TCP\n - name: \"ext-svc-4527\"\n containerPort: 4527\n\ + \ protocol: TCP\n - name: \"ext-svc-4528\"\n \ + \ containerPort: 4528\n protocol: TCP\n - name: + \"ext-svc-4529\"\n containerPort: 4529\n protocol: + TCP\n - name: \"ext-svc-4530\"\n containerPort: 4530\n\ + \ protocol: TCP\n - name: \"ext-svc-4531\"\n \ + \ containerPort: 4531\n protocol: TCP\n - name: + \"ext-svc-4532\"\n containerPort: 4532\n protocol: + TCP\n - name: \"ext-svc-4533\"\n containerPort: 4533\n\ + \ protocol: TCP\n - name: \"ext-svc-4534\"\n \ + \ containerPort: 4534\n protocol: TCP\n - name: + \"ext-svc-4535\"\n containerPort: 4535\n protocol: + TCP\n - name: \"ext-svc-4536\"\n containerPort: 4536\n\ + \ protocol: TCP\n - name: \"ext-svc-4537\"\n \ + \ containerPort: 4537\n protocol: TCP\n - name: + \"ext-svc-4538\"\n containerPort: 4538\n protocol: + TCP\n - name: \"ext-svc-4539\"\n containerPort: 4539\n\ + \ protocol: TCP\n - name: \"ext-svc-4540\"\n \ + \ containerPort: 4540\n protocol: TCP\n - name: + \"ext-svc-4541\"\n containerPort: 4541\n protocol: + TCP\n - name: \"ext-svc-4542\"\n containerPort: 4542\n\ + \ protocol: TCP\n - name: \"ext-svc-4543\"\n \ + \ containerPort: 4543\n protocol: TCP\n - name: + \"ext-svc-4544\"\n containerPort: 4544\n protocol: + TCP\n - name: \"ext-svc-4545\"\n containerPort: 4545\n\ + \ protocol: TCP\n - name: \"ext-svc-4546\"\n \ + \ containerPort: 4546\n protocol: TCP\n - name: + \"ext-svc-4547\"\n containerPort: 4547\n protocol: + TCP\n - name: \"ext-svc-4548\"\n containerPort: 4548\n\ + \ protocol: TCP\n - name: \"ext-svc-4549\"\n \ + \ containerPort: 4549\n protocol: TCP\n - name: + \"ext-svc-4550\"\n containerPort: 4550\n protocol: + TCP\n - name: \"ext-svc-4551\"\n containerPort: 4551\n\ + \ protocol: TCP\n - name: \"ext-svc-4552\"\n \ + \ containerPort: 4552\n protocol: TCP\n - name: + \"ext-svc-4553\"\n containerPort: 4553\n protocol: + TCP\n - name: \"ext-svc-4554\"\n containerPort: 4554\n\ + \ protocol: TCP\n - name: \"ext-svc-4555\"\n \ + \ containerPort: 4555\n protocol: TCP\n - name: + \"ext-svc-4556\"\n containerPort: 4556\n protocol: + TCP\n - name: \"ext-svc-4557\"\n containerPort: 4557\n\ + \ protocol: TCP\n - name: \"ext-svc-4558\"\n \ + \ containerPort: 4558\n protocol: TCP\n - name: + \"ext-svc-4559\"\n containerPort: 4559\n protocol: + TCP\n livenessProbe:\n failureThreshold: 3\n \ + \ initialDelaySeconds: 0\n periodSeconds: 10\n successThreshold: + 1\n timeoutSeconds: 1\n httpGet:\n path: + /_localstack/health\n port: edge\n readinessProbe:\n \ + \ failureThreshold: 3\n initialDelaySeconds: 0\n \ + \ periodSeconds: 10\n successThreshold: 1\n timeoutSeconds: + 1\n httpGet:\n path: /_localstack/health\n \ + \ port: edge\n resources: {}\n env:\n - name: + DEBUG\n value: \"0\"\n - name: EXTERNAL_SERVICE_PORTS_START\n\ + \ value: \"4510\"\n - name: EXTERNAL_SERVICE_PORTS_END\n\ + \ value: \"4560\"\n - name: LOCALSTACK_K8S_SERVICE_NAME\n\ + \ value: localstack\n - name: LOCALSTACK_K8S_NAMESPACE\n\ + \ valueFrom:\n fieldRef:\n fieldPath: + metadata.namespace\n - name: LAMBDA_RUNTIME_EXECUTOR\n \ + \ value: \"docker\"\n - name: LAMBDA_K8S_IMAGE_PREFIX\n \ + \ value: \"localstack/lambda-\"\n - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT\n\ + \ value: \"60\"\n - name: OVERRIDE_IN_DOCKER\n \ + \ value: \"1\"\n volumes: []\n" + key: manifests + name: manifests + persist: false + node_selector: + k8s.scaleway.com/pool-name: processing-node-pool + pod_env_vars: + CODE_SERVER_WS: /workspace/quickwin + CONDARC: /workspace/.condarc + CONDA_ENVS_PATH: /workspace/.envs + HOME: /workspace + XDG_CONFIG_HOME: /workspace + XDG_DATA_HOME: /workspace/.local/share/ + XDG_RUNTIME_DIR: /workspace/.local + role_bindings: + - name: log-reader-role-binding + persist: false + role: + api_groups: + - '' + name: log-reader-role + resources: + - pods + - pods/log + verbs: + - get + - list + - watch + subjects: + - kind: ServiceAccount + name: default + - name: pod-reader-role-binding + persist: false + role: + api_groups: + - '' + name: pod-manager-role + resources: + - pods + verbs: + - create + - patch + - delete + - list + - watch + subjects: + - kind: ServiceAccount + name: default + volumes: + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: false + size: 10Gi + storage_class: scw-bssd + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: + - content: "set -x \n\ncd /workspace\n\ngit clone 'https://github.com/eoap/mastering-app-package.git'\n + \ncode-server --install-extension ms-python.python \ncode-server --install-extension + redhat.vscode-yaml\ncode-server --install-extension sbg-rabix.benten-cwl\ncode-server + --install-extension ms-toolsai.jupyter\n\nln -s /workspace/.local/share/code-server/extensions + /workspace/extensions\n\nmkdir -p /workspace/User/\n\necho '{\"workbench.colorTheme\"\ + : \"Visual Studio Dark\"}' > /workspace/User/settings.json\n\npython -m venv + /workspace/.venv\nsource /workspace/.venv/bin/activate\n/workspace/.venv/bin/python + -m pip install --no-cache-dir rasterio click pystac loguru pyproj shapely scikit-image + pystac rio_stac ipykernel stactools[validate]\n/workspace/.venv/bin/python -m + ipykernel install --user --name quickwin_env --display-name \"Python (Quickwin)\"\ + \n\nexit 0" + default_mode: null + key: init + mount_path: /opt/init/.init.sh + name: init + persist: false + readonly: true + - content: "'k8s.scaleway.com/pool-name': 'processing-node-pool'" + default_mode: null + key: node-selector + mount_path: /etc/calrissian/pod-node-selector.yaml + name: node-selector + persist: false + readonly: true + - content: "[plugins]\n endpoint = awscli_plugin_endpoint\n[default]\nregion + = nl-ams\ns3 =\n endpoint_url = https://s3.nl-ams.scw.cloud\n signature_version + = s3v4\n max_concurrent_requests = 100\n max_queue_size = 1000\n multipart_threshold + = 50MB\n # Edit the multipart_chunksize value according to the file sizes + that you want to upload. The present configuration allows to upload files up + to 10 GB (1000 requests * 10MB). For example setting it to 5GB allows you to + upload files up to 5TB.\n multipart_chunksize = 10MB\n addressing_style + = virtual\ns3api =\n endpoint_url = https://s3.nl-ams.scw.cloud" + default_mode: null + key: aws-config + mount_path: /workspace/.aws/config + name: aws-config + persist: true + readonly: true + - content: "[default]\n aws_access_key_id=SCW3KE38CBAS0KV481PZ\n aws_secret_access_key=9092aaa8-13e7-4696-a777-8bd158738d84\n" + default_mode: null + key: aws-credentials + mount_path: /workspace/.aws/credentials + name: aws-credentials + persist: true + readonly: true + - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors + /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram + 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ + \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc + \n# >>> conda initialize >>>\n# !! Contents within this block are managed by + 'conda init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' + 2> /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n\ + \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ + \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset + __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ + \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n\n + export PATH=/workspace/.venv/bin:$PATH" + default_mode: null + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: false + readonly: true + - content: source /workspace/.bashrc + default_mode: null + key: bash-login + mount_path: /workspace/.bash_login + name: bash-login + persist: false + readonly: true + default_url: null + definition: + default: false + description: Dive into the world of EO Application Packages and explore how to + effectively package, share, and execute Earth observation workflows using the + Common Workflow Language (CWL) standard. + display_name: Mastering Earth Observation Application Packaging with CWL + kubespawner_override: + cpu_guarantee: 1 + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: + cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 + mem_guarantee: 4G + mem_limit: 6G + slug: ellip_studio_coder_slug_mastering + groups: + - bids23-coder + - bids23-fair + - mastering + id: profile_studio_coder_mastering + image_pull_secrets: + - data: + eyJhdXRocyI6eyJjci50ZXJyYWR1ZS5jb20iOnsidXNlcm5hbWUiOiJyb2JvdCRlbGxpcC1zdHVkaW8iLCJwYXNzd29yZCI6IjIzRlpXSm5PeEtad05RVGVYeWNzb1VnNmxVbTZIeXhTIiwiZW1haWwiOiJlbGxpcC1zdHVkaW9AdGVycmFkdWUuY29tIiwiYXV0aCI6ImNtOWliM1FrWld4c2FYQXRjM1IxWkdsdk9qSXpSbHBYU201UGVFdGFkMDVSVkdWWWVXTnpiMVZuTm14VmJUWkllWGhUIn19fQ== + name: cr-config + persist: false + init_containers: + - command: + - sh + - -c + - sh /opt/init/.init.sh + image: + cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 + name: init-file-on-volume + volume_mounts: + - mount_path: /workspace + name: workspace-volume + - mount_path: /opt/init/.init.sh + name: init + sub_path: init + manifests: + - content: "---\n# Source: localstack/templates/serviceaccount.yaml\napiVersion: + v1\nkind: ServiceAccount\nmetadata:\n name: localstack\n# ---\n# # Source: + localstack/templates/role.yaml\n# apiVersion: rbac.authorization.k8s.io/v1\n + # kind: Role\n# metadata:\n# name: localstack\n# rules:\n# - apiGroups: [\"\ + \"] # \"\" indicates the core API group\n# resources: [\"pods\"]\n# verbs: + [\"*\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/log\"]\n# verbs: [\"\ + get\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/exec\"]\n# verbs: [\"\ + get\", \"create\"]\n# - apiGroups: [\"\"]\n# resources: [\"services\"]\n#\ + \ verbs: [\"get\", \"list\"]\n# ---\n# # Source: localstack/templates/rolebinding.yaml\n + # apiVersion: rbac.authorization.k8s.io/v1\n# kind: RoleBinding\n# metadata:\n\ + # name: localstack\n# subjects:\n# # You can specify more than one \"subject\"\ + \n# - kind: ServiceAccount\n# name: localstack\n# roleRef:\n# kind: Role\n\ + # name: localstack\n# apiGroup: rbac.authorization.k8s.io\n---\n# Source: + localstack/templates/service.yaml\napiVersion: v1\nkind: Service\nmetadata:\n\ + \ name: localstack\n \n \nspec:\n type: ClusterIP\n ports:\n - name: + edge\n port: 4566\n targetPort: 4566\n - name: \"external-service-port-4510\"\ + \n port: 4510\n targetPort: \"ext-svc-4510\"\n - name: \"external-service-port-4511\"\ + \n port: 4511\n targetPort: \"ext-svc-4511\"\n - name: \"external-service-port-4512\"\ + \n port: 4512\n targetPort: \"ext-svc-4512\"\n - name: \"external-service-port-4513\"\ + \n port: 4513\n targetPort: \"ext-svc-4513\"\n - name: \"external-service-port-4514\"\ + \n port: 4514\n targetPort: \"ext-svc-4514\"\n - name: \"external-service-port-4515\"\ + \n port: 4515\n targetPort: \"ext-svc-4515\"\n - name: \"external-service-port-4516\"\ + \n port: 4516\n targetPort: \"ext-svc-4516\"\n - name: \"external-service-port-4517\"\ + \n port: 4517\n targetPort: \"ext-svc-4517\"\n - name: \"external-service-port-4518\"\ + \n port: 4518\n targetPort: \"ext-svc-4518\"\n - name: \"external-service-port-4519\"\ + \n port: 4519\n targetPort: \"ext-svc-4519\"\n - name: \"external-service-port-4520\"\ + \n port: 4520\n targetPort: \"ext-svc-4520\"\n - name: \"external-service-port-4521\"\ + \n port: 4521\n targetPort: \"ext-svc-4521\"\n - name: \"external-service-port-4522\"\ + \n port: 4522\n targetPort: \"ext-svc-4522\"\n - name: \"external-service-port-4523\"\ + \n port: 4523\n targetPort: \"ext-svc-4523\"\n - name: \"external-service-port-4524\"\ + \n port: 4524\n targetPort: \"ext-svc-4524\"\n - name: \"external-service-port-4525\"\ + \n port: 4525\n targetPort: \"ext-svc-4525\"\n - name: \"external-service-port-4526\"\ + \n port: 4526\n targetPort: \"ext-svc-4526\"\n - name: \"external-service-port-4527\"\ + \n port: 4527\n targetPort: \"ext-svc-4527\"\n - name: \"external-service-port-4528\"\ + \n port: 4528\n targetPort: \"ext-svc-4528\"\n - name: \"external-service-port-4529\"\ + \n port: 4529\n targetPort: \"ext-svc-4529\"\n - name: \"external-service-port-4530\"\ + \n port: 4530\n targetPort: \"ext-svc-4530\"\n - name: \"external-service-port-4531\"\ + \n port: 4531\n targetPort: \"ext-svc-4531\"\n - name: \"external-service-port-4532\"\ + \n port: 4532\n targetPort: \"ext-svc-4532\"\n - name: \"external-service-port-4533\"\ + \n port: 4533\n targetPort: \"ext-svc-4533\"\n - name: \"external-service-port-4534\"\ + \n port: 4534\n targetPort: \"ext-svc-4534\"\n - name: \"external-service-port-4535\"\ + \n port: 4535\n targetPort: \"ext-svc-4535\"\n - name: \"external-service-port-4536\"\ + \n port: 4536\n targetPort: \"ext-svc-4536\"\n - name: \"external-service-port-4537\"\ + \n port: 4537\n targetPort: \"ext-svc-4537\"\n - name: \"external-service-port-4538\"\ + \n port: 4538\n targetPort: \"ext-svc-4538\"\n - name: \"external-service-port-4539\"\ + \n port: 4539\n targetPort: \"ext-svc-4539\"\n - name: \"external-service-port-4540\"\ + \n port: 4540\n targetPort: \"ext-svc-4540\"\n - name: \"external-service-port-4541\"\ + \n port: 4541\n targetPort: \"ext-svc-4541\"\n - name: \"external-service-port-4542\"\ + \n port: 4542\n targetPort: \"ext-svc-4542\"\n - name: \"external-service-port-4543\"\ + \n port: 4543\n targetPort: \"ext-svc-4543\"\n - name: \"external-service-port-4544\"\ + \n port: 4544\n targetPort: \"ext-svc-4544\"\n - name: \"external-service-port-4545\"\ + \n port: 4545\n targetPort: \"ext-svc-4545\"\n - name: \"external-service-port-4546\"\ + \n port: 4546\n targetPort: \"ext-svc-4546\"\n - name: \"external-service-port-4547\"\ + \n port: 4547\n targetPort: \"ext-svc-4547\"\n - name: \"external-service-port-4548\"\ + \n port: 4548\n targetPort: \"ext-svc-4548\"\n - name: \"external-service-port-4549\"\ + \n port: 4549\n targetPort: \"ext-svc-4549\"\n - name: \"external-service-port-4550\"\ + \n port: 4550\n targetPort: \"ext-svc-4550\"\n - name: \"external-service-port-4551\"\ + \n port: 4551\n targetPort: \"ext-svc-4551\"\n - name: \"external-service-port-4552\"\ + \n port: 4552\n targetPort: \"ext-svc-4552\"\n - name: \"external-service-port-4553\"\ + \n port: 4553\n targetPort: \"ext-svc-4553\"\n - name: \"external-service-port-4554\"\ + \n port: 4554\n targetPort: \"ext-svc-4554\"\n - name: \"external-service-port-4555\"\ + \n port: 4555\n targetPort: \"ext-svc-4555\"\n - name: \"external-service-port-4556\"\ + \n port: 4556\n targetPort: \"ext-svc-4556\"\n - name: \"external-service-port-4557\"\ + \n port: 4557\n targetPort: \"ext-svc-4557\"\n - name: \"external-service-port-4558\"\ + \n port: 4558\n targetPort: \"ext-svc-4558\"\n - name: \"external-service-port-4559\"\ + \n port: 4559\n targetPort: \"ext-svc-4559\"\n selector:\n app.kubernetes.io/name: + localstack\n app.kubernetes.io/instance: localstack\n---\n# Source: localstack/templates/deployment.yaml\n + apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: localstack\n \n\ + spec:\n replicas: 1\n strategy:\n type: RollingUpdate\n selector:\n \ + \ matchLabels:\n app.kubernetes.io/name: localstack\n app.kubernetes.io/instance: + localstack\n template:\n metadata:\n labels:\n app.kubernetes.io/name: + localstack\n app.kubernetes.io/instance: localstack\n spec:\n \ + \ serviceAccountName: localstack\n securityContext:\n {}\n \ + \ containers:\n - name: localstack\n securityContext:\n \ + \ {}\n image: \"localstack/localstack:latest\"\n imagePullPolicy: + IfNotPresent\n ports:\n - name: edge\n containerPort: + 4566\n protocol: TCP\n - name: \"ext-svc-4510\"\n \ + \ containerPort: 4510\n protocol: TCP\n - + name: \"ext-svc-4511\"\n containerPort: 4511\n protocol: + TCP\n - name: \"ext-svc-4512\"\n containerPort: 4512\n\ + \ protocol: TCP\n - name: \"ext-svc-4513\"\n \ + \ containerPort: 4513\n protocol: TCP\n - name: + \"ext-svc-4514\"\n containerPort: 4514\n protocol: + TCP\n - name: \"ext-svc-4515\"\n containerPort: 4515\n\ + \ protocol: TCP\n - name: \"ext-svc-4516\"\n \ + \ containerPort: 4516\n protocol: TCP\n - name: + \"ext-svc-4517\"\n containerPort: 4517\n protocol: + TCP\n - name: \"ext-svc-4518\"\n containerPort: 4518\n\ + \ protocol: TCP\n - name: \"ext-svc-4519\"\n \ + \ containerPort: 4519\n protocol: TCP\n - name: + \"ext-svc-4520\"\n containerPort: 4520\n protocol: + TCP\n - name: \"ext-svc-4521\"\n containerPort: 4521\n\ + \ protocol: TCP\n - name: \"ext-svc-4522\"\n \ + \ containerPort: 4522\n protocol: TCP\n - name: + \"ext-svc-4523\"\n containerPort: 4523\n protocol: + TCP\n - name: \"ext-svc-4524\"\n containerPort: 4524\n\ + \ protocol: TCP\n - name: \"ext-svc-4525\"\n \ + \ containerPort: 4525\n protocol: TCP\n - name: + \"ext-svc-4526\"\n containerPort: 4526\n protocol: + TCP\n - name: \"ext-svc-4527\"\n containerPort: 4527\n\ + \ protocol: TCP\n - name: \"ext-svc-4528\"\n \ + \ containerPort: 4528\n protocol: TCP\n - name: + \"ext-svc-4529\"\n containerPort: 4529\n protocol: + TCP\n - name: \"ext-svc-4530\"\n containerPort: 4530\n\ + \ protocol: TCP\n - name: \"ext-svc-4531\"\n \ + \ containerPort: 4531\n protocol: TCP\n - name: + \"ext-svc-4532\"\n containerPort: 4532\n protocol: + TCP\n - name: \"ext-svc-4533\"\n containerPort: 4533\n\ + \ protocol: TCP\n - name: \"ext-svc-4534\"\n \ + \ containerPort: 4534\n protocol: TCP\n - name: + \"ext-svc-4535\"\n containerPort: 4535\n protocol: + TCP\n - name: \"ext-svc-4536\"\n containerPort: 4536\n\ + \ protocol: TCP\n - name: \"ext-svc-4537\"\n \ + \ containerPort: 4537\n protocol: TCP\n - name: + \"ext-svc-4538\"\n containerPort: 4538\n protocol: + TCP\n - name: \"ext-svc-4539\"\n containerPort: 4539\n\ + \ protocol: TCP\n - name: \"ext-svc-4540\"\n \ + \ containerPort: 4540\n protocol: TCP\n - name: + \"ext-svc-4541\"\n containerPort: 4541\n protocol: + TCP\n - name: \"ext-svc-4542\"\n containerPort: 4542\n\ + \ protocol: TCP\n - name: \"ext-svc-4543\"\n \ + \ containerPort: 4543\n protocol: TCP\n - name: + \"ext-svc-4544\"\n containerPort: 4544\n protocol: + TCP\n - name: \"ext-svc-4545\"\n containerPort: 4545\n\ + \ protocol: TCP\n - name: \"ext-svc-4546\"\n \ + \ containerPort: 4546\n protocol: TCP\n - name: + \"ext-svc-4547\"\n containerPort: 4547\n protocol: + TCP\n - name: \"ext-svc-4548\"\n containerPort: 4548\n\ + \ protocol: TCP\n - name: \"ext-svc-4549\"\n \ + \ containerPort: 4549\n protocol: TCP\n - name: + \"ext-svc-4550\"\n containerPort: 4550\n protocol: + TCP\n - name: \"ext-svc-4551\"\n containerPort: 4551\n\ + \ protocol: TCP\n - name: \"ext-svc-4552\"\n \ + \ containerPort: 4552\n protocol: TCP\n - name: + \"ext-svc-4553\"\n containerPort: 4553\n protocol: + TCP\n - name: \"ext-svc-4554\"\n containerPort: 4554\n\ + \ protocol: TCP\n - name: \"ext-svc-4555\"\n \ + \ containerPort: 4555\n protocol: TCP\n - name: + \"ext-svc-4556\"\n containerPort: 4556\n protocol: + TCP\n - name: \"ext-svc-4557\"\n containerPort: 4557\n\ + \ protocol: TCP\n - name: \"ext-svc-4558\"\n \ + \ containerPort: 4558\n protocol: TCP\n - name: + \"ext-svc-4559\"\n containerPort: 4559\n protocol: + TCP\n livenessProbe:\n failureThreshold: 3\n \ + \ initialDelaySeconds: 0\n periodSeconds: 10\n successThreshold: + 1\n timeoutSeconds: 1\n httpGet:\n path: + /_localstack/health\n port: edge\n readinessProbe:\n \ + \ failureThreshold: 3\n initialDelaySeconds: 0\n \ + \ periodSeconds: 10\n successThreshold: 1\n timeoutSeconds: + 1\n httpGet:\n path: /_localstack/health\n \ + \ port: edge\n resources: {}\n env:\n - name: + DEBUG\n value: \"0\"\n - name: EXTERNAL_SERVICE_PORTS_START\n\ + \ value: \"4510\"\n - name: EXTERNAL_SERVICE_PORTS_END\n\ + \ value: \"4560\"\n - name: LOCALSTACK_K8S_SERVICE_NAME\n\ + \ value: localstack\n - name: LOCALSTACK_K8S_NAMESPACE\n\ + \ valueFrom:\n fieldRef:\n fieldPath: + metadata.namespace\n - name: LAMBDA_RUNTIME_EXECUTOR\n \ + \ value: \"docker\"\n - name: LAMBDA_K8S_IMAGE_PREFIX\n \ + \ value: \"localstack/lambda-\"\n - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT\n\ + \ value: \"60\"\n - name: OVERRIDE_IN_DOCKER\n \ + \ value: \"1\"\n volumes: []\n" + key: manifests + name: manifests + persist: false + node_selector: + k8s.scaleway.com/pool-name: processing-node-pool pod_env_vars: CODE_SERVER_WS: /workspace/mastering-app-package CONDARC: /workspace/.condarc CONDA_ENVS_PATH: /workspace/.envs + CWLTOOL_OPTIONS: --podman HOME: /workspace + XDG_CONFIG_HOME: /workspace/.local + XDG_DATA_HOME: /workspace/.local/share/ XDG_RUNTIME_DIR: /workspace/.local - role_bindings: null + role_bindings: + - name: log-reader-role-binding + persist: false + role: + api_groups: + - '' + name: log-reader-role + resources: + - pods + - pods/log + verbs: + - get + - list + - watch + subjects: + - kind: ServiceAccount + name: default + - name: pod-reader-role-binding + persist: false + role: + api_groups: + - '' + name: pod-manager-role + resources: + - pods + verbs: + - create + - patch + - delete + - list + - watch + subjects: + - kind: ServiceAccount + name: default + - name: job-submitter-role-binding + persist: false + role: + api_groups: + - batch + name: job-submitter-role + resources: + - jobs + verbs: + - create + - delete + - list + - watch + - get + subjects: + - kind: ServiceAccount + name: default volumes: - access_modes: - ReadWriteMany @@ -208,7 +1502,7 @@ profiles: name: calrissian-volume persist: false size: 50Gi - storage_class: standard + storage_class: openebs-kernel-nfs-scw volume_mount: mount_path: /calrissian name: calrissian-volume @@ -216,88 +1510,642 @@ profiles: - ReadWriteOnce claim_name: workspace-claim name: workspace-volume - persist: true - size: 50Gi - storage_class: standard + persist: false + size: 10Gi + storage_class: scw-bssd volume_mount: mount_path: /workspace name: workspace-volume -- config_maps: [] +- config_maps: + - content: "set -x \n\ncd /workspace\n\ngit clone 'https://github.com/eoap/open-reproducible-app-package.git'\n + \ncode-server --install-extension ms-python.python \ncode-server --install-extension + ms-toolsai.jupyter\n\nln -s /workspace/.local/share/code-server/extensions /workspace/extensions\n + \nmamba create -y -p /workspace/.envs/e-learn 'python==3.10' pip\n\n/workspace/.envs/e-learn/bin/pip3 + install ipykernel stac-asset pystac pystac-client ipyleaflet pydantic-yaml cwltool + pydantic black[jupyter] rasterio cwl-utils\n\nexit 0" + default_mode: null + key: init + mount_path: /opt/init/.init.sh + name: init + persist: false + readonly: true + - content: "auto_update_conda: false\nshow_channel_urls: true\nchannels:\n - conda-forge\n\ + \ - terradue" + default_mode: null + key: conda-rc + mount_path: /workspace/.condarc + name: conda-rc + persist: false + readonly: true + - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors + /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram + 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ + \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc + \n# >>> conda initialize >>>\n# !! Contents within this block are managed by + 'conda init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' + 2> /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n\ + \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ + \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset + __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ + \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n\n + export PATH=/workspace/.venv/bin:$PATH" + default_mode: null + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: false + readonly: true + - content: source /workspace/.bashrc + default_mode: null + key: bash-login + mount_path: /workspace/.bash_login + name: bash-login + persist: false + readonly: true default_url: null definition: default: false - description: Jupyter Lab with Python 3.11 - display_name: Jupyter Lab + description: Learn how Bob reproduces Alice's Application Package (image pull)) + display_name: Open Reproducible EO Application Package kubespawner_override: cpu_guarantee: 1 cpu_limit: 2 extra_resource_guarantees: {} extra_resource_limits: {} - image: jupyter/scipy-notebook + image: + cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 mem_guarantee: 4G mem_limit: 6G - slug: eoepca_jupyter_lab + slug: ellip_studio_open_reproducible_app_package groups: - - group-c - id: profile_jupyter_lab - image_pull_secrets: [] - init_containers: [] - manifests: null - node_selector: {} + - bids23-coder + - bids23-fair + - reproducible + id: profile_studio_coder_reproducible + image_pull_secrets: + - data: + eyJhdXRocyI6eyJjci50ZXJyYWR1ZS5jb20iOnsidXNlcm5hbWUiOiJyb2JvdCRlbGxpcC1zdHVkaW8iLCJwYXNzd29yZCI6IjIzRlpXSm5PeEtad05RVGVYeWNzb1VnNmxVbTZIeXhTIiwiZW1haWwiOiJlbGxpcC1zdHVkaW9AdGVycmFkdWUuY29tIiwiYXV0aCI6ImNtOWliM1FrWld4c2FYQXRjM1IxWkdsdk9qSXpSbHBYU201UGVFdGFkMDVSVkdWWWVXTnpiMVZuTm14VmJUWkllWGhUIn19fQ== + name: cr-config + persist: false + - data: + ewoJImF1dGhzIjogewoJCSJjci50ZXJyYWR1ZS5jb20iOiB7CgkJCSJhdXRoIjogIlptSnlhWFJ2T21ZNVZFNUNaVTlIVEE9PSIKCQl9Cgl9Cn0= + name: empty-cr-secret + persist: false + init_containers: + - command: + - sh + - -c + - sh /opt/init/.init.sh + image: + cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 + name: init-file-on-volume + volume_mounts: + - mount_path: /workspace + name: workspace-volume + - mount_path: /opt/init/.init.sh + name: init + sub_path: init + manifests: + - content: "---\n# Source: localstack/templates/serviceaccount.yaml\napiVersion: + v1\nkind: ServiceAccount\nmetadata:\n name: localstack\n# ---\n# # Source: + localstack/templates/role.yaml\n# apiVersion: rbac.authorization.k8s.io/v1\n + # kind: Role\n# metadata:\n# name: localstack\n# rules:\n# - apiGroups: [\"\ + \"] # \"\" indicates the core API group\n# resources: [\"pods\"]\n# verbs: + [\"*\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/log\"]\n# verbs: [\"\ + get\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/exec\"]\n# verbs: [\"\ + get\", \"create\"]\n# - apiGroups: [\"\"]\n# resources: [\"services\"]\n#\ + \ verbs: [\"get\", \"list\"]\n# ---\n# # Source: localstack/templates/rolebinding.yaml\n + # apiVersion: rbac.authorization.k8s.io/v1\n# kind: RoleBinding\n# metadata:\n\ + # name: localstack\n# subjects:\n# # You can specify more than one \"subject\"\ + \n# - kind: ServiceAccount\n# name: localstack\n# roleRef:\n# kind: Role\n\ + # name: localstack\n# apiGroup: rbac.authorization.k8s.io\n---\n# Source: + localstack/templates/service.yaml\napiVersion: v1\nkind: Service\nmetadata:\n\ + \ name: localstack\n \n \nspec:\n type: ClusterIP\n ports:\n - name: + edge\n port: 4566\n targetPort: 4566\n - name: \"external-service-port-4510\"\ + \n port: 4510\n targetPort: \"ext-svc-4510\"\n - name: \"external-service-port-4511\"\ + \n port: 4511\n targetPort: \"ext-svc-4511\"\n - name: \"external-service-port-4512\"\ + \n port: 4512\n targetPort: \"ext-svc-4512\"\n - name: \"external-service-port-4513\"\ + \n port: 4513\n targetPort: \"ext-svc-4513\"\n - name: \"external-service-port-4514\"\ + \n port: 4514\n targetPort: \"ext-svc-4514\"\n - name: \"external-service-port-4515\"\ + \n port: 4515\n targetPort: \"ext-svc-4515\"\n - name: \"external-service-port-4516\"\ + \n port: 4516\n targetPort: \"ext-svc-4516\"\n - name: \"external-service-port-4517\"\ + \n port: 4517\n targetPort: \"ext-svc-4517\"\n - name: \"external-service-port-4518\"\ + \n port: 4518\n targetPort: \"ext-svc-4518\"\n - name: \"external-service-port-4519\"\ + \n port: 4519\n targetPort: \"ext-svc-4519\"\n - name: \"external-service-port-4520\"\ + \n port: 4520\n targetPort: \"ext-svc-4520\"\n - name: \"external-service-port-4521\"\ + \n port: 4521\n targetPort: \"ext-svc-4521\"\n - name: \"external-service-port-4522\"\ + \n port: 4522\n targetPort: \"ext-svc-4522\"\n - name: \"external-service-port-4523\"\ + \n port: 4523\n targetPort: \"ext-svc-4523\"\n - name: \"external-service-port-4524\"\ + \n port: 4524\n targetPort: \"ext-svc-4524\"\n - name: \"external-service-port-4525\"\ + \n port: 4525\n targetPort: \"ext-svc-4525\"\n - name: \"external-service-port-4526\"\ + \n port: 4526\n targetPort: \"ext-svc-4526\"\n - name: \"external-service-port-4527\"\ + \n port: 4527\n targetPort: \"ext-svc-4527\"\n - name: \"external-service-port-4528\"\ + \n port: 4528\n targetPort: \"ext-svc-4528\"\n - name: \"external-service-port-4529\"\ + \n port: 4529\n targetPort: \"ext-svc-4529\"\n - name: \"external-service-port-4530\"\ + \n port: 4530\n targetPort: \"ext-svc-4530\"\n - name: \"external-service-port-4531\"\ + \n port: 4531\n targetPort: \"ext-svc-4531\"\n - name: \"external-service-port-4532\"\ + \n port: 4532\n targetPort: \"ext-svc-4532\"\n - name: \"external-service-port-4533\"\ + \n port: 4533\n targetPort: \"ext-svc-4533\"\n - name: \"external-service-port-4534\"\ + \n port: 4534\n targetPort: \"ext-svc-4534\"\n - name: \"external-service-port-4535\"\ + \n port: 4535\n targetPort: \"ext-svc-4535\"\n - name: \"external-service-port-4536\"\ + \n port: 4536\n targetPort: \"ext-svc-4536\"\n - name: \"external-service-port-4537\"\ + \n port: 4537\n targetPort: \"ext-svc-4537\"\n - name: \"external-service-port-4538\"\ + \n port: 4538\n targetPort: \"ext-svc-4538\"\n - name: \"external-service-port-4539\"\ + \n port: 4539\n targetPort: \"ext-svc-4539\"\n - name: \"external-service-port-4540\"\ + \n port: 4540\n targetPort: \"ext-svc-4540\"\n - name: \"external-service-port-4541\"\ + \n port: 4541\n targetPort: \"ext-svc-4541\"\n - name: \"external-service-port-4542\"\ + \n port: 4542\n targetPort: \"ext-svc-4542\"\n - name: \"external-service-port-4543\"\ + \n port: 4543\n targetPort: \"ext-svc-4543\"\n - name: \"external-service-port-4544\"\ + \n port: 4544\n targetPort: \"ext-svc-4544\"\n - name: \"external-service-port-4545\"\ + \n port: 4545\n targetPort: \"ext-svc-4545\"\n - name: \"external-service-port-4546\"\ + \n port: 4546\n targetPort: \"ext-svc-4546\"\n - name: \"external-service-port-4547\"\ + \n port: 4547\n targetPort: \"ext-svc-4547\"\n - name: \"external-service-port-4548\"\ + \n port: 4548\n targetPort: \"ext-svc-4548\"\n - name: \"external-service-port-4549\"\ + \n port: 4549\n targetPort: \"ext-svc-4549\"\n - name: \"external-service-port-4550\"\ + \n port: 4550\n targetPort: \"ext-svc-4550\"\n - name: \"external-service-port-4551\"\ + \n port: 4551\n targetPort: \"ext-svc-4551\"\n - name: \"external-service-port-4552\"\ + \n port: 4552\n targetPort: \"ext-svc-4552\"\n - name: \"external-service-port-4553\"\ + \n port: 4553\n targetPort: \"ext-svc-4553\"\n - name: \"external-service-port-4554\"\ + \n port: 4554\n targetPort: \"ext-svc-4554\"\n - name: \"external-service-port-4555\"\ + \n port: 4555\n targetPort: \"ext-svc-4555\"\n - name: \"external-service-port-4556\"\ + \n port: 4556\n targetPort: \"ext-svc-4556\"\n - name: \"external-service-port-4557\"\ + \n port: 4557\n targetPort: \"ext-svc-4557\"\n - name: \"external-service-port-4558\"\ + \n port: 4558\n targetPort: \"ext-svc-4558\"\n - name: \"external-service-port-4559\"\ + \n port: 4559\n targetPort: \"ext-svc-4559\"\n selector:\n app.kubernetes.io/name: + localstack\n app.kubernetes.io/instance: localstack\n---\n# Source: localstack/templates/deployment.yaml\n + apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: localstack\n \n\ + spec:\n replicas: 1\n strategy:\n type: RollingUpdate\n selector:\n \ + \ matchLabels:\n app.kubernetes.io/name: localstack\n app.kubernetes.io/instance: + localstack\n template:\n metadata:\n labels:\n app.kubernetes.io/name: + localstack\n app.kubernetes.io/instance: localstack\n spec:\n \ + \ serviceAccountName: localstack\n securityContext:\n {}\n \ + \ containers:\n - name: localstack\n securityContext:\n \ + \ {}\n image: \"localstack/localstack:latest\"\n imagePullPolicy: + IfNotPresent\n ports:\n - name: edge\n containerPort: + 4566\n protocol: TCP\n - name: \"ext-svc-4510\"\n \ + \ containerPort: 4510\n protocol: TCP\n - + name: \"ext-svc-4511\"\n containerPort: 4511\n protocol: + TCP\n - name: \"ext-svc-4512\"\n containerPort: 4512\n\ + \ protocol: TCP\n - name: \"ext-svc-4513\"\n \ + \ containerPort: 4513\n protocol: TCP\n - name: + \"ext-svc-4514\"\n containerPort: 4514\n protocol: + TCP\n - name: \"ext-svc-4515\"\n containerPort: 4515\n\ + \ protocol: TCP\n - name: \"ext-svc-4516\"\n \ + \ containerPort: 4516\n protocol: TCP\n - name: + \"ext-svc-4517\"\n containerPort: 4517\n protocol: + TCP\n - name: \"ext-svc-4518\"\n containerPort: 4518\n\ + \ protocol: TCP\n - name: \"ext-svc-4519\"\n \ + \ containerPort: 4519\n protocol: TCP\n - name: + \"ext-svc-4520\"\n containerPort: 4520\n protocol: + TCP\n - name: \"ext-svc-4521\"\n containerPort: 4521\n\ + \ protocol: TCP\n - name: \"ext-svc-4522\"\n \ + \ containerPort: 4522\n protocol: TCP\n - name: + \"ext-svc-4523\"\n containerPort: 4523\n protocol: + TCP\n - name: \"ext-svc-4524\"\n containerPort: 4524\n\ + \ protocol: TCP\n - name: \"ext-svc-4525\"\n \ + \ containerPort: 4525\n protocol: TCP\n - name: + \"ext-svc-4526\"\n containerPort: 4526\n protocol: + TCP\n - name: \"ext-svc-4527\"\n containerPort: 4527\n\ + \ protocol: TCP\n - name: \"ext-svc-4528\"\n \ + \ containerPort: 4528\n protocol: TCP\n - name: + \"ext-svc-4529\"\n containerPort: 4529\n protocol: + TCP\n - name: \"ext-svc-4530\"\n containerPort: 4530\n\ + \ protocol: TCP\n - name: \"ext-svc-4531\"\n \ + \ containerPort: 4531\n protocol: TCP\n - name: + \"ext-svc-4532\"\n containerPort: 4532\n protocol: + TCP\n - name: \"ext-svc-4533\"\n containerPort: 4533\n\ + \ protocol: TCP\n - name: \"ext-svc-4534\"\n \ + \ containerPort: 4534\n protocol: TCP\n - name: + \"ext-svc-4535\"\n containerPort: 4535\n protocol: + TCP\n - name: \"ext-svc-4536\"\n containerPort: 4536\n\ + \ protocol: TCP\n - name: \"ext-svc-4537\"\n \ + \ containerPort: 4537\n protocol: TCP\n - name: + \"ext-svc-4538\"\n containerPort: 4538\n protocol: + TCP\n - name: \"ext-svc-4539\"\n containerPort: 4539\n\ + \ protocol: TCP\n - name: \"ext-svc-4540\"\n \ + \ containerPort: 4540\n protocol: TCP\n - name: + \"ext-svc-4541\"\n containerPort: 4541\n protocol: + TCP\n - name: \"ext-svc-4542\"\n containerPort: 4542\n\ + \ protocol: TCP\n - name: \"ext-svc-4543\"\n \ + \ containerPort: 4543\n protocol: TCP\n - name: + \"ext-svc-4544\"\n containerPort: 4544\n protocol: + TCP\n - name: \"ext-svc-4545\"\n containerPort: 4545\n\ + \ protocol: TCP\n - name: \"ext-svc-4546\"\n \ + \ containerPort: 4546\n protocol: TCP\n - name: + \"ext-svc-4547\"\n containerPort: 4547\n protocol: + TCP\n - name: \"ext-svc-4548\"\n containerPort: 4548\n\ + \ protocol: TCP\n - name: \"ext-svc-4549\"\n \ + \ containerPort: 4549\n protocol: TCP\n - name: + \"ext-svc-4550\"\n containerPort: 4550\n protocol: + TCP\n - name: \"ext-svc-4551\"\n containerPort: 4551\n\ + \ protocol: TCP\n - name: \"ext-svc-4552\"\n \ + \ containerPort: 4552\n protocol: TCP\n - name: + \"ext-svc-4553\"\n containerPort: 4553\n protocol: + TCP\n - name: \"ext-svc-4554\"\n containerPort: 4554\n\ + \ protocol: TCP\n - name: \"ext-svc-4555\"\n \ + \ containerPort: 4555\n protocol: TCP\n - name: + \"ext-svc-4556\"\n containerPort: 4556\n protocol: + TCP\n - name: \"ext-svc-4557\"\n containerPort: 4557\n\ + \ protocol: TCP\n - name: \"ext-svc-4558\"\n \ + \ containerPort: 4558\n protocol: TCP\n - name: + \"ext-svc-4559\"\n containerPort: 4559\n protocol: + TCP\n livenessProbe:\n failureThreshold: 3\n \ + \ initialDelaySeconds: 0\n periodSeconds: 10\n successThreshold: + 1\n timeoutSeconds: 1\n httpGet:\n path: + /_localstack/health\n port: edge\n readinessProbe:\n \ + \ failureThreshold: 3\n initialDelaySeconds: 0\n \ + \ periodSeconds: 10\n successThreshold: 1\n timeoutSeconds: + 1\n httpGet:\n path: /_localstack/health\n \ + \ port: edge\n resources: {}\n env:\n - name: + DEBUG\n value: \"0\"\n - name: EXTERNAL_SERVICE_PORTS_START\n\ + \ value: \"4510\"\n - name: EXTERNAL_SERVICE_PORTS_END\n\ + \ value: \"4560\"\n - name: LOCALSTACK_K8S_SERVICE_NAME\n\ + \ value: localstack\n - name: LOCALSTACK_K8S_NAMESPACE\n\ + \ valueFrom:\n fieldRef:\n fieldPath: + metadata.namespace\n - name: LAMBDA_RUNTIME_EXECUTOR\n \ + \ value: \"docker\"\n - name: LAMBDA_K8S_IMAGE_PREFIX\n \ + \ value: \"localstack/lambda-\"\n - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT\n\ + \ value: \"60\"\n - name: OVERRIDE_IN_DOCKER\n \ + \ value: \"1\"\n volumes: []\n" + key: manifests + name: manifests + persist: false + node_selector: + k8s.scaleway.com/pool-name: processing-node-pool pod_env_vars: + CODE_SERVER_WS: /workspace/open-reproducible-app-package + CONDARC: /workspace/.condarc + CONDA_ENVS_PATH: /workspace/.envs HOME: /workspace - XDG_CONFIG_HOME: /workspace/.config + XDG_CONFIG_HOME: /workspace/.local + XDG_DATA_HOME: /workspace/.local/share/ XDG_RUNTIME_DIR: /workspace/.local - role_bindings: null + role_bindings: + - name: log-reader-role-binding + persist: false + role: + api_groups: + - '' + name: log-reader-role + resources: + - pods + - pods/log + verbs: + - get + - list + - watch + subjects: + - kind: ServiceAccount + name: default + - name: pod-reader-role-binding + persist: false + role: + api_groups: + - '' + name: pod-manager-role + resources: + - pods + verbs: + - create + - patch + - delete + - list + - watch + subjects: + - kind: ServiceAccount + name: default + - name: secret-patcher-role-binding + persist: false + role: + api_groups: + - '' + name: secret-patcher-role + resources: + - secrets + verbs: + - create + - delete + subjects: + - kind: ServiceAccount + name: default volumes: - access_modes: - ReadWriteOnce claim_name: workspace-claim name: workspace-volume - persist: true - size: 50Gi - storage_class: standard + persist: false + size: 10Gi + storage_class: scw-bssd volume_mount: mount_path: /workspace name: workspace-volume -- config_maps: [] +- config_maps: + - content: "set -x \n\ncd /workspace\n\ngit clone 'https://github.com/eoap/inference-eoap.git'\n + \ncode-server --install-extension ms-python.python \ncode-server --install-extension + redhat.vscode-yaml\ncode-server --install-extension sbg-rabix.benten-cwl\ncode-server + --install-extension ms-toolsai.jupyter\n\npip install -r /workspace/inference-eoap/requirements.txt\n + pip install stactools[validate]\n\nln -s /workspace/.local/share/code-server/extensions + /workspace/extensions\n\nmkdir -p /workspace/User/\n\necho '{\"workbench.colorTheme\"\ + : \"Visual Studio Dark\"}' > /workspace/User/settings.json\n\nexit 0" + default_mode: null + key: init + mount_path: /opt/init/.init.sh + name: init + persist: false + readonly: true + - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors + /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram + 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ + \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc + \n# >>> conda initialize >>>\n# !! Contents within this block are managed by + 'conda init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' + 2> /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n\ + \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ + \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset + __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ + \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n\n + export PATH=\"/workspace/inference-eoap/bin:$PATH\"" + default_mode: null + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: false + readonly: true + - content: source /workspace/.bashrc + default_mode: null + key: bash-login + mount_path: /workspace/.bash_login + name: bash-login + persist: false + readonly: true default_url: null definition: default: false - description: Jupyter Lab with Python 3.11 - demoes the use of an image pull secret - display_name: Jupyter Lab - profile 2 + description: This tutorial is designed for developers, scientists, and Earth observation + enthusiasts who want to get started with the EO Application Package for Machine + Learning inference. + display_name: Inference with Earth Observation Application Package kubespawner_override: - cpu_guarantee: 1 - cpu_limit: 2 + cpu_guarantee: 3 + cpu_limit: 3 extra_resource_guarantees: {} extra_resource_limits: {} - image: jupyter/scipy-notebook - mem_guarantee: 4G - mem_limit: 6G - slug: eoepca_jupyter_lab_2 + image: + cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 + mem_guarantee: 14G + mem_limit: 14G + slug: ellip_studio_coder_slug_inference groups: - - group-c - id: profile_jupyter_lab_2 + - ai-inference + id: profile_studio_coder_inference image_pull_secrets: - - data: '' + - data: + eyJhdXRocyI6eyJjci50ZXJyYWR1ZS5jb20iOnsidXNlcm5hbWUiOiJyb2JvdCRlbGxpcC1zdHVkaW8iLCJwYXNzd29yZCI6IjIzRlpXSm5PeEtad05RVGVYeWNzb1VnNmxVbTZIeXhTIiwiZW1haWwiOiJlbGxpcC1zdHVkaW9AdGVycmFkdWUuY29tIiwiYXV0aCI6ImNtOWliM1FrWld4c2FYQXRjM1IxWkdsdk9qSXpSbHBYU201UGVFdGFkMDVSVkdWWWVXTnpiMVZuTm14VmJUWkllWGhUIn19fQ== name: cr-config persist: false - init_containers: [] - manifests: null - node_selector: {} + init_containers: + - command: + - sh + - -c + - sh /opt/init/.init.sh + image: + cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 + name: init-file-on-volume + volume_mounts: + - mount_path: /workspace + name: workspace-volume + - mount_path: /opt/init/.init.sh + name: init + sub_path: init + manifests: + - content: "---\n# Source: localstack/templates/serviceaccount.yaml\napiVersion: + v1\nkind: ServiceAccount\nmetadata:\n name: localstack\n# ---\n# # Source: + localstack/templates/role.yaml\n# apiVersion: rbac.authorization.k8s.io/v1\n + # kind: Role\n# metadata:\n# name: localstack\n# rules:\n# - apiGroups: [\"\ + \"] # \"\" indicates the core API group\n# resources: [\"pods\"]\n# verbs: + [\"*\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/log\"]\n# verbs: [\"\ + get\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/exec\"]\n# verbs: [\"\ + get\", \"create\"]\n# - apiGroups: [\"\"]\n# resources: [\"services\"]\n#\ + \ verbs: [\"get\", \"list\"]\n# ---\n# # Source: localstack/templates/rolebinding.yaml\n + # apiVersion: rbac.authorization.k8s.io/v1\n# kind: RoleBinding\n# metadata:\n\ + # name: localstack\n# subjects:\n# # You can specify more than one \"subject\"\ + \n# - kind: ServiceAccount\n# name: localstack\n# roleRef:\n# kind: Role\n\ + # name: localstack\n# apiGroup: rbac.authorization.k8s.io\n---\n# Source: + localstack/templates/service.yaml\napiVersion: v1\nkind: Service\nmetadata:\n\ + \ name: localstack\n \n \nspec:\n type: ClusterIP\n ports:\n - name: + edge\n port: 4566\n targetPort: 4566\n - name: \"external-service-port-4510\"\ + \n port: 4510\n targetPort: \"ext-svc-4510\"\n - name: \"external-service-port-4511\"\ + \n port: 4511\n targetPort: \"ext-svc-4511\"\n - name: \"external-service-port-4512\"\ + \n port: 4512\n targetPort: \"ext-svc-4512\"\n - name: \"external-service-port-4513\"\ + \n port: 4513\n targetPort: \"ext-svc-4513\"\n - name: \"external-service-port-4514\"\ + \n port: 4514\n targetPort: \"ext-svc-4514\"\n - name: \"external-service-port-4515\"\ + \n port: 4515\n targetPort: \"ext-svc-4515\"\n - name: \"external-service-port-4516\"\ + \n port: 4516\n targetPort: \"ext-svc-4516\"\n - name: \"external-service-port-4517\"\ + \n port: 4517\n targetPort: \"ext-svc-4517\"\n - name: \"external-service-port-4518\"\ + \n port: 4518\n targetPort: \"ext-svc-4518\"\n - name: \"external-service-port-4519\"\ + \n port: 4519\n targetPort: \"ext-svc-4519\"\n - name: \"external-service-port-4520\"\ + \n port: 4520\n targetPort: \"ext-svc-4520\"\n - name: \"external-service-port-4521\"\ + \n port: 4521\n targetPort: \"ext-svc-4521\"\n - name: \"external-service-port-4522\"\ + \n port: 4522\n targetPort: \"ext-svc-4522\"\n - name: \"external-service-port-4523\"\ + \n port: 4523\n targetPort: \"ext-svc-4523\"\n - name: \"external-service-port-4524\"\ + \n port: 4524\n targetPort: \"ext-svc-4524\"\n - name: \"external-service-port-4525\"\ + \n port: 4525\n targetPort: \"ext-svc-4525\"\n - name: \"external-service-port-4526\"\ + \n port: 4526\n targetPort: \"ext-svc-4526\"\n - name: \"external-service-port-4527\"\ + \n port: 4527\n targetPort: \"ext-svc-4527\"\n - name: \"external-service-port-4528\"\ + \n port: 4528\n targetPort: \"ext-svc-4528\"\n - name: \"external-service-port-4529\"\ + \n port: 4529\n targetPort: \"ext-svc-4529\"\n - name: \"external-service-port-4530\"\ + \n port: 4530\n targetPort: \"ext-svc-4530\"\n - name: \"external-service-port-4531\"\ + \n port: 4531\n targetPort: \"ext-svc-4531\"\n - name: \"external-service-port-4532\"\ + \n port: 4532\n targetPort: \"ext-svc-4532\"\n - name: \"external-service-port-4533\"\ + \n port: 4533\n targetPort: \"ext-svc-4533\"\n - name: \"external-service-port-4534\"\ + \n port: 4534\n targetPort: \"ext-svc-4534\"\n - name: \"external-service-port-4535\"\ + \n port: 4535\n targetPort: \"ext-svc-4535\"\n - name: \"external-service-port-4536\"\ + \n port: 4536\n targetPort: \"ext-svc-4536\"\n - name: \"external-service-port-4537\"\ + \n port: 4537\n targetPort: \"ext-svc-4537\"\n - name: \"external-service-port-4538\"\ + \n port: 4538\n targetPort: \"ext-svc-4538\"\n - name: \"external-service-port-4539\"\ + \n port: 4539\n targetPort: \"ext-svc-4539\"\n - name: \"external-service-port-4540\"\ + \n port: 4540\n targetPort: \"ext-svc-4540\"\n - name: \"external-service-port-4541\"\ + \n port: 4541\n targetPort: \"ext-svc-4541\"\n - name: \"external-service-port-4542\"\ + \n port: 4542\n targetPort: \"ext-svc-4542\"\n - name: \"external-service-port-4543\"\ + \n port: 4543\n targetPort: \"ext-svc-4543\"\n - name: \"external-service-port-4544\"\ + \n port: 4544\n targetPort: \"ext-svc-4544\"\n - name: \"external-service-port-4545\"\ + \n port: 4545\n targetPort: \"ext-svc-4545\"\n - name: \"external-service-port-4546\"\ + \n port: 4546\n targetPort: \"ext-svc-4546\"\n - name: \"external-service-port-4547\"\ + \n port: 4547\n targetPort: \"ext-svc-4547\"\n - name: \"external-service-port-4548\"\ + \n port: 4548\n targetPort: \"ext-svc-4548\"\n - name: \"external-service-port-4549\"\ + \n port: 4549\n targetPort: \"ext-svc-4549\"\n - name: \"external-service-port-4550\"\ + \n port: 4550\n targetPort: \"ext-svc-4550\"\n - name: \"external-service-port-4551\"\ + \n port: 4551\n targetPort: \"ext-svc-4551\"\n - name: \"external-service-port-4552\"\ + \n port: 4552\n targetPort: \"ext-svc-4552\"\n - name: \"external-service-port-4553\"\ + \n port: 4553\n targetPort: \"ext-svc-4553\"\n - name: \"external-service-port-4554\"\ + \n port: 4554\n targetPort: \"ext-svc-4554\"\n - name: \"external-service-port-4555\"\ + \n port: 4555\n targetPort: \"ext-svc-4555\"\n - name: \"external-service-port-4556\"\ + \n port: 4556\n targetPort: \"ext-svc-4556\"\n - name: \"external-service-port-4557\"\ + \n port: 4557\n targetPort: \"ext-svc-4557\"\n - name: \"external-service-port-4558\"\ + \n port: 4558\n targetPort: \"ext-svc-4558\"\n - name: \"external-service-port-4559\"\ + \n port: 4559\n targetPort: \"ext-svc-4559\"\n selector:\n app.kubernetes.io/name: + localstack\n app.kubernetes.io/instance: localstack\n---\n# Source: localstack/templates/deployment.yaml\n + apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: localstack\n \n\ + spec:\n replicas: 1\n strategy:\n type: RollingUpdate\n selector:\n \ + \ matchLabels:\n app.kubernetes.io/name: localstack\n app.kubernetes.io/instance: + localstack\n template:\n metadata:\n labels:\n app.kubernetes.io/name: + localstack\n app.kubernetes.io/instance: localstack\n spec:\n \ + \ serviceAccountName: localstack\n securityContext:\n {}\n \ + \ containers:\n - name: localstack\n securityContext:\n \ + \ {}\n image: \"localstack/localstack:latest\"\n imagePullPolicy: + IfNotPresent\n ports:\n - name: edge\n containerPort: + 4566\n protocol: TCP\n - name: \"ext-svc-4510\"\n \ + \ containerPort: 4510\n protocol: TCP\n - + name: \"ext-svc-4511\"\n containerPort: 4511\n protocol: + TCP\n - name: \"ext-svc-4512\"\n containerPort: 4512\n\ + \ protocol: TCP\n - name: \"ext-svc-4513\"\n \ + \ containerPort: 4513\n protocol: TCP\n - name: + \"ext-svc-4514\"\n containerPort: 4514\n protocol: + TCP\n - name: \"ext-svc-4515\"\n containerPort: 4515\n\ + \ protocol: TCP\n - name: \"ext-svc-4516\"\n \ + \ containerPort: 4516\n protocol: TCP\n - name: + \"ext-svc-4517\"\n containerPort: 4517\n protocol: + TCP\n - name: \"ext-svc-4518\"\n containerPort: 4518\n\ + \ protocol: TCP\n - name: \"ext-svc-4519\"\n \ + \ containerPort: 4519\n protocol: TCP\n - name: + \"ext-svc-4520\"\n containerPort: 4520\n protocol: + TCP\n - name: \"ext-svc-4521\"\n containerPort: 4521\n\ + \ protocol: TCP\n - name: \"ext-svc-4522\"\n \ + \ containerPort: 4522\n protocol: TCP\n - name: + \"ext-svc-4523\"\n containerPort: 4523\n protocol: + TCP\n - name: \"ext-svc-4524\"\n containerPort: 4524\n\ + \ protocol: TCP\n - name: \"ext-svc-4525\"\n \ + \ containerPort: 4525\n protocol: TCP\n - name: + \"ext-svc-4526\"\n containerPort: 4526\n protocol: + TCP\n - name: \"ext-svc-4527\"\n containerPort: 4527\n\ + \ protocol: TCP\n - name: \"ext-svc-4528\"\n \ + \ containerPort: 4528\n protocol: TCP\n - name: + \"ext-svc-4529\"\n containerPort: 4529\n protocol: + TCP\n - name: \"ext-svc-4530\"\n containerPort: 4530\n\ + \ protocol: TCP\n - name: \"ext-svc-4531\"\n \ + \ containerPort: 4531\n protocol: TCP\n - name: + \"ext-svc-4532\"\n containerPort: 4532\n protocol: + TCP\n - name: \"ext-svc-4533\"\n containerPort: 4533\n\ + \ protocol: TCP\n - name: \"ext-svc-4534\"\n \ + \ containerPort: 4534\n protocol: TCP\n - name: + \"ext-svc-4535\"\n containerPort: 4535\n protocol: + TCP\n - name: \"ext-svc-4536\"\n containerPort: 4536\n\ + \ protocol: TCP\n - name: \"ext-svc-4537\"\n \ + \ containerPort: 4537\n protocol: TCP\n - name: + \"ext-svc-4538\"\n containerPort: 4538\n protocol: + TCP\n - name: \"ext-svc-4539\"\n containerPort: 4539\n\ + \ protocol: TCP\n - name: \"ext-svc-4540\"\n \ + \ containerPort: 4540\n protocol: TCP\n - name: + \"ext-svc-4541\"\n containerPort: 4541\n protocol: + TCP\n - name: \"ext-svc-4542\"\n containerPort: 4542\n\ + \ protocol: TCP\n - name: \"ext-svc-4543\"\n \ + \ containerPort: 4543\n protocol: TCP\n - name: + \"ext-svc-4544\"\n containerPort: 4544\n protocol: + TCP\n - name: \"ext-svc-4545\"\n containerPort: 4545\n\ + \ protocol: TCP\n - name: \"ext-svc-4546\"\n \ + \ containerPort: 4546\n protocol: TCP\n - name: + \"ext-svc-4547\"\n containerPort: 4547\n protocol: + TCP\n - name: \"ext-svc-4548\"\n containerPort: 4548\n\ + \ protocol: TCP\n - name: \"ext-svc-4549\"\n \ + \ containerPort: 4549\n protocol: TCP\n - name: + \"ext-svc-4550\"\n containerPort: 4550\n protocol: + TCP\n - name: \"ext-svc-4551\"\n containerPort: 4551\n\ + \ protocol: TCP\n - name: \"ext-svc-4552\"\n \ + \ containerPort: 4552\n protocol: TCP\n - name: + \"ext-svc-4553\"\n containerPort: 4553\n protocol: + TCP\n - name: \"ext-svc-4554\"\n containerPort: 4554\n\ + \ protocol: TCP\n - name: \"ext-svc-4555\"\n \ + \ containerPort: 4555\n protocol: TCP\n - name: + \"ext-svc-4556\"\n containerPort: 4556\n protocol: + TCP\n - name: \"ext-svc-4557\"\n containerPort: 4557\n\ + \ protocol: TCP\n - name: \"ext-svc-4558\"\n \ + \ containerPort: 4558\n protocol: TCP\n - name: + \"ext-svc-4559\"\n containerPort: 4559\n protocol: + TCP\n livenessProbe:\n failureThreshold: 3\n \ + \ initialDelaySeconds: 0\n periodSeconds: 10\n successThreshold: + 1\n timeoutSeconds: 1\n httpGet:\n path: + /_localstack/health\n port: edge\n readinessProbe:\n \ + \ failureThreshold: 3\n initialDelaySeconds: 0\n \ + \ periodSeconds: 10\n successThreshold: 1\n timeoutSeconds: + 1\n httpGet:\n path: /_localstack/health\n \ + \ port: edge\n resources: {}\n env:\n - name: + DEBUG\n value: \"0\"\n - name: EXTERNAL_SERVICE_PORTS_START\n\ + \ value: \"4510\"\n - name: EXTERNAL_SERVICE_PORTS_END\n\ + \ value: \"4560\"\n - name: LOCALSTACK_K8S_SERVICE_NAME\n\ + \ value: localstack\n - name: LOCALSTACK_K8S_NAMESPACE\n\ + \ valueFrom:\n fieldRef:\n fieldPath: + metadata.namespace\n - name: LAMBDA_RUNTIME_EXECUTOR\n \ + \ value: \"docker\"\n - name: LAMBDA_K8S_IMAGE_PREFIX\n \ + \ value: \"localstack/lambda-\"\n - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT\n\ + \ value: \"60\"\n - name: OVERRIDE_IN_DOCKER\n \ + \ value: \"1\"\n volumes: []\n" + key: manifests + name: manifests + persist: false + node_selector: + k8s.scaleway.com/pool-name: processing-node-pool-inference pod_env_vars: + CODE_SERVER_WS: /workspace/inference-eoap + CONDARC: /workspace/.condarc + CONDA_ENVS_PATH: /workspace/.envs + CWLTOOL_OPTIONS: --podman HOME: /workspace - XDG_CONFIG_HOME: /workspace/.config + XDG_CONFIG_HOME: /workspace/.local + XDG_DATA_HOME: /workspace/.local/share/ XDG_RUNTIME_DIR: /workspace/.local - role_bindings: null + role_bindings: + - name: log-reader-role-binding + persist: false + role: + api_groups: + - '' + name: log-reader-role + resources: + - pods + - pods/log + verbs: + - get + - list + - watch + subjects: + - kind: ServiceAccount + name: default + - name: pod-reader-role-binding + persist: false + role: + api_groups: + - '' + name: pod-manager-role + resources: + - pods + verbs: + - create + - patch + - delete + - list + - watch + subjects: + - kind: ServiceAccount + name: default volumes: + - access_modes: + - ReadWriteMany + claim_name: calrissian-claim + name: calrissian-volume + persist: false + size: 50Gi + storage_class: openebs-kernel-nfs-scw + volume_mount: + mount_path: /calrissian + name: calrissian-volume - access_modes: - ReadWriteOnce claim_name: workspace-claim name: workspace-volume - persist: true - size: 50Gi - storage_class: standard + persist: false + size: 20Gi + storage_class: scw-bssd volume_mount: mount_path: /workspace name: workspace-volume + diff --git a/skaffold.yaml b/skaffold.yaml index d4ddbee..3babd11 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -1,6 +1,8 @@ apiVersion: skaffold/v4beta9 kind: Config build: + # tagPolicy: + # sha256: {} artifacts: - image: hubimage @@ -30,6 +32,35 @@ profiles: - sk-k8s/script.yaml - sk-k8s/job.yaml + - name: eoepca-demo + deploy: + helm: + releases: + - name: jupyterhub + chartPath: jupyterhub + namespace: jupyter + createNamespace: true + valuesFiles: + - demo-eoepca-values.yaml + setValueTemplates: + hub.image.name: "{{.IMAGE_REPO_hubimage}}" + hub.image.tag: "{{.IMAGE_TAG_hubimage}}" + hub.image.pullPolicy: "Always" + hub.db.pvc.storageClassName: "managed-nfs-storage" + hub.image.pullSecrets: + - "eoepca-plus-secret-ro" + setFiles: { + appHubConfig: ./eoepca-demo/config.yml, + pageTheme: ./files/theme/page.html, + spawnPendingTheme: ./files/theme/spawn_pending.html, + spawnTheme: ./files/theme/spawn.html + } + + manifests: + rawYaml: + - sk-k8s/cluster-role-binding.yaml + - sk-k8s/script.yaml + - sk-k8s/job.yaml portForward: - resourceType: service resourceName: proxy-public From 2aefd56e9140b5f8b17f9c24edb7733f292fac0e Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Mon, 7 Oct 2024 09:06:35 +0200 Subject: [PATCH 17/65] bumps image to 1.3.0 --- build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.yml b/build.yml index ef8ccfc..bcc95ae 100644 --- a/build.yml +++ b/build.yml @@ -1,2 +1,2 @@ docker_image_name: eoepca/application-hub -docker_image_version: 1.2.0 +docker_image_version: 1.3.0 From ba4b63ccc224b082bad04f28b15101993b56845a Mon Sep 17 00:00:00 2001 From: Michele Sica <33629436+MicheleSica@users.noreply.github.com> Date: Mon, 7 Oct 2024 10:04:53 +0200 Subject: [PATCH 18/65] Update .github-ci.yaml fixed variable --- .github/workflows/.github-ci.yaml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index aa7e6d6..020c0d3 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -45,7 +45,7 @@ jobs: uses: actions/upload-artifact@v3 with: name: docker-image-tar - path: application-hub-context_1.6.2.tar.gz + path: "application-hub-context_${APP_VERSION}.tar.gz" # Step 6: Scan Docker Image with Trivy - name: Scan Docker Image with Trivy @@ -72,12 +72,12 @@ jobs: # Step 3: Extract the Docker Image tar.gz - name: Extract Docker Image tar.gz run: | - tar -xzf application-hub-context_1.6.2.tar.gz + tar -xzf "application-hub-context_${APP_VERSION}.tar.gz" # Step 4: Load Docker Image - name: Load Docker Image run: | - docker load -i application-hub-context_1.6.2.tar + docker load -i "application-hub-context_${APP_VERSION}.tar" # Step 5: Log in to Docker Registry (use GitHub secrets for security) - name: Login to Docker Registry From c47bb3831e92190bc74890af2b628f8a5b898f21 Mon Sep 17 00:00:00 2001 From: Michele Sica <33629436+MicheleSica@users.noreply.github.com> Date: Tue, 8 Oct 2024 17:12:50 +0200 Subject: [PATCH 19/65] Update .github-ci.yaml --- .github/workflows/.github-ci.yaml | 151 +++++++++++++++++++++++------- 1 file changed, 119 insertions(+), 32 deletions(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index 020c0d3..26674ac 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -21,40 +21,92 @@ jobs: sudo apt-get update -y sudo apt-get install -y trivy - # Step 3: Build Docker image - - name: Build Docker image + # Step 3: Read image name + - name: Read docker name + id: yaml-docker-name + uses: jbutcher5/read-yaml@main + with: + file: 'build.yml' + key-path: '["docker_image_name"]' + + # Step 4: Read image version + - name: Read docker version + id: yaml-docker-version + uses: jbutcher5/read-yaml@main + with: + file: 'build.yml' + key-path: '["docker_image_version"]' + + # Step 5: Generate Docker tag + - name: Generate docker tag + env: + GITHUB_BRANCH: ${{ github.ref }} + docker_image_name: ${{ steps.yaml-docker-name.outputs.data }} + docker_image_version: ${{ steps.yaml-docker-version.outputs.data }} + run: | + branch_name=${GITHUB_BRANCH#refs/heads/} + echo "branch_name=${GITHUB_BRANCH#refs/heads/}" >> $GITHUB_ENV + if [[ "$branch_name" = "main" ]] + then + mType="" + else + mType="dev" + fi + echo "docker_tag=$docker_image_name:$docker_image_version" >> $GITHUB_ENV + echo "docker_tag_latest=$docker_image_name:latest" >> $GITHUB_ENV + docker_image_application=(${docker_image_name#*/}) + echo "docker_image_application=$docker_image_application" >> $GITHUB_ENV + echo "docker_image_version=$docker_image_version" >> $GITHUB_ENV + + # Step 6: Build Docker image to inspect it with Trivy + - name: Build Docker image run: | - APP_NAME="application-hub-context" - APP_VERSION="1.6.2" - tag="${APP_NAME}:${APP_VERSION}" + tag="${docker_image_application}:${docker_image_version}" echo "${{ secrets.CR_PASSWORD }}" | docker login -u "${{ secrets.CR_USERNAME }}" --password-stdin "${{ secrets.CR_REGISTRY }}" - docker build -t "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" --file Dockerfile . - + echo "build ${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}"/"${tag}" + docker build -t "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}"/"${tag}" --file Dockerfile . - # Step 4: Save Docker image as tar.gz + # Step 7: Save Docker image as tar.gz - name: Save Docker Image as tar.gz run: | - APP_NAME="application-hub-context" - APP_VERSION="1.6.2" - tag="${APP_NAME}:${APP_VERSION}" - docker save "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" -o "${APP_NAME}_${APP_VERSION}.tar" - tar -czf "${APP_NAME}_${APP_VERSION}.tar.gz" "${APP_NAME}_${APP_VERSION}.tar" + tag="${docker_image_application}:${docker_image_version}" + docker save "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" -o "${docker_image_application}_${docker_image_version}.tar" + tar -czf "${docker_image_application}_${docker_image_version}.tar.gz" "${docker_image_application}_${docker_image_version}.tar" - # Step 5: Upload Docker Image tar.gz as an artifact + # Step 8: Upload Docker Image tar.gz as an artifact - name: Upload Docker Image Artifact uses: actions/upload-artifact@v3 with: name: docker-image-tar - path: "application-hub-context_${APP_VERSION}.tar.gz" + path: ${{ env.docker_image_application }}_${{ env.docker_image_version }}.tar.gz + - # Step 6: Scan Docker Image with Trivy + # Step 9: Scan Docker Image with Trivy - name: Scan Docker Image with Trivy run: | - APP_NAME="application-hub-context" - APP_VERSION="1.6.2" - tag="${APP_NAME}:${APP_VERSION}" + tag="${docker_image_application}:${docker_image_version}" echo "${{ secrets.CR_PASSWORD }}" | docker login -u "${{ secrets.CR_USERNAME }}" --password-stdin "${{ secrets.CR_REGISTRY }}" - trivy image --no-progress --exit-code 1 --severity HIGH,CRITICAL,UNKNOWN --format table "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" + # trivy image --no-progress --exit-code 1 --severity HIGH,CRITICAL,UNKNOWN --format table "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" + trivy image --no-progress --exit-code 1 --severity LOW --format table "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" + + + # Step 10: Login Docker Hub + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + # Step 11: Build and push to Docker Hub + - name: Build and push to Docker Hub + uses: docker/build-push-action@v4 + with: + context: . + push: true + no-cache: true + tags: | + ${{ env.docker_tag }} + ${{ env.docker_tag_latest }} deploy: needs: build @@ -62,33 +114,68 @@ jobs: steps: # Step 1: Checkout repository - uses: actions/checkout@v4 + + # Step 2: Read image name + - name: Read docker name + id: yaml-docker-name + uses: jbutcher5/read-yaml@main + with: + file: 'build.yml' + key-path: '["docker_image_name"]' - # Step 2: Download Docker Image tar.gz Artifact + # Step 3: Read image version + - name: Read docker version + id: yaml-docker-version + uses: jbutcher5/read-yaml@main + with: + file: 'build.yml' + key-path: '["docker_image_version"]' + + # Step 4: Generate Docker tag + - name: Generate docker tag + env: + GITHUB_BRANCH: ${{ github.ref }} + docker_image_name: ${{ steps.yaml-docker-name.outputs.data }} + docker_image_version: ${{ steps.yaml-docker-version.outputs.data }} + run: | + branch_name=${GITHUB_BRANCH#refs/heads/} + echo "branch_name=${GITHUB_BRANCH#refs/heads/}" >> $GITHUB_ENV + if [[ "$branch_name" = "main" ]] + then + mType="" + else + mType="dev" + fi + echo "docker_tag=$docker_image_name:$docker_image_version" >> $GITHUB_ENV + echo "docker_tag_latest=$docker_image_name:latest" >> $GITHUB_ENV + docker_image_application=(${docker_image_name#*/}) + echo "docker_image_application=$docker_image_application" >> $GITHUB_ENV + echo "docker_image_version=$docker_image_version" >> $GITHUB_ENV + + # Step 5: Download Docker Image tar.gz Artifact - name: Download Docker Image Artifact uses: actions/download-artifact@v3 with: name: docker-image-tar - # Step 3: Extract the Docker Image tar.gz + # Step 6: Extract the Docker Image tar.gz - name: Extract Docker Image tar.gz run: | - tar -xzf "application-hub-context_${APP_VERSION}.tar.gz" + tar -xzf "${docker_image_application}_${docker_image_version}.tar.gz" - # Step 4: Load Docker Image + # Step 7: Load Docker Image - name: Load Docker Image run: | - docker load -i "application-hub-context_${APP_VERSION}.tar" + docker load -i "${docker_image_application}_${docker_image_version}.tar" - # Step 5: Log in to Docker Registry (use GitHub secrets for security) + # Step 8: Log in to Docker Registry (use GitHub secrets for security) - name: Login to Docker Registry run: | echo "${{ secrets.CR_PASSWORD }}" | docker login -u "${{ secrets.CR_USERNAME }}" --password-stdin "${{ secrets.CR_REGISTRY }}" - # Step 6: Push Docker Image to Registry + # Step 9: Push Docker Image to Registry - name: Push Docker Image to Registry run: | - APP_NAME="application-hub-context" - APP_VERSION="1.6.2" - tag="${APP_NAME}:${APP_VERSION}" - docker push "${{ secrets.CR_REGISTRY }}"/"${{ secrets.CR_REPO }}"/${tag} - + tag="${docker_image_application}:${docker_image_version}" + docker push "${{ secrets.CR_REGISTRY }}"/"${{ secrets.CR_REPO }}"/"${tag}" + From ade381edfe1bbf6382e450337bbbdd32c117c9f9 Mon Sep 17 00:00:00 2001 From: Michele Sica <33629436+MicheleSica@users.noreply.github.com> Date: Tue, 8 Oct 2024 17:25:46 +0200 Subject: [PATCH 20/65] Update .github-ci.yaml --- .github/workflows/.github-ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index 26674ac..d96f411 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -86,8 +86,8 @@ jobs: run: | tag="${docker_image_application}:${docker_image_version}" echo "${{ secrets.CR_PASSWORD }}" | docker login -u "${{ secrets.CR_USERNAME }}" --password-stdin "${{ secrets.CR_REGISTRY }}" - # trivy image --no-progress --exit-code 1 --severity HIGH,CRITICAL,UNKNOWN --format table "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" - trivy image --no-progress --exit-code 1 --severity LOW --format table "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" + trivy image --no-progress --exit-code 1 --severity HIGH,CRITICAL,UNKNOWN --format table "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" + exit code 1 # Step 10: Login Docker Hub From 651dfdfadcb4bac68238de1a0d89c3e7d581ef65 Mon Sep 17 00:00:00 2001 From: Michele Sica <33629436+MicheleSica@users.noreply.github.com> Date: Tue, 8 Oct 2024 17:44:43 +0200 Subject: [PATCH 21/65] Update .github-ci.yaml new configuration --- .github/workflows/.github-ci.yaml | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index d96f411..7cc0919 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -63,7 +63,6 @@ jobs: run: | tag="${docker_image_application}:${docker_image_version}" echo "${{ secrets.CR_PASSWORD }}" | docker login -u "${{ secrets.CR_USERNAME }}" --password-stdin "${{ secrets.CR_REGISTRY }}" - echo "build ${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}"/"${tag}" docker build -t "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}"/"${tag}" --file Dockerfile . # Step 7: Save Docker image as tar.gz @@ -87,9 +86,7 @@ jobs: tag="${docker_image_application}:${docker_image_version}" echo "${{ secrets.CR_PASSWORD }}" | docker login -u "${{ secrets.CR_USERNAME }}" --password-stdin "${{ secrets.CR_REGISTRY }}" trivy image --no-progress --exit-code 1 --severity HIGH,CRITICAL,UNKNOWN --format table "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" - exit code 1 - - + # Step 10: Login Docker Hub - name: Login to Docker Hub uses: docker/login-action@v2 @@ -140,12 +137,6 @@ jobs: run: | branch_name=${GITHUB_BRANCH#refs/heads/} echo "branch_name=${GITHUB_BRANCH#refs/heads/}" >> $GITHUB_ENV - if [[ "$branch_name" = "main" ]] - then - mType="" - else - mType="dev" - fi echo "docker_tag=$docker_image_name:$docker_image_version" >> $GITHUB_ENV echo "docker_tag_latest=$docker_image_name:latest" >> $GITHUB_ENV docker_image_application=(${docker_image_name#*/}) From 10d0691071be817b425b399a8a9520623a67eb62 Mon Sep 17 00:00:00 2001 From: Michele Sica <33629436+MicheleSica@users.noreply.github.com> Date: Wed, 9 Oct 2024 09:40:03 +0200 Subject: [PATCH 22/65] Update .github-ci.yaml --- .github/workflows/.github-ci.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index 7cc0919..53876f3 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -85,7 +85,8 @@ jobs: run: | tag="${docker_image_application}:${docker_image_version}" echo "${{ secrets.CR_PASSWORD }}" | docker login -u "${{ secrets.CR_USERNAME }}" --password-stdin "${{ secrets.CR_REGISTRY }}" - trivy image --no-progress --exit-code 1 --severity HIGH,CRITICAL,UNKNOWN --format table "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" + # trivy image --no-progress --exit-code 1 --severity HIGH,CRITICAL,UNKNOWN --format table "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" + trivy image --no-progress --exit-code 1 --severity MEDIUM --format table "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" # Step 10: Login Docker Hub - name: Login to Docker Hub From 859ec7b9adde2388b841bdf85204e36f0f1ef775 Mon Sep 17 00:00:00 2001 From: Michele Sica <33629436+MicheleSica@users.noreply.github.com> Date: Wed, 9 Oct 2024 10:28:29 +0200 Subject: [PATCH 23/65] Update .github-ci.yaml --- .github/workflows/.github-ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index 53876f3..5c62511 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -85,8 +85,8 @@ jobs: run: | tag="${docker_image_application}:${docker_image_version}" echo "${{ secrets.CR_PASSWORD }}" | docker login -u "${{ secrets.CR_USERNAME }}" --password-stdin "${{ secrets.CR_REGISTRY }}" - # trivy image --no-progress --exit-code 1 --severity HIGH,CRITICAL,UNKNOWN --format table "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" - trivy image --no-progress --exit-code 1 --severity MEDIUM --format table "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" + trivy image --no-progress --exit-code 1 --severity HIGH,CRITICAL,UNKNOWN --format table "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" + # Step 10: Login Docker Hub - name: Login to Docker Hub From 0388aab198dd5eadf33676b85b4bb3795fb83432 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 15 Oct 2024 09:32:19 +0200 Subject: [PATCH 24/65] Update requirements.txt --- requirements.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 157fe5e..7dc61bf 100644 --- a/requirements.txt +++ b/requirements.txt @@ -44,8 +44,8 @@ jupyterhub-ldapauthenticator==1.3.2 jupyterhub-ltiauthenticator==1.6.2 jupyterhub-nativeauthenticator==1.3.0 jupyterhub-tmpauthenticator==1.0.0 -kubernetes==31.0.0 -kubernetes_asyncio==31.1.0 +kubernetes==26.1.0 +kubernetes_asyncio==26.1.0 ldap3==2.9.1 loguru==0.7.2 Mako==1.3.5 From 62a9aabbbf8013e03722240b24287b903f31796a Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 15 Oct 2024 09:35:19 +0200 Subject: [PATCH 25/65] Update requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 7dc61bf..3b12263 100644 --- a/requirements.txt +++ b/requirements.txt @@ -45,7 +45,7 @@ jupyterhub-ltiauthenticator==1.6.2 jupyterhub-nativeauthenticator==1.3.0 jupyterhub-tmpauthenticator==1.0.0 kubernetes==26.1.0 -kubernetes_asyncio==26.1.0 +kubernetes_asyncio==24.2.3 ldap3==2.9.1 loguru==0.7.2 Mako==1.3.5 From 2bef91066150e49ca27023bc24a2afa674e656c9 Mon Sep 17 00:00:00 2001 From: ldonnini Date: Tue, 15 Oct 2024 16:24:15 +0200 Subject: [PATCH 26/65] removed redundant builds --- .github/workflows/.github-ci.yaml | 32 +++++++++++++------------------ 1 file changed, 13 insertions(+), 19 deletions(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index 5c62511..18c3abb 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -87,24 +87,6 @@ jobs: echo "${{ secrets.CR_PASSWORD }}" | docker login -u "${{ secrets.CR_USERNAME }}" --password-stdin "${{ secrets.CR_REGISTRY }}" trivy image --no-progress --exit-code 1 --severity HIGH,CRITICAL,UNKNOWN --format table "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" - - # Step 10: Login Docker Hub - - name: Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - # Step 11: Build and push to Docker Hub - - name: Build and push to Docker Hub - uses: docker/build-push-action@v4 - with: - context: . - push: true - no-cache: true - tags: | - ${{ env.docker_tag }} - ${{ env.docker_tag_latest }} deploy: needs: build @@ -170,4 +152,16 @@ jobs: run: | tag="${docker_image_application}:${docker_image_version}" docker push "${{ secrets.CR_REGISTRY }}"/"${{ secrets.CR_REPO }}"/"${tag}" - + + # Step 10: Login Docker Hub + - name: Login to Docker Hub + uses: docker/login-action@v2 + with: + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + # Step 11: Push to Docker Hub + - name: push to Docker Hub + run: | + docker push "$docker.io/{{ env.docker_tag }}" + docker push "$docker.io/{{ env.docker_tag_latest }}" \ No newline at end of file From bdb313b56530a89a01f24e94b48b7a8da2efdb4b Mon Sep 17 00:00:00 2001 From: ldonnini Date: Tue, 15 Oct 2024 16:33:02 +0200 Subject: [PATCH 27/65] fix issue --- .github/workflows/.github-ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index 18c3abb..d8beffd 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -163,5 +163,5 @@ jobs: # Step 11: Push to Docker Hub - name: push to Docker Hub run: | - docker push "$docker.io/{{ env.docker_tag }}" - docker push "$docker.io/{{ env.docker_tag_latest }}" \ No newline at end of file + docker push "$docker.io/${{ env.docker_tag }}" + docker push "$docker.io/${{ env.docker_tag_latest }}" \ No newline at end of file From 8fa3549e529369f30b800bbed9e7f4732ac83937 Mon Sep 17 00:00:00 2001 From: ldonnini Date: Tue, 15 Oct 2024 16:40:01 +0200 Subject: [PATCH 28/65] fix issue --- .github/workflows/.github-ci.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index d8beffd..fa0f0e8 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -163,5 +163,5 @@ jobs: # Step 11: Push to Docker Hub - name: push to Docker Hub run: | - docker push "$docker.io/${{ env.docker_tag }}" - docker push "$docker.io/${{ env.docker_tag_latest }}" \ No newline at end of file + docker push "docker.io/${{ env.docker_tag }}" + docker push "docker.io/${{ env.docker_tag_latest }}" \ No newline at end of file From ec5be7134a0adc2b241319c6c68cb13341fd5677 Mon Sep 17 00:00:00 2001 From: ldonnini Date: Tue, 15 Oct 2024 17:26:09 +0200 Subject: [PATCH 29/65] added tag to docker image --- .github/workflows/.github-ci.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index fa0f0e8..468ef11 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -122,6 +122,7 @@ jobs: echo "branch_name=${GITHUB_BRANCH#refs/heads/}" >> $GITHUB_ENV echo "docker_tag=$docker_image_name:$docker_image_version" >> $GITHUB_ENV echo "docker_tag_latest=$docker_image_name:latest" >> $GITHUB_ENV + echo "docker_hub=" docker_image_application=(${docker_image_name#*/}) echo "docker_image_application=$docker_image_application" >> $GITHUB_ENV echo "docker_image_version=$docker_image_version" >> $GITHUB_ENV @@ -163,5 +164,7 @@ jobs: # Step 11: Push to Docker Hub - name: push to Docker Hub run: | + docker tag "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}"/"${tag}" "docker.io/${{ env.docker_tag }}" + docker tag "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}"/"${tag}" "docker.io/${{ env.docker_tag_latest }}" docker push "docker.io/${{ env.docker_tag }}" docker push "docker.io/${{ env.docker_tag_latest }}" \ No newline at end of file From 02ba446667ac16e3cd368feb0cc5cb1a425b9aa0 Mon Sep 17 00:00:00 2001 From: ldonnini Date: Tue, 15 Oct 2024 17:35:32 +0200 Subject: [PATCH 30/65] added tag to docker image --- .github/workflows/.github-ci.yaml | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index 468ef11..6e0735b 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -63,7 +63,7 @@ jobs: run: | tag="${docker_image_application}:${docker_image_version}" echo "${{ secrets.CR_PASSWORD }}" | docker login -u "${{ secrets.CR_USERNAME }}" --password-stdin "${{ secrets.CR_REGISTRY }}" - docker build -t "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}"/"${tag}" --file Dockerfile . + docker build -t "${tag}" --file Dockerfile . # Step 7: Save Docker image as tar.gz - name: Save Docker Image as tar.gz @@ -122,7 +122,6 @@ jobs: echo "branch_name=${GITHUB_BRANCH#refs/heads/}" >> $GITHUB_ENV echo "docker_tag=$docker_image_name:$docker_image_version" >> $GITHUB_ENV echo "docker_tag_latest=$docker_image_name:latest" >> $GITHUB_ENV - echo "docker_hub=" docker_image_application=(${docker_image_name#*/}) echo "docker_image_application=$docker_image_application" >> $GITHUB_ENV echo "docker_image_version=$docker_image_version" >> $GITHUB_ENV @@ -152,6 +151,7 @@ jobs: - name: Push Docker Image to Registry run: | tag="${docker_image_application}:${docker_image_version}" + docker tag "${tag}" "${{ secrets.CR_REGISTRY }}"/"${{ secrets.CR_REPO }}"/"${tag}" docker push "${{ secrets.CR_REGISTRY }}"/"${{ secrets.CR_REPO }}"/"${tag}" # Step 10: Login Docker Hub @@ -164,7 +164,8 @@ jobs: # Step 11: Push to Docker Hub - name: push to Docker Hub run: | - docker tag "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}"/"${tag}" "docker.io/${{ env.docker_tag }}" - docker tag "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}"/"${tag}" "docker.io/${{ env.docker_tag_latest }}" + tag="${docker_image_application}:${docker_image_version}" + docker tag "${tag}" "docker.io/${{ env.docker_tag }}" + docker tag "${tag}" "docker.io/${{ env.docker_tag_latest }}" docker push "docker.io/${{ env.docker_tag }}" docker push "docker.io/${{ env.docker_tag_latest }}" \ No newline at end of file From a4c9fa14e184e21a26a59d5e414eb41e61fbf449 Mon Sep 17 00:00:00 2001 From: ldonnini Date: Tue, 15 Oct 2024 17:40:43 +0200 Subject: [PATCH 31/65] fix saved docker --- .github/workflows/.github-ci.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index 6e0735b..6c572ce 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -69,7 +69,7 @@ jobs: - name: Save Docker Image as tar.gz run: | tag="${docker_image_application}:${docker_image_version}" - docker save "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" -o "${docker_image_application}_${docker_image_version}.tar" + docker save "${tag}" -o "${docker_image_application}_${docker_image_version}.tar" tar -czf "${docker_image_application}_${docker_image_version}.tar.gz" "${docker_image_application}_${docker_image_version}.tar" # Step 8: Upload Docker Image tar.gz as an artifact From 68fb7e090494d50261a3af2c57ad125d20868557 Mon Sep 17 00:00:00 2001 From: Fabio Zingaretti Date: Thu, 24 Oct 2024 13:57:28 +0200 Subject: [PATCH 32/65] [FIX]Use config_map.name instead of config_map.key --- application_hub_context/app_hub_context.py | 2 +- build.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index 68d134b..4ff5026 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -701,7 +701,7 @@ def initialise(self): { "name": config_map.name, "configMap": { - "name": config_map.key, + "name": config_map.name, "defaultMode": int(config_map.default_mode, 8) if config_map.default_mode else 0o644, # noqa: E501 diff --git a/build.yml b/build.yml index bcc95ae..511c9dd 100644 --- a/build.yml +++ b/build.yml @@ -1,2 +1,2 @@ docker_image_name: eoepca/application-hub -docker_image_version: 1.3.0 +docker_image_version: 1.3.1 From ab2163d91fd25ed200632897d3d57129826ccb8a Mon Sep 17 00:00:00 2001 From: ldonnini Date: Thu, 24 Oct 2024 16:37:26 +0200 Subject: [PATCH 33/65] updated github-ci --- .github/workflows/.github-ci.yaml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index 6c572ce..ae7cc5c 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -84,8 +84,7 @@ jobs: - name: Scan Docker Image with Trivy run: | tag="${docker_image_application}:${docker_image_version}" - echo "${{ secrets.CR_PASSWORD }}" | docker login -u "${{ secrets.CR_USERNAME }}" --password-stdin "${{ secrets.CR_REGISTRY }}" - trivy image --no-progress --exit-code 1 --severity HIGH,CRITICAL,UNKNOWN --format table "${{ secrets.CR_REGISTRY }}/${{ secrets.CR_REPO }}/${tag}" + trivy image --no-progress --exit-code 1 --severity HIGH,CRITICAL,UNKNOWN --format table "${tag}" deploy: From dd69e17e1870426924df632ddf3346da62b291e4 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Wed, 18 Dec 2024 15:33:36 +0100 Subject: [PATCH 34/65] WIP on new chart 4.0.0 --- setup.cfg | 2 +- skaffold.yaml | 81 ++++++++++++++++++++++++++++----------------------- 2 files changed, 45 insertions(+), 38 deletions(-) diff --git a/setup.cfg b/setup.cfg index 710cebb..03d2509 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,3 @@ [metadata] name = application-context-hub -version = 1.2.0 +version = 1.3.0 diff --git a/skaffold.yaml b/skaffold.yaml index 3babd11..534d786 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -11,19 +11,26 @@ profiles: deploy: helm: releases: - - name: jupyterhub - chartPath: jupyterhub + - name: application-hub + #chartPath: jupyterhub + remoteChart: eoepca/application-hub namespace: jupyter + version: 4.0.0 createNamespace: true - valuesFiles: - - demo-values.yaml + valuesFiles: [] + # - demo-values.yaml setValueTemplates: - hub.image.name: "{{.IMAGE_REPO_hubimage}}/{{.IMAGE_TAG_hubimage}}@{{.IMAGE_DIGEST_hubimage}}" + jupyterhub.hub.image.name: "{{.IMAGE_REPO_hubimage}}/{{.IMAGE_TAG_hubimage}}@{{.IMAGE_DIGEST_hubimage}}" + setValues: + jupyterhub.hub.db.pvc.storageClassName: standard + jupyterhub.hub.extraEnv.JUPYTERHUB_CRYPT_KEY: 032d5bfe141a7eab86d57587879b33c5d168617cacb339823d7f47fe2933f880 + jupyterhub.hub.extraEnv.APP_HUB_NAMESPACE: jupyter setFiles: { appHubConfig: ./files/hub/config.yml, - pageTheme: ./files/theme/page.html, - spawnPendingTheme: ./files/theme/spawn_pending.html, - spawnTheme: ./files/theme/spawn.html + jupHubConfig: ./files/hub/jupyterhub_config.py, + #pageTheme: ./files/theme/page.html, + #spawnPendingTheme: ./files/theme/spawn_pending.html, + #spawnTheme: ./files/theme/spawn.html } manifests: @@ -32,38 +39,38 @@ profiles: - sk-k8s/script.yaml - sk-k8s/job.yaml - - name: eoepca-demo - deploy: - helm: - releases: - - name: jupyterhub - chartPath: jupyterhub - namespace: jupyter - createNamespace: true - valuesFiles: - - demo-eoepca-values.yaml - setValueTemplates: - hub.image.name: "{{.IMAGE_REPO_hubimage}}" - hub.image.tag: "{{.IMAGE_TAG_hubimage}}" - hub.image.pullPolicy: "Always" - hub.db.pvc.storageClassName: "managed-nfs-storage" - hub.image.pullSecrets: - - "eoepca-plus-secret-ro" - setFiles: { - appHubConfig: ./eoepca-demo/config.yml, - pageTheme: ./files/theme/page.html, - spawnPendingTheme: ./files/theme/spawn_pending.html, - spawnTheme: ./files/theme/spawn.html - } + # - name: eoepca-demo + # deploy: + # helm: + # releases: + # - name: jupyterhub + # chartPath: jupyterhub + # namespace: jupyter + # createNamespace: true + # valuesFiles: + # - demo-eoepca-values.yaml + # setValueTemplates: + # hub.image.name: "{{.IMAGE_REPO_hubimage}}" + # hub.image.tag: "{{.IMAGE_TAG_hubimage}}" + # hub.image.pullPolicy: "Always" + # hub.db.pvc.storageClassName: "managed-nfs-storage" + # hub.image.pullSecrets: + # - "eoepca-plus-secret-ro" + # setFiles: { + # appHubConfig: ./eoepca-demo/config.yml, + # pageTheme: ./files/theme/page.html, + # spawnPendingTheme: ./files/theme/spawn_pending.html, + # spawnTheme: ./files/theme/spawn.html + # } - manifests: - rawYaml: - - sk-k8s/cluster-role-binding.yaml - - sk-k8s/script.yaml - - sk-k8s/job.yaml + # manifests: + # rawYaml: + # - sk-k8s/cluster-role-binding.yaml + # - sk-k8s/script.yaml + # - sk-k8s/job.yaml portForward: - resourceType: service - resourceName: proxy-public + resourceName: application-hub-proxy-public namespace: jupyter # Optional, if you are using a specific namespace port: 80 # Target port on the pod localPort: 8000 # Local port on your machine From 892c6efebc014b72c15c722fe4eda9421d83fd53 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Wed, 18 Dec 2024 15:55:00 +0100 Subject: [PATCH 35/65] fix docker CVE-2024-52804 --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 3b12263..de011f2 100644 --- a/requirements.txt +++ b/requirements.txt @@ -97,7 +97,7 @@ sqlalchemy-cockroachdb==2.0.2 statsd==4.0.1 text-unidecode==1.3 tomli==2.0.1 -tornado==6.4.1 +tornado==6.4.2 traitlets==5.14.3 types-python-dateutil==2.9.0.20240906 typing_extensions==4.12.2 From 13f6f61e2ac65e4766c453a3497c432e0d1f29d2 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Sat, 21 Dec 2024 01:00:14 +0100 Subject: [PATCH 36/65] updates Dockerfile for 4.0.0 --- .dockerignore | 3 +- Dockerfile | 4 +- requirements.txt | 216 +++++++++++++++++++++++------------------------ 3 files changed, 112 insertions(+), 111 deletions(-) diff --git a/.dockerignore b/.dockerignore index 05573c9..a5d8001 100644 --- a/.dockerignore +++ b/.dockerignore @@ -2,4 +2,5 @@ jupyterhub values.yaml config.yml config-generator -skaffold.yaml \ No newline at end of file +skaffold.yaml +files/* \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index d866f6c..598c405 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM ghcr.io/eoepca/container-k8s-hub/container-k8s-hub:2.0.0 +FROM ghcr.io/eoepca/container-k8s-hub/container-k8s-hub:4.0.0 ARG NB_USER=johub ARG NB_UID=1001 @@ -55,4 +55,4 @@ RUN cd /tmp && python3 setup.py install USER ${NB_USER} # Command to start jupyterhub -CMD ["jupyterhub", "--config", "/etc/jupyterhub/jupyterhub_config.py"] +CMD ["jupyterhub", "--config", "/etc/jupyterhub/jupyterhub_config.py"] \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index de011f2..763f5a8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,109 +1,109 @@ -addict==2.4.0 -aiohappyeyeballs==2.4.0 -aiohttp==3.10.6 -aiosignal==1.3.1 -alembic==1.13.3 -annotated-types==0.7.0 -arrow==1.3.0 -async-generator==1.10 -async-timeout==4.0.3 -attrs==24.2.0 -bcrypt==4.2.0 -cachetools==5.5.0 -certifi==2024.8.30 -certipy==0.2.1 -cffi==1.17.1 -charset-normalizer==3.3.2 -coverage==7.6.1 -cryptography==43.0.1 -durationpy==0.7 -escapism==1.0.1 -exceptiongroup==1.2.2 -fqdn==1.5.1 -frozenlist==1.4.1 -google-auth==2.35.0 -greenlet==3.1.1 -httplib2==0.22.0 -idna==3.10 -importlib_metadata==8.5.0 -importlib_resources==6.4.5 -iniconfig==2.0.0 -isoduration==20.11.0 -Jinja2==3.1.4 -jsonpointer==3.0.0 -jsonschema==4.23.0 -jsonschema-specifications==2023.12.1 -jupyter-events==0.10.0 -jupyter-telemetry==0.1.0 -jupyterhub==5.1.0 -jupyterhub-firstuseauthenticator==1.1.0 -jupyterhub-hmacauthenticator==1.0 -jupyterhub-idle-culler==1.4.0 -jupyterhub-kubespawner==6.2.0 -jupyterhub-ldapauthenticator==1.3.2 -jupyterhub-ltiauthenticator==1.6.2 -jupyterhub-nativeauthenticator==1.3.0 -jupyterhub-tmpauthenticator==1.0.0 -kubernetes==26.1.0 -kubernetes_asyncio==24.2.3 -ldap3==2.9.1 +# addict==2.4.0 +# aiohappyeyeballs==2.4.0 +# aiohttp==3.10.6 +# aiosignal==1.3.1 +# alembic==1.13.3 +# annotated-types==0.7.0 +# arrow==1.3.0 +# async-generator==1.10 +# async-timeout==4.0.3 +# attrs==24.2.0 +# bcrypt==4.2.0 +# cachetools==5.5.0 +# certifi==2024.8.30 +# certipy==0.2.1 +# cffi==1.17.1 +# charset-normalizer==3.3.2 +# coverage==7.6.1 +# cryptography==43.0.1 +# durationpy==0.7 +# escapism==1.0.1 +# exceptiongroup==1.2.2 +# fqdn==1.5.1 +# frozenlist==1.4.1 +# google-auth==2.35.0 +# greenlet==3.1.1 +# httplib2==0.22.0 +# idna==3.10 +# importlib_metadata==8.5.0 +# importlib_resources==6.4.5 +# iniconfig==2.0.0 +# isoduration==20.11.0 +# Jinja2==3.1.4 +# jsonpointer==3.0.0 +# jsonschema==4.23.0 +# jsonschema-specifications==2023.12.1 +# jupyter-events==0.10.0 +# jupyter-telemetry==0.1.0 +# jupyterhub==5.1.0 +# jupyterhub-firstuseauthenticator==1.1.0 +# jupyterhub-hmacauthenticator==1.0 +# jupyterhub-idle-culler==1.4.0 +# jupyterhub-kubespawner==6.2.0 +# jupyterhub-ldapauthenticator==1.3.2 +# jupyterhub-ltiauthenticator==1.6.2 +# jupyterhub-nativeauthenticator==1.3.0 +# jupyterhub-tmpauthenticator==1.0.0 +kubernetes==31.0.0 +# kubernetes_asyncio==24.2.3 +# ldap3==2.9.1 loguru==0.7.2 -Mako==1.3.5 -MarkupSafe==2.1.5 -multidict==6.1.0 -mwoauth==0.4.0 -nullauthenticator==1.0.0 -oauthenticator==17.0.0 -oauthlib==3.2.2 -onetimepass==1.0.1 -packaging==24.1 -pamela==1.2.0 -pkgutil_resolve_name==1.3.10 -pluggy==1.5.0 -prometheus_client==0.21.0 -psycopg2-binary==2.9.9 -py-spy==0.3.14 -pyasn1==0.6.1 -pyasn1_modules==0.4.1 -pycparser==2.22 -pycurl==7.45.3 -pydantic==2.9.2 -pydantic_core==2.23.4 -PyJWT==2.9.0 -PyMySQL==1.1.1 -pyOpenSSL==24.2.1 -pyparsing==3.1.4 -pyrsistent==0.20.0 -pytest==8.3.3 -pytest-asyncio==0.24.0 -pytest-cov==5.0.0 -python-dateutil==2.9.0.post0 -python-json-logger==2.0.7 -python-slugify==8.0.4 -PyYAML==6.0.2 -referencing==0.35.1 -requests==2.32.3 -requests-mock==1.12.1 -requests-oauthlib==2.0.0 -rfc3339-validator==0.1.4 -rfc3986-validator==0.1.1 -rpds-py==0.20.0 -rsa==4.9 -ruamel.yaml==0.18.6 -ruamel.yaml.clib==0.2.8 -six==1.16.0 -SQLAlchemy==2.0.35 -sqlalchemy-cockroachdb==2.0.2 -statsd==4.0.1 -text-unidecode==1.3 -tomli==2.0.1 -tornado==6.4.2 -traitlets==5.14.3 -types-python-dateutil==2.9.0.20240906 -typing_extensions==4.12.2 -uri-template==1.3.0 -urllib3==2.2.3 -webcolors==24.8.0 -websocket-client==1.8.0 -yarl==1.12.1 -zipp==3.20.2 +# Mako==1.3.5 +# MarkupSafe==2.1.5 +# multidict==6.1.0 +# mwoauth==0.4.0 +# nullauthenticator==1.0.0 +# oauthenticator==17.0.0 +# oauthlib==3.2.2 +# onetimepass==1.0.1 +# packaging==24.1 +# pamela==1.2.0 +# pkgutil_resolve_name==1.3.10 +# pluggy==1.5.0 +# prometheus_client==0.21.0 +# psycopg2-binary==2.9.9 +# py-spy==0.3.14 +# pyasn1==0.6.1 +# pyasn1_modules==0.4.1 +# pycparser==2.22 +# pycurl==7.45.3 +# pydantic==2.9.2 +# pydantic_core==2.23.4 +# PyJWT==2.9.0 +# PyMySQL==1.1.1 +# pyOpenSSL==24.2.1 +# pyparsing==3.1.4 +# pyrsistent==0.20.0 +# pytest==8.3.3 +# pytest-asyncio==0.24.0 +# pytest-cov==5.0.0 +# python-dateutil==2.9.0.post0 +# python-json-logger==2.0.7 +# python-slugify==8.0.4 +# PyYAML==6.0.2 +# referencing==0.35.1 +# requests==2.32.3 +# requests-mock==1.12.1 +# requests-oauthlib==2.0.0 +# rfc3339-validator==0.1.4 +# rfc3986-validator==0.1.1 +# rpds-py==0.20.0 +# rsa==4.9 +# ruamel.yaml==0.18.6 +# ruamel.yaml.clib==0.2.8 +# six==1.16.0 +# SQLAlchemy==2.0.35 +# sqlalchemy-cockroachdb==2.0.2 +# statsd==4.0.1 +# text-unidecode==1.3 +# tomli==2.0.1 +# tornado==6.4.2 +# traitlets==5.14.3 +# types-python-dateutil==2.9.0.20240906 +# typing_extensions==4.12.2 +# uri-template==1.3.0 +# urllib3==2.2.3 +# webcolors==24.8.0 +# websocket-client==1.8.0 +# yarl==1.12.1 +# zipp==3.20.2 From 55098109beaded018fafec1fa9c8151a1b920c7d Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Sat, 21 Dec 2024 01:29:40 +0100 Subject: [PATCH 37/65] adds spawner object to manifest templating --- application_hub_context/app_hub_context.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index 4ff5026..62d39e1 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -4,6 +4,7 @@ from abc import ABC from http import HTTPStatus from typing import Dict, TextIO +from jinja2 import Template from kubernetes import client, config from kubernetes.utils import create_from_dict @@ -535,9 +536,14 @@ def patch_service_account(self, secret_name: str): # def apply_manifests(self, manifest_file): def apply_manifest(self, manifest): + template = Template(yaml.dump(manifest)) + rendered_manifest = template.render(spawner=self.spawner) + + self.spawner.log.info(f"Applying manifest: {yaml.safe_load(rendered_manifest)}") + create_from_dict( k8s_client=self.api_client, - data=manifest, + data=yaml.safe_load(rendered_manifest), verbose=True, namespace=self.namespace, ) From 6b91432514487cbacd832d48c347d1f3ae74479e Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Sat, 21 Dec 2024 02:04:22 +0100 Subject: [PATCH 38/65] adds env from configmap --- application_hub_context/__init__.py | 1 + application_hub_context/app_hub_context.py | 20 ++++- application_hub_context/models.py | 2 +- application_hub_context/parser.py | 7 ++ config-generator/config-generator.ipynb | 98 +++++++++++++--------- config-generator/manifests/manifest.yaml | 11 ++- config-generator/models.py | 1 + 7 files changed, 99 insertions(+), 41 deletions(-) diff --git a/application_hub_context/__init__.py b/application_hub_context/__init__.py index e69de29..6225854 100644 --- a/application_hub_context/__init__.py +++ b/application_hub_context/__init__.py @@ -0,0 +1 @@ +version = "1.3.1" \ No newline at end of file diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index 62d39e1..efce087 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -577,7 +577,8 @@ def unapply_manifests(self, manifest_content): self.rbac_authorization_v1_api.delete_namespaced_role_binding(name, namespace) elif kind == "ServiceAccount": self.core_v1_api.delete_namespaced_service_account(name, namespace) - + elif kind == "ConfigMap": + self.core_v1_api.delete_namespaced_config_map(name, namespace) # Add other kinds as needed else: @@ -892,6 +893,23 @@ def initialise(self): f"Skipping creation of manifest {manifest.name}" ) + # process the pod env vars from config maps + env_from_config_maps = self.config_parser.get_profile_env_from_config_maps( + profile_id=profile_id + ) + self.spawner.log.info(f"env_from_config_maps {env_from_config_maps}") + if env_from_config_maps: + self.spawner.extra_container_config["env_from"] = [] + + for env_from_config_map in env_from_config_maps: + self.spawner.log.info(f"env_from_config_map {env_from_config_map}") + self.spawner.extra_container_config["env_from"].append( + { + "configMapRef": { + "name": env_from_config_map, + }}) + self.spawner.log.info(f"extra_container_config {self.spawner.extra_container_config}") + def dispose(self): profile_id = self.config_parser.get_profile_by_slug(slug=self.profile_slug).id diff --git a/application_hub_context/models.py b/application_hub_context/models.py index bda9082..0c5132a 100644 --- a/application_hub_context/models.py +++ b/application_hub_context/models.py @@ -177,7 +177,7 @@ class Profile(BaseModel): image_pull_secrets: Optional[List[ImagePullSecret]] = None init_containers: Optional[List[InitContainer]] = None manifests: Optional[List[Manifest]] = None - + env_from_config_maps: Optional[List[str]] = None class Config(BaseModel): profiles: List[Profile] diff --git a/application_hub_context/parser.py b/application_hub_context/parser.py index 6364a6b..920c8c4 100644 --- a/application_hub_context/parser.py +++ b/application_hub_context/parser.py @@ -131,3 +131,10 @@ def get_profile_manifests(self, profile_id): return self.get_profile_by_id(profile_id=profile_id).manifests except AttributeError: pass + + def get_profile_env_from_config_maps(self, profile_id): + """returns the profile env from config maps""" + try: + return self.get_profile_by_id(profile_id=profile_id).env_from_config_maps + except AttributeError: + pass \ No newline at end of file diff --git a/config-generator/config-generator.ipynb b/config-generator/config-generator.ipynb index 41a5ba5..7a87bb3 100644 --- a/config-generator/config-generator.ipynb +++ b/config-generator/config-generator.ipynb @@ -11,6 +11,22 @@ "import os" ] }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"manifests/manifest.yaml\", \"r\") as f:\n", + " content = yaml.safe_load_all(f.read())\n", + "\n", + "\n", + "\n", + "localstack_manifest = Manifest(\n", + " name=\"manifests\", key=\"manifests\", readonly=True, persist=False, content=[e for e in content]\n", + ")" + ] + }, { "attachments": {}, "cell_type": "markdown", @@ -22,7 +38,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -30,7 +46,9 @@ "storage_class_rwx = \"standard\"\n", "\n", "workspace_volume_size = \"50Gi\"\n", - "calrissian_volume_size = \"50Gi\"\n" + "calrissian_volume_size = \"50Gi\"\n", + "\n", + "node_selector = {} #\"k8s.scaleway.com/pool-name\": \"application-hub\"}\n" ] }, { @@ -55,7 +73,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -83,7 +101,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -120,7 +138,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -147,7 +165,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -173,7 +191,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -190,7 +208,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -225,7 +243,7 @@ " id=f\"profile_studio_{key}\",\n", " groups=[\"group-a\", \"group-b\"],\n", " definition=coder_definition,\n", - " node_selector={},\n", + " node_selector=node_selector,\n", " volumes=[calrissian_volume, workspace_volume],\n", " config_maps=[\n", " bash_rc_cm,\n", @@ -234,6 +252,9 @@ " \"HOME\": \"/workspace\",\n", " \"CONDA_ENVS_PATH\": \" /workspace/.envs\",\n", " },\n", + " manifests=[localstack_manifest],\n", + " env_from_config_maps=[\"my-config\"],\n", + "\n", " )\n", "\n", " profiles.append(coder_profile)" @@ -248,7 +269,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -295,7 +316,7 @@ " image=\"eoepca/pde-code-server:develop\",\n", " ),\n", " ),\n", - " node_selector={},\n", + " node_selector=node_selector,\n", " volumes=[calrissian_volume, workspace_volume],\n", " config_maps=[init_cm],\n", " pod_env_vars={\n", @@ -311,7 +332,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -333,7 +354,7 @@ " image=\"eoepca/pde-code-server:develop\",\n", " ),\n", " ),\n", - " node_selector={},\n", + " node_selector=node_selector,\n", " volumes=[calrissian_volume, workspace_volume],\n", " config_maps=[init_cm],\n", " pod_env_vars={\n", @@ -356,7 +377,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -379,7 +400,7 @@ " image=image,\n", " ),\n", " ),\n", - " node_selector={},\n", + " node_selector=node_selector,\n", " volumes=[workspace_volume],\n", " config_maps=[],\n", " pod_env_vars={\n", @@ -401,7 +422,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -409,13 +430,28 @@ " name=\"cr-config\",\n", " persist=False,\n", " data=\"\",\n", - " data=\"\",\n", ")" ] }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, + "metadata": {}, + "outputs": [], + "source": [ + "with open(\"manifests/manifest.yaml\", \"r\") as f:\n", + " content = yaml.safe_load_all(f.read())\n", + "\n", + "\n", + "\n", + "localstack_manifest = Manifest(\n", + " name=\"manifests\", key=\"manifests\", readonly=True, persist=False, content=[e for e in content]\n", + ")" + ] + }, + { + "cell_type": "code", + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -438,7 +474,7 @@ " image=image,\n", " ),\n", " ),\n", - " node_selector={},\n", + " node_selector=node_selector,\n", " volumes=[workspace_volume],\n", " config_maps=[],\n", " pod_env_vars={\n", @@ -447,6 +483,7 @@ " \"XDG_CONFIG_HOME\": \"/workspace/.config\",\n", " },\n", " image_pull_secrets=[image_pull_secret],\n", + " manifests=[localstack_manifest],\n", ")\n", "\n", "profiles.append(eoepca_jupyter_lab_profile_2)" @@ -462,7 +499,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -478,24 +515,9 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 17, "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "[Profile(id='profile_studio_coder1', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='Code Server Small', description=None, slug='ellip_studio_coder_slug_s', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=None, mem_limit='8G', mem_guarantee=None, image='eoepca/pde-code-server:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[ConfigMap(name='bash-rc', key='bash-rc', mount_path='/workspace/.bashrc', default_mode=None, readonly=True, content='alias ll=\"ls -l\"\\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\\nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\\n. /home/jovyan/.bashrc\\n# >>> conda initialize >>>\\n# !! Contents within this block are managed by \\'conda init\\' !!\\n__conda_setup=\"$(\\'/opt/conda/bin/conda\\' \\'shell.bash\\' \\'hook\\' 2> /dev/null)\"\\nif [ $? -eq 0 ]; then\\n eval \"$__conda_setup\"\\nelse\\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/conda.sh\"\\n else\\n export PATH=\"/srv/conda/bin:$PATH\"\\n fi\\nfi\\nunset __conda_setup\\n\\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/mamba.sh\"\\nfi\\n# <<< conda initialize <<<\\n', persist=True)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteMany'], volume_mount=VolumeMount(name='calrissian-volume', mount_path='/calrissian'), persist=False), Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': ' /workspace/.envs'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[], manifests=None),\n", - " Profile(id='profile_studio_coder2', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='Code Server Medium', description=None, slug='ellip_studio_coder_slug_m', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=4, cpu_guarantee=None, mem_limit='12G', mem_guarantee=None, image='eoepca/pde-code-server:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[ConfigMap(name='bash-rc', key='bash-rc', mount_path='/workspace/.bashrc', default_mode=None, readonly=True, content='alias ll=\"ls -l\"\\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\\nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\\n. /home/jovyan/.bashrc\\n# >>> conda initialize >>>\\n# !! Contents within this block are managed by \\'conda init\\' !!\\n__conda_setup=\"$(\\'/opt/conda/bin/conda\\' \\'shell.bash\\' \\'hook\\' 2> /dev/null)\"\\nif [ $? -eq 0 ]; then\\n eval \"$__conda_setup\"\\nelse\\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/conda.sh\"\\n else\\n export PATH=\"/srv/conda/bin:$PATH\"\\n fi\\nfi\\nunset __conda_setup\\n\\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\\n . \"/opt/conda/etc/profile.d/mamba.sh\"\\nfi\\n# <<< conda initialize <<<\\n', persist=True)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteMany'], volume_mount=VolumeMount(name='calrissian-volume', mount_path='/calrissian'), persist=False), Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': ' /workspace/.envs'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[], manifests=None),\n", - " Profile(id='profile_demo_init_script', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='Coder demo init script', description='This profile is used to demonstrate the use of an init script', slug='eoepca_demo_init_script', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='eoepca/pde-code-server:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[ConfigMap(name='init', key='init', mount_path='/opt/init/.init.sh', default_mode='0660', readonly=True, content=\"set -x\\n\\ncd /workspace\\n\\ngit clone 'https://github.com/eoap/mastering-app-package.git'\\n\\ncode-server --install-extension ms-python.python\\ncode-server --install-extension redhat.vscode-yaml\\ncode-server --install-extension sbg-rabix.benten-cwl\\ncode-server --install-extension ms-toolsai.jupyter\\n\\nln -s /workspace/.local/share/code-server/extensions /workspace/extensions\\n\\nexit 0\\n\", persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteMany'], volume_mount=VolumeMount(name='calrissian-volume', mount_path='/calrissian'), persist=False), Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': '/workspace/.envs', 'CONDARC': '/workspace/.condarc', 'XDG_RUNTIME_DIR': '/workspace/.local', 'CODE_SERVER_WS': '/workspace/mastering-app-package'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[InitContainer(name='init-file-on-volume', image='eoepca/pde-code-server:develop', command=['sh', '-c', 'sh /opt/init/.init.sh'], volume_mounts=[VolumeMount(name='workspace-volume', mount_path='/workspace'), InitContainerVolumeMount(name='init', mount_path='/opt/init/.init.sh', sub_path='init')])], manifests=None),\n", - " Profile(id='profile_jupyter_lab', groups=['group-c'], definition=ProfileDefinition(display_name='Jupyter Lab', description='Jupyter Lab with Python 3.11', slug='eoepca_jupyter_lab', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='jupyter/scipy-notebook', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'XDG_RUNTIME_DIR': '/workspace/.local', 'XDG_CONFIG_HOME': '/workspace/.config'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[], manifests=None),\n", - " Profile(id='profile_jupyter_lab_2', groups=['group-c'], definition=ProfileDefinition(display_name='Jupyter Lab - profile 2', description='Jupyter Lab with Python 3.11 - demoes the use of an image pull secret', slug='eoepca_jupyter_lab_2', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='jupyter/scipy-notebook', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='standard', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace', 'XDG_RUNTIME_DIR': '/workspace/.local', 'XDG_CONFIG_HOME': '/workspace/.config'}, default_url=None, node_selector={}, role_bindings=None, image_pull_secrets=[ImagePullSecret(name='cr-config', persist=False, data='ewogICAgImF1dGhzIjogewogICAgICAgICJjci50ZXJyYWR1ZS5jb20iOiB7CiAgICAgICAgICAgICJ1c2VybmFtZSI6ICJyb2JvdCRlb2VwY2EtcGx1cy1ybyIsCiAgICAgICAgICAgICJwYXNzd29yZCI6ICJQMlE4TnkyZ0lHODhkZkxveXlLN05QVUZVbHJOekFZSiIsCiAgICAgICAgICAgICJlbWFpbCI6ICJlb2VwY2EtcGx1c0B0ZXJyYWR1ZS5jb20iLAogICAgICAgICAgICAiYXV0aCI6ICJjbTlpYjNRa1pXOWxjR05oTFhCc2RYTXRjbTg2VURKUk9FNTVNbWRKUnpnNFpHWk1iM2w1U3pkT1VGVkdWV3h5VG5wQldVbz0iCiAgICAgICAgfQogICAgfQp9')], init_containers=[], manifests=None)]" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], + "outputs": [], "source": [ "# profiles" ] @@ -524,7 +546,7 @@ ], "metadata": { "kernelspec": { - "display_name": "base", + "display_name": ".env-config-generator", "language": "python", "name": "python3" }, diff --git a/config-generator/manifests/manifest.yaml b/config-generator/manifests/manifest.yaml index d570078..0742bc2 100644 --- a/config-generator/manifests/manifest.yaml +++ b/config-generator/manifests/manifest.yaml @@ -212,6 +212,7 @@ spec: template: metadata: labels: + app: localstack-{{ spawner.user.name }} app.kubernetes.io/name: localstack app.kubernetes.io/instance: localstack spec: @@ -418,4 +419,12 @@ spec: value: "60" - name: OVERRIDE_IN_DOCKER value: "1" - volumes: [] \ No newline at end of file + volumes: [] +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: my-config +data: + ENV_VAR1: value1 + ENV_VAR2: value2 \ No newline at end of file diff --git a/config-generator/models.py b/config-generator/models.py index 02eabdc..799609c 100644 --- a/config-generator/models.py +++ b/config-generator/models.py @@ -140,6 +140,7 @@ class Profile(BaseModel): image_pull_secrets: Optional[List[ImagePullSecret]] = [] init_containers: Optional[List[InitContainer]] = [] manifests: Optional[List[Manifest]] = None + env_from_config_maps: Optional[List[str]] = None class Config(BaseModel): From 3d71d27060e06c271450f0c7ca2f283ebdcd683d Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Sat, 21 Dec 2024 02:13:50 +0100 Subject: [PATCH 39/65] adds env from secrets --- application_hub_context/app_hub_context.py | 21 ++++++++++++++++++++- application_hub_context/models.py | 1 + application_hub_context/parser.py | 10 +++++++++- config-generator/config-generator.ipynb | 1 + config-generator/manifests/manifest.yaml | 11 ++++++++++- config-generator/models.py | 2 +- 6 files changed, 42 insertions(+), 4 deletions(-) diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index efce087..f0152c6 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -579,7 +579,8 @@ def unapply_manifests(self, manifest_content): self.core_v1_api.delete_namespaced_service_account(name, namespace) elif kind == "ConfigMap": self.core_v1_api.delete_namespaced_config_map(name, namespace) - + elif kind == "Secret": + self.core_v1_api.delete_namespaced_secret(name, namespace) # Add other kinds as needed else: self.spawner.log.error(f"Unsupported kind: {kind}") @@ -910,6 +911,24 @@ def initialise(self): }}) self.spawner.log.info(f"extra_container_config {self.spawner.extra_container_config}") + # process the pod env vars from secrets + env_from_secrets = self.config_parser.get_profile_env_from_secrets( + profile_id=profile_id + ) + self.spawner.log.info(f"env_from_secrets {env_from_secrets}") + if env_from_secrets and self.spawner.extra_container_config["env_from"] is None: + self.spawner.extra_container_config["env_from"] = [] + + for env_from_secret in env_from_secrets: + self.spawner.log.info(f"env_from_secret {env_from_secret}") + self.spawner.extra_container_config["env_from"].append( + { + "secretRef": { + "name": env_from_secret, + }}) + self.spawner.log.info(f"extra_container_config {self.spawner.extra_container_config}") + + def dispose(self): profile_id = self.config_parser.get_profile_by_slug(slug=self.profile_slug).id diff --git a/application_hub_context/models.py b/application_hub_context/models.py index 0c5132a..a78df6c 100644 --- a/application_hub_context/models.py +++ b/application_hub_context/models.py @@ -178,6 +178,7 @@ class Profile(BaseModel): init_containers: Optional[List[InitContainer]] = None manifests: Optional[List[Manifest]] = None env_from_config_maps: Optional[List[str]] = None + env_from_secrets: Optional[List[str]] = None class Config(BaseModel): profiles: List[Profile] diff --git a/application_hub_context/parser.py b/application_hub_context/parser.py index 920c8c4..ed6e035 100644 --- a/application_hub_context/parser.py +++ b/application_hub_context/parser.py @@ -137,4 +137,12 @@ def get_profile_env_from_config_maps(self, profile_id): try: return self.get_profile_by_id(profile_id=profile_id).env_from_config_maps except AttributeError: - pass \ No newline at end of file + pass + + def get_profile_env_from_secrets(self, profile_id): + """returns the profile env from secrets""" + try: + return self.get_profile_by_id(profile_id=profile_id).env_from_secrets + except AttributeError: + pass + \ No newline at end of file diff --git a/config-generator/config-generator.ipynb b/config-generator/config-generator.ipynb index 7a87bb3..8c9ae07 100644 --- a/config-generator/config-generator.ipynb +++ b/config-generator/config-generator.ipynb @@ -254,6 +254,7 @@ " },\n", " manifests=[localstack_manifest],\n", " env_from_config_maps=[\"my-config\"],\n", + " env_from_secrets=[\"my-secret\"],\n", "\n", " )\n", "\n", diff --git a/config-generator/manifests/manifest.yaml b/config-generator/manifests/manifest.yaml index 0742bc2..372648e 100644 --- a/config-generator/manifests/manifest.yaml +++ b/config-generator/manifests/manifest.yaml @@ -427,4 +427,13 @@ metadata: name: my-config data: ENV_VAR1: value1 - ENV_VAR2: value2 \ No newline at end of file + ENV_VAR2: value2 +--- +apiVersion: v1 +kind: Secret +metadata: + name: my-secret +type: Opaque +data: + SECRET_KEY1: dmFsdWUx # base64 for "value1" + SECRET_KEY2: dmFsdWUy # base64 for "value2" \ No newline at end of file diff --git a/config-generator/models.py b/config-generator/models.py index 799609c..c3f7a0c 100644 --- a/config-generator/models.py +++ b/config-generator/models.py @@ -141,7 +141,7 @@ class Profile(BaseModel): init_containers: Optional[List[InitContainer]] = [] manifests: Optional[List[Manifest]] = None env_from_config_maps: Optional[List[str]] = None - + env_from_secrets: Optional[List[str]] = None class Config(BaseModel): """config object""" From da198ba54ea9414b831ce16e3f8f3185c67fca86 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Sat, 21 Dec 2024 07:47:07 +0100 Subject: [PATCH 40/65] adds secret mounts --- application_hub_context/app_hub_context.py | 29 +++++++++++++++++ application_hub_context/models.py | 5 +++ application_hub_context/parser.py | 8 ++++- config-generator/config-generator.ipynb | 36 +++++++++++----------- config-generator/manifests/manifest.yaml | 10 +++++- config-generator/models.py | 6 ++++ 6 files changed, 74 insertions(+), 20 deletions(-) diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index f0152c6..282807f 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -929,6 +929,35 @@ def initialise(self): self.spawner.log.info(f"extra_container_config {self.spawner.extra_container_config}") + secret_mounts = self.config_parser.get_profile_secret_mounts( + profile_id=profile_id + ) + + if secret_mounts: + for secret_mount in secret_mounts: + self.spawner.log.info(f"Mounting secret {secret_mount.name}") + self.spawner.volume_mounts.extend( + [ + { + "name": secret_mount.name, + "mountPath": secret_mount.mount_path, + }, + ] + ) + + self.spawner.volumes.extend( + [ + { + "name": secret_mount.name, + "secret": { + "secretName": secret_mount.name, + }, + } + ] + ) + + self.spawner.log.info(f"Mounted secret {secret_mount.name}") + def dispose(self): profile_id = self.config_parser.get_profile_by_slug(slug=self.profile_slug).id diff --git a/application_hub_context/models.py b/application_hub_context/models.py index a78df6c..c049c3a 100644 --- a/application_hub_context/models.py +++ b/application_hub_context/models.py @@ -163,6 +163,10 @@ class ImagePullSecret(BaseModel): persist: bool = True data: Optional[str] = None +class SecretMount(BaseModel): + name: str + mount_path: str + class Profile(BaseModel): id: str @@ -179,6 +183,7 @@ class Profile(BaseModel): manifests: Optional[List[Manifest]] = None env_from_config_maps: Optional[List[str]] = None env_from_secrets: Optional[List[str]] = None + secret_mounts: Optional[List[SecretMount]] = None class Config(BaseModel): profiles: List[Profile] diff --git a/application_hub_context/parser.py b/application_hub_context/parser.py index ed6e035..a60c290 100644 --- a/application_hub_context/parser.py +++ b/application_hub_context/parser.py @@ -145,4 +145,10 @@ def get_profile_env_from_secrets(self, profile_id): return self.get_profile_by_id(profile_id=profile_id).env_from_secrets except AttributeError: pass - \ No newline at end of file + + def get_profile_secret_mounts(self, profile_id): + """returns the profile secret mounts""" + try: + return self.get_profile_by_id(profile_id=profile_id).secret_mounts + except AttributeError: + pass \ No newline at end of file diff --git a/config-generator/config-generator.ipynb b/config-generator/config-generator.ipynb index 8c9ae07..7bfd655 100644 --- a/config-generator/config-generator.ipynb +++ b/config-generator/config-generator.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 1, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -38,7 +38,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -73,7 +73,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -101,7 +101,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -138,7 +138,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -165,7 +165,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -191,7 +191,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -208,7 +208,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -255,7 +255,7 @@ " manifests=[localstack_manifest],\n", " env_from_config_maps=[\"my-config\"],\n", " env_from_secrets=[\"my-secret\"],\n", - "\n", + " secret_mounts=[SecretMount(name=\"aws-credentials\", mount_path=\"/workspace/.aws\")],\n", " )\n", "\n", " profiles.append(coder_profile)" @@ -270,7 +270,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -333,7 +333,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -378,7 +378,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -423,7 +423,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -436,7 +436,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -452,7 +452,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -500,7 +500,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ @@ -516,7 +516,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 18, "metadata": {}, "outputs": [], "source": [ diff --git a/config-generator/manifests/manifest.yaml b/config-generator/manifests/manifest.yaml index 372648e..472e1e1 100644 --- a/config-generator/manifests/manifest.yaml +++ b/config-generator/manifests/manifest.yaml @@ -436,4 +436,12 @@ metadata: type: Opaque data: SECRET_KEY1: dmFsdWUx # base64 for "value1" - SECRET_KEY2: dmFsdWUy # base64 for "value2" \ No newline at end of file + SECRET_KEY2: dmFsdWUy # base64 for "value2" +--- +apiVersion: v1 +kind: Secret +metadata: + name: aws-credentials +type: Opaque +data: + credentials: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg= diff --git a/config-generator/models.py b/config-generator/models.py index c3f7a0c..03b91c3 100644 --- a/config-generator/models.py +++ b/config-generator/models.py @@ -124,6 +124,10 @@ class ImagePullSecret(BaseModel): persist: bool = True data: Optional[str] = None +class SecretMount(BaseModel): + name: str + mount_path: str + class Profile(BaseModel): """profile object""" @@ -142,6 +146,8 @@ class Profile(BaseModel): manifests: Optional[List[Manifest]] = None env_from_config_maps: Optional[List[str]] = None env_from_secrets: Optional[List[str]] = None + secret_mounts: Optional[List[SecretMount]] = None + class Config(BaseModel): """config object""" From 6f93dfc40a888168adbcc3181a287ea66798891e Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Sat, 21 Dec 2024 09:12:02 +0100 Subject: [PATCH 41/65] add crossplane jelm releases --- application_hub_context/app_hub_context.py | 43 +- config-generator/config-generator.ipynb | 34 +- config-generator/manifests/manifest.yaml | 18 + files/hub/config.yml | 1480 +++++++++++++++++++- 4 files changed, 1534 insertions(+), 41 deletions(-) diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index 282807f..84e226f 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -38,6 +38,7 @@ def __init__( self.batch_v1_api = self._get_batch_v1_api() self.apps_v1_api = self._get_apps_v1_api() self.rbac_authorization_v1_api = self._get_rbac_authorization_v1_api() + self.custom_objects_api = self._get_custom_objects_api() self.namespace = namespace self.spawner = spawner # get the groups the user belongs to @@ -127,6 +128,9 @@ def _get_rbac_authorization_v1_api(self) -> client.RbacAuthorizationApi: def _get_apps_v1_api(self) -> client.AppsV1Api: return client.AppsV1Api(self.api_client) + + def _get_custom_objects_api(self) -> client.CustomObjectsApi: + return client.CustomObjectsApi(self.api_client) def is_object_created(self, read_method, **kwargs): read_methods = {} @@ -541,12 +545,32 @@ def apply_manifest(self, manifest): self.spawner.log.info(f"Applying manifest: {yaml.safe_load(rendered_manifest)}") - create_from_dict( - k8s_client=self.api_client, - data=yaml.safe_load(rendered_manifest), - verbose=True, - namespace=self.namespace, - ) + if yaml.safe_load(rendered_manifest)["kind"] in "Release": + # see https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/CustomObjectsApi.md#create_namespaced_custom_object + # Define the Crossplane HelmRelease details + group = "helm.crossplane.io" # API group for Crossplane HelmRelease + version = "v1beta1" # API version + namespace = f"jupyter-{self.spawner.user.name}" # Namespace of the HelmRelease + plural = "releases" # Resource type + + self.spawner.log.info(f"Creating Crossplane HelmRelease in namespace {namespace}") + + # Create the Crossplane HelmRelease + self.custom_objects_api.create_cluster_custom_object( + group=group, + version=version, + #namespace=namespace, + plural=plural, + body=yaml.safe_load(rendered_manifest) + ) + else: + self.spawner.log.info(f"Creating K8s object in namespace {self.namespace}") + create_from_dict( + k8s_client=self.api_client, + data=yaml.safe_load(rendered_manifest), + verbose=True, + namespace=self.namespace, + ) def unapply_manifests(self, manifest_content): @@ -581,6 +605,13 @@ def unapply_manifests(self, manifest_content): self.core_v1_api.delete_namespaced_config_map(name, namespace) elif kind == "Secret": self.core_v1_api.delete_namespaced_secret(name, namespace) + elif kind == "Release": + self.custom_objects_api.delete_cluster_custom_object( + group="helm.crossplane.io", + version="v1beta1", + plural="releases", + name=name, + ) # Add other kinds as needed else: self.spawner.log.error(f"Unsupported kind: {kind}") diff --git a/config-generator/config-generator.ipynb b/config-generator/config-generator.ipynb index 7bfd655..fcd6aa2 100644 --- a/config-generator/config-generator.ipynb +++ b/config-generator/config-generator.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 2, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ @@ -13,7 +13,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -38,7 +38,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -73,7 +73,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -101,7 +101,7 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 5, "metadata": {}, "outputs": [], "source": [ @@ -138,7 +138,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -165,7 +165,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ @@ -191,7 +191,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -208,7 +208,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -270,7 +270,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 10, "metadata": {}, "outputs": [], "source": [ @@ -333,7 +333,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -378,7 +378,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -423,7 +423,7 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -436,7 +436,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 14, "metadata": {}, "outputs": [], "source": [ @@ -452,7 +452,7 @@ }, { "cell_type": "code", - "execution_count": 16, + "execution_count": 15, "metadata": {}, "outputs": [], "source": [ @@ -500,7 +500,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 16, "metadata": {}, "outputs": [], "source": [ @@ -516,7 +516,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 17, "metadata": {}, "outputs": [], "source": [ diff --git a/config-generator/manifests/manifest.yaml b/config-generator/manifests/manifest.yaml index 472e1e1..2d07c63 100644 --- a/config-generator/manifests/manifest.yaml +++ b/config-generator/manifests/manifest.yaml @@ -445,3 +445,21 @@ metadata: type: Opaque data: credentials: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg= +--- +apiVersion: helm.crossplane.io/v1beta1 +kind: Release +metadata: + name: wordpress-release + namespace: jupyter-{{ spawner.user.name }} +spec: + forProvider: + chart: + name: wordpress + version: "24.1.4" + repository: oci://registry-1.docker.io/bitnamicharts/ + namespace: jupyter-{{ spawner.user.name }} + values: + service: + type: ClusterIP + providerConfigRef: + name: helm-provider diff --git a/files/hub/config.yml b/files/hub/config.yml index c6f7db9..efcd8eb 100644 --- a/files/hub/config.yml +++ b/files/hub/config.yml @@ -4,13 +4,14 @@ profiles: \ /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram\ \ 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\ - # >>> conda initialize >>>\n# !! Contents within this block are managed by 'conda\ - \ init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' 2>\ - \ /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n \ - \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ - \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset\ - \ __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ - \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n" + \nalias aws=\"aws --endpoint-url=http://localstack:4566\"\n\n# >>> conda initialize\ + \ >>>\n# !! Contents within this block are managed by 'conda init' !!\n__conda_setup=\"\ + $('/opt/conda/bin/conda' 'shell.bash' 'hook' 2> /dev/null)\"\nif [ $? -eq 0\ + \ ]; then\n eval \"$__conda_setup\"\nelse\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\"\ + \ ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\n else\n \ + \ export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset __conda_setup\n\n\ + if [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n . \"/opt/conda/etc/profile.d/mamba.sh\"\ + \nfi\n# <<< conda initialize <<<\n" default_mode: null key: bash-rc mount_path: /workspace/.bashrc @@ -31,18 +32,498 @@ profiles: mem_guarantee: null mem_limit: 8G slug: eoepca_coder_slug_s + env_from_config_maps: + - my-config + env_from_secrets: + - my-secret groups: - group-a - group-b id: profile_studio_coder1 image_pull_secrets: [] init_containers: [] - manifests: null + manifests: + - content: + - apiVersion: v1 + kind: ServiceAccount + metadata: + name: localstack + - apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + name: localstack + rules: + - apiGroups: + - '' + resources: + - pods + verbs: + - '*' + - apiGroups: + - '' + resources: + - pods/log + verbs: + - get + - apiGroups: + - '' + resources: + - pods/exec + verbs: + - get + - create + - apiGroups: + - '' + resources: + - services + verbs: + - get + - list + - apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + name: localstack + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: localstack + subjects: + - kind: ServiceAccount + name: localstack + - apiVersion: v1 + kind: Service + metadata: + name: localstack + spec: + ports: + - name: edge + port: 4566 + targetPort: 4566 + - name: external-service-port-4510 + port: 4510 + targetPort: ext-svc-4510 + - name: external-service-port-4511 + port: 4511 + targetPort: ext-svc-4511 + - name: external-service-port-4512 + port: 4512 + targetPort: ext-svc-4512 + - name: external-service-port-4513 + port: 4513 + targetPort: ext-svc-4513 + - name: external-service-port-4514 + port: 4514 + targetPort: ext-svc-4514 + - name: external-service-port-4515 + port: 4515 + targetPort: ext-svc-4515 + - name: external-service-port-4516 + port: 4516 + targetPort: ext-svc-4516 + - name: external-service-port-4517 + port: 4517 + targetPort: ext-svc-4517 + - name: external-service-port-4518 + port: 4518 + targetPort: ext-svc-4518 + - name: external-service-port-4519 + port: 4519 + targetPort: ext-svc-4519 + - name: external-service-port-4520 + port: 4520 + targetPort: ext-svc-4520 + - name: external-service-port-4521 + port: 4521 + targetPort: ext-svc-4521 + - name: external-service-port-4522 + port: 4522 + targetPort: ext-svc-4522 + - name: external-service-port-4523 + port: 4523 + targetPort: ext-svc-4523 + - name: external-service-port-4524 + port: 4524 + targetPort: ext-svc-4524 + - name: external-service-port-4525 + port: 4525 + targetPort: ext-svc-4525 + - name: external-service-port-4526 + port: 4526 + targetPort: ext-svc-4526 + - name: external-service-port-4527 + port: 4527 + targetPort: ext-svc-4527 + - name: external-service-port-4528 + port: 4528 + targetPort: ext-svc-4528 + - name: external-service-port-4529 + port: 4529 + targetPort: ext-svc-4529 + - name: external-service-port-4530 + port: 4530 + targetPort: ext-svc-4530 + - name: external-service-port-4531 + port: 4531 + targetPort: ext-svc-4531 + - name: external-service-port-4532 + port: 4532 + targetPort: ext-svc-4532 + - name: external-service-port-4533 + port: 4533 + targetPort: ext-svc-4533 + - name: external-service-port-4534 + port: 4534 + targetPort: ext-svc-4534 + - name: external-service-port-4535 + port: 4535 + targetPort: ext-svc-4535 + - name: external-service-port-4536 + port: 4536 + targetPort: ext-svc-4536 + - name: external-service-port-4537 + port: 4537 + targetPort: ext-svc-4537 + - name: external-service-port-4538 + port: 4538 + targetPort: ext-svc-4538 + - name: external-service-port-4539 + port: 4539 + targetPort: ext-svc-4539 + - name: external-service-port-4540 + port: 4540 + targetPort: ext-svc-4540 + - name: external-service-port-4541 + port: 4541 + targetPort: ext-svc-4541 + - name: external-service-port-4542 + port: 4542 + targetPort: ext-svc-4542 + - name: external-service-port-4543 + port: 4543 + targetPort: ext-svc-4543 + - name: external-service-port-4544 + port: 4544 + targetPort: ext-svc-4544 + - name: external-service-port-4545 + port: 4545 + targetPort: ext-svc-4545 + - name: external-service-port-4546 + port: 4546 + targetPort: ext-svc-4546 + - name: external-service-port-4547 + port: 4547 + targetPort: ext-svc-4547 + - name: external-service-port-4548 + port: 4548 + targetPort: ext-svc-4548 + - name: external-service-port-4549 + port: 4549 + targetPort: ext-svc-4549 + - name: external-service-port-4550 + port: 4550 + targetPort: ext-svc-4550 + - name: external-service-port-4551 + port: 4551 + targetPort: ext-svc-4551 + - name: external-service-port-4552 + port: 4552 + targetPort: ext-svc-4552 + - name: external-service-port-4553 + port: 4553 + targetPort: ext-svc-4553 + - name: external-service-port-4554 + port: 4554 + targetPort: ext-svc-4554 + - name: external-service-port-4555 + port: 4555 + targetPort: ext-svc-4555 + - name: external-service-port-4556 + port: 4556 + targetPort: ext-svc-4556 + - name: external-service-port-4557 + port: 4557 + targetPort: ext-svc-4557 + - name: external-service-port-4558 + port: 4558 + targetPort: ext-svc-4558 + - name: external-service-port-4559 + port: 4559 + targetPort: ext-svc-4559 + selector: + app.kubernetes.io/instance: localstack + app.kubernetes.io/name: localstack + type: ClusterIP + - apiVersion: apps/v1 + kind: Deployment + metadata: + name: localstack + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: localstack + app.kubernetes.io/name: localstack + strategy: + type: RollingUpdate + template: + metadata: + labels: + app: localstack-{{ spawner.user.name }} + app.kubernetes.io/instance: localstack + app.kubernetes.io/name: localstack + spec: + containers: + - env: + - name: DEBUG + value: '0' + - name: EXTERNAL_SERVICE_PORTS_START + value: '4510' + - name: EXTERNAL_SERVICE_PORTS_END + value: '4560' + - name: LOCALSTACK_K8S_SERVICE_NAME + value: localstack + - name: LOCALSTACK_K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: LAMBDA_RUNTIME_EXECUTOR + value: docker + - name: LAMBDA_K8S_IMAGE_PREFIX + value: localstack/lambda- + - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT + value: '60' + - name: OVERRIDE_IN_DOCKER + value: '1' + image: localstack/localstack:latest + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + path: /_localstack/health + port: edge + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: localstack + ports: + - containerPort: 4566 + name: edge + protocol: TCP + - containerPort: 4510 + name: ext-svc-4510 + protocol: TCP + - containerPort: 4511 + name: ext-svc-4511 + protocol: TCP + - containerPort: 4512 + name: ext-svc-4512 + protocol: TCP + - containerPort: 4513 + name: ext-svc-4513 + protocol: TCP + - containerPort: 4514 + name: ext-svc-4514 + protocol: TCP + - containerPort: 4515 + name: ext-svc-4515 + protocol: TCP + - containerPort: 4516 + name: ext-svc-4516 + protocol: TCP + - containerPort: 4517 + name: ext-svc-4517 + protocol: TCP + - containerPort: 4518 + name: ext-svc-4518 + protocol: TCP + - containerPort: 4519 + name: ext-svc-4519 + protocol: TCP + - containerPort: 4520 + name: ext-svc-4520 + protocol: TCP + - containerPort: 4521 + name: ext-svc-4521 + protocol: TCP + - containerPort: 4522 + name: ext-svc-4522 + protocol: TCP + - containerPort: 4523 + name: ext-svc-4523 + protocol: TCP + - containerPort: 4524 + name: ext-svc-4524 + protocol: TCP + - containerPort: 4525 + name: ext-svc-4525 + protocol: TCP + - containerPort: 4526 + name: ext-svc-4526 + protocol: TCP + - containerPort: 4527 + name: ext-svc-4527 + protocol: TCP + - containerPort: 4528 + name: ext-svc-4528 + protocol: TCP + - containerPort: 4529 + name: ext-svc-4529 + protocol: TCP + - containerPort: 4530 + name: ext-svc-4530 + protocol: TCP + - containerPort: 4531 + name: ext-svc-4531 + protocol: TCP + - containerPort: 4532 + name: ext-svc-4532 + protocol: TCP + - containerPort: 4533 + name: ext-svc-4533 + protocol: TCP + - containerPort: 4534 + name: ext-svc-4534 + protocol: TCP + - containerPort: 4535 + name: ext-svc-4535 + protocol: TCP + - containerPort: 4536 + name: ext-svc-4536 + protocol: TCP + - containerPort: 4537 + name: ext-svc-4537 + protocol: TCP + - containerPort: 4538 + name: ext-svc-4538 + protocol: TCP + - containerPort: 4539 + name: ext-svc-4539 + protocol: TCP + - containerPort: 4540 + name: ext-svc-4540 + protocol: TCP + - containerPort: 4541 + name: ext-svc-4541 + protocol: TCP + - containerPort: 4542 + name: ext-svc-4542 + protocol: TCP + - containerPort: 4543 + name: ext-svc-4543 + protocol: TCP + - containerPort: 4544 + name: ext-svc-4544 + protocol: TCP + - containerPort: 4545 + name: ext-svc-4545 + protocol: TCP + - containerPort: 4546 + name: ext-svc-4546 + protocol: TCP + - containerPort: 4547 + name: ext-svc-4547 + protocol: TCP + - containerPort: 4548 + name: ext-svc-4548 + protocol: TCP + - containerPort: 4549 + name: ext-svc-4549 + protocol: TCP + - containerPort: 4550 + name: ext-svc-4550 + protocol: TCP + - containerPort: 4551 + name: ext-svc-4551 + protocol: TCP + - containerPort: 4552 + name: ext-svc-4552 + protocol: TCP + - containerPort: 4553 + name: ext-svc-4553 + protocol: TCP + - containerPort: 4554 + name: ext-svc-4554 + protocol: TCP + - containerPort: 4555 + name: ext-svc-4555 + protocol: TCP + - containerPort: 4556 + name: ext-svc-4556 + protocol: TCP + - containerPort: 4557 + name: ext-svc-4557 + protocol: TCP + - containerPort: 4558 + name: ext-svc-4558 + protocol: TCP + - containerPort: 4559 + name: ext-svc-4559 + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /_localstack/health + port: edge + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: {} + securityContext: {} + securityContext: {} + serviceAccountName: localstack + volumes: [] + - apiVersion: v1 + data: + ENV_VAR1: value1 + ENV_VAR2: value2 + kind: ConfigMap + metadata: + name: my-config + - apiVersion: v1 + data: + SECRET_KEY1: dmFsdWUx + SECRET_KEY2: dmFsdWUy + kind: Secret + metadata: + name: my-secret + type: Opaque + - apiVersion: v1 + data: + credentials: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg= + kind: Secret + metadata: + name: aws-credentials + type: Opaque + - apiVersion: helm.crossplane.io/v1beta1 + kind: Release + metadata: + name: wordpress-release + namespace: jupyter-{{ spawner.user.name }} + spec: + forProvider: + chart: + name: wordpress + repository: oci://registry-1.docker.io/bitnamicharts/ + version: 24.1.4 + namespace: jupyter-{{ spawner.user.name }} + values: + service: + type: ClusterIP + providerConfigRef: + name: helm-provider + key: manifests + name: manifests + persist: false node_selector: {} pod_env_vars: CONDA_ENVS_PATH: ' /workspace/.envs' HOME: /workspace role_bindings: null + secret_mounts: + - mount_path: /workspace/.aws + name: aws-credentials volumes: - access_modes: - ReadWriteMany @@ -69,13 +550,14 @@ profiles: \ /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram\ \ 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\ - # >>> conda initialize >>>\n# !! Contents within this block are managed by 'conda\ - \ init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' 2>\ - \ /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n \ - \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ - \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset\ - \ __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ - \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n" + \nalias aws=\"aws --endpoint-url=http://localstack:4566\"\n\n# >>> conda initialize\ + \ >>>\n# !! Contents within this block are managed by 'conda init' !!\n__conda_setup=\"\ + $('/opt/conda/bin/conda' 'shell.bash' 'hook' 2> /dev/null)\"\nif [ $? -eq 0\ + \ ]; then\n eval \"$__conda_setup\"\nelse\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\"\ + \ ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\n else\n \ + \ export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset __conda_setup\n\n\ + if [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n . \"/opt/conda/etc/profile.d/mamba.sh\"\ + \nfi\n# <<< conda initialize <<<\n" default_mode: null key: bash-rc mount_path: /workspace/.bashrc @@ -96,18 +578,498 @@ profiles: mem_guarantee: null mem_limit: 12G slug: eoepca_coder_slug_m + env_from_config_maps: + - my-config + env_from_secrets: + - my-secret groups: - group-a - group-b id: profile_studio_coder2 image_pull_secrets: [] init_containers: [] - manifests: null + manifests: + - content: + - apiVersion: v1 + kind: ServiceAccount + metadata: + name: localstack + - apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + name: localstack + rules: + - apiGroups: + - '' + resources: + - pods + verbs: + - '*' + - apiGroups: + - '' + resources: + - pods/log + verbs: + - get + - apiGroups: + - '' + resources: + - pods/exec + verbs: + - get + - create + - apiGroups: + - '' + resources: + - services + verbs: + - get + - list + - apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + name: localstack + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: localstack + subjects: + - kind: ServiceAccount + name: localstack + - apiVersion: v1 + kind: Service + metadata: + name: localstack + spec: + ports: + - name: edge + port: 4566 + targetPort: 4566 + - name: external-service-port-4510 + port: 4510 + targetPort: ext-svc-4510 + - name: external-service-port-4511 + port: 4511 + targetPort: ext-svc-4511 + - name: external-service-port-4512 + port: 4512 + targetPort: ext-svc-4512 + - name: external-service-port-4513 + port: 4513 + targetPort: ext-svc-4513 + - name: external-service-port-4514 + port: 4514 + targetPort: ext-svc-4514 + - name: external-service-port-4515 + port: 4515 + targetPort: ext-svc-4515 + - name: external-service-port-4516 + port: 4516 + targetPort: ext-svc-4516 + - name: external-service-port-4517 + port: 4517 + targetPort: ext-svc-4517 + - name: external-service-port-4518 + port: 4518 + targetPort: ext-svc-4518 + - name: external-service-port-4519 + port: 4519 + targetPort: ext-svc-4519 + - name: external-service-port-4520 + port: 4520 + targetPort: ext-svc-4520 + - name: external-service-port-4521 + port: 4521 + targetPort: ext-svc-4521 + - name: external-service-port-4522 + port: 4522 + targetPort: ext-svc-4522 + - name: external-service-port-4523 + port: 4523 + targetPort: ext-svc-4523 + - name: external-service-port-4524 + port: 4524 + targetPort: ext-svc-4524 + - name: external-service-port-4525 + port: 4525 + targetPort: ext-svc-4525 + - name: external-service-port-4526 + port: 4526 + targetPort: ext-svc-4526 + - name: external-service-port-4527 + port: 4527 + targetPort: ext-svc-4527 + - name: external-service-port-4528 + port: 4528 + targetPort: ext-svc-4528 + - name: external-service-port-4529 + port: 4529 + targetPort: ext-svc-4529 + - name: external-service-port-4530 + port: 4530 + targetPort: ext-svc-4530 + - name: external-service-port-4531 + port: 4531 + targetPort: ext-svc-4531 + - name: external-service-port-4532 + port: 4532 + targetPort: ext-svc-4532 + - name: external-service-port-4533 + port: 4533 + targetPort: ext-svc-4533 + - name: external-service-port-4534 + port: 4534 + targetPort: ext-svc-4534 + - name: external-service-port-4535 + port: 4535 + targetPort: ext-svc-4535 + - name: external-service-port-4536 + port: 4536 + targetPort: ext-svc-4536 + - name: external-service-port-4537 + port: 4537 + targetPort: ext-svc-4537 + - name: external-service-port-4538 + port: 4538 + targetPort: ext-svc-4538 + - name: external-service-port-4539 + port: 4539 + targetPort: ext-svc-4539 + - name: external-service-port-4540 + port: 4540 + targetPort: ext-svc-4540 + - name: external-service-port-4541 + port: 4541 + targetPort: ext-svc-4541 + - name: external-service-port-4542 + port: 4542 + targetPort: ext-svc-4542 + - name: external-service-port-4543 + port: 4543 + targetPort: ext-svc-4543 + - name: external-service-port-4544 + port: 4544 + targetPort: ext-svc-4544 + - name: external-service-port-4545 + port: 4545 + targetPort: ext-svc-4545 + - name: external-service-port-4546 + port: 4546 + targetPort: ext-svc-4546 + - name: external-service-port-4547 + port: 4547 + targetPort: ext-svc-4547 + - name: external-service-port-4548 + port: 4548 + targetPort: ext-svc-4548 + - name: external-service-port-4549 + port: 4549 + targetPort: ext-svc-4549 + - name: external-service-port-4550 + port: 4550 + targetPort: ext-svc-4550 + - name: external-service-port-4551 + port: 4551 + targetPort: ext-svc-4551 + - name: external-service-port-4552 + port: 4552 + targetPort: ext-svc-4552 + - name: external-service-port-4553 + port: 4553 + targetPort: ext-svc-4553 + - name: external-service-port-4554 + port: 4554 + targetPort: ext-svc-4554 + - name: external-service-port-4555 + port: 4555 + targetPort: ext-svc-4555 + - name: external-service-port-4556 + port: 4556 + targetPort: ext-svc-4556 + - name: external-service-port-4557 + port: 4557 + targetPort: ext-svc-4557 + - name: external-service-port-4558 + port: 4558 + targetPort: ext-svc-4558 + - name: external-service-port-4559 + port: 4559 + targetPort: ext-svc-4559 + selector: + app.kubernetes.io/instance: localstack + app.kubernetes.io/name: localstack + type: ClusterIP + - apiVersion: apps/v1 + kind: Deployment + metadata: + name: localstack + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: localstack + app.kubernetes.io/name: localstack + strategy: + type: RollingUpdate + template: + metadata: + labels: + app: localstack-{{ spawner.user.name }} + app.kubernetes.io/instance: localstack + app.kubernetes.io/name: localstack + spec: + containers: + - env: + - name: DEBUG + value: '0' + - name: EXTERNAL_SERVICE_PORTS_START + value: '4510' + - name: EXTERNAL_SERVICE_PORTS_END + value: '4560' + - name: LOCALSTACK_K8S_SERVICE_NAME + value: localstack + - name: LOCALSTACK_K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: LAMBDA_RUNTIME_EXECUTOR + value: docker + - name: LAMBDA_K8S_IMAGE_PREFIX + value: localstack/lambda- + - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT + value: '60' + - name: OVERRIDE_IN_DOCKER + value: '1' + image: localstack/localstack:latest + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + path: /_localstack/health + port: edge + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: localstack + ports: + - containerPort: 4566 + name: edge + protocol: TCP + - containerPort: 4510 + name: ext-svc-4510 + protocol: TCP + - containerPort: 4511 + name: ext-svc-4511 + protocol: TCP + - containerPort: 4512 + name: ext-svc-4512 + protocol: TCP + - containerPort: 4513 + name: ext-svc-4513 + protocol: TCP + - containerPort: 4514 + name: ext-svc-4514 + protocol: TCP + - containerPort: 4515 + name: ext-svc-4515 + protocol: TCP + - containerPort: 4516 + name: ext-svc-4516 + protocol: TCP + - containerPort: 4517 + name: ext-svc-4517 + protocol: TCP + - containerPort: 4518 + name: ext-svc-4518 + protocol: TCP + - containerPort: 4519 + name: ext-svc-4519 + protocol: TCP + - containerPort: 4520 + name: ext-svc-4520 + protocol: TCP + - containerPort: 4521 + name: ext-svc-4521 + protocol: TCP + - containerPort: 4522 + name: ext-svc-4522 + protocol: TCP + - containerPort: 4523 + name: ext-svc-4523 + protocol: TCP + - containerPort: 4524 + name: ext-svc-4524 + protocol: TCP + - containerPort: 4525 + name: ext-svc-4525 + protocol: TCP + - containerPort: 4526 + name: ext-svc-4526 + protocol: TCP + - containerPort: 4527 + name: ext-svc-4527 + protocol: TCP + - containerPort: 4528 + name: ext-svc-4528 + protocol: TCP + - containerPort: 4529 + name: ext-svc-4529 + protocol: TCP + - containerPort: 4530 + name: ext-svc-4530 + protocol: TCP + - containerPort: 4531 + name: ext-svc-4531 + protocol: TCP + - containerPort: 4532 + name: ext-svc-4532 + protocol: TCP + - containerPort: 4533 + name: ext-svc-4533 + protocol: TCP + - containerPort: 4534 + name: ext-svc-4534 + protocol: TCP + - containerPort: 4535 + name: ext-svc-4535 + protocol: TCP + - containerPort: 4536 + name: ext-svc-4536 + protocol: TCP + - containerPort: 4537 + name: ext-svc-4537 + protocol: TCP + - containerPort: 4538 + name: ext-svc-4538 + protocol: TCP + - containerPort: 4539 + name: ext-svc-4539 + protocol: TCP + - containerPort: 4540 + name: ext-svc-4540 + protocol: TCP + - containerPort: 4541 + name: ext-svc-4541 + protocol: TCP + - containerPort: 4542 + name: ext-svc-4542 + protocol: TCP + - containerPort: 4543 + name: ext-svc-4543 + protocol: TCP + - containerPort: 4544 + name: ext-svc-4544 + protocol: TCP + - containerPort: 4545 + name: ext-svc-4545 + protocol: TCP + - containerPort: 4546 + name: ext-svc-4546 + protocol: TCP + - containerPort: 4547 + name: ext-svc-4547 + protocol: TCP + - containerPort: 4548 + name: ext-svc-4548 + protocol: TCP + - containerPort: 4549 + name: ext-svc-4549 + protocol: TCP + - containerPort: 4550 + name: ext-svc-4550 + protocol: TCP + - containerPort: 4551 + name: ext-svc-4551 + protocol: TCP + - containerPort: 4552 + name: ext-svc-4552 + protocol: TCP + - containerPort: 4553 + name: ext-svc-4553 + protocol: TCP + - containerPort: 4554 + name: ext-svc-4554 + protocol: TCP + - containerPort: 4555 + name: ext-svc-4555 + protocol: TCP + - containerPort: 4556 + name: ext-svc-4556 + protocol: TCP + - containerPort: 4557 + name: ext-svc-4557 + protocol: TCP + - containerPort: 4558 + name: ext-svc-4558 + protocol: TCP + - containerPort: 4559 + name: ext-svc-4559 + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /_localstack/health + port: edge + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: {} + securityContext: {} + securityContext: {} + serviceAccountName: localstack + volumes: [] + - apiVersion: v1 + data: + ENV_VAR1: value1 + ENV_VAR2: value2 + kind: ConfigMap + metadata: + name: my-config + - apiVersion: v1 + data: + SECRET_KEY1: dmFsdWUx + SECRET_KEY2: dmFsdWUy + kind: Secret + metadata: + name: my-secret + type: Opaque + - apiVersion: v1 + data: + credentials: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg= + kind: Secret + metadata: + name: aws-credentials + type: Opaque + - apiVersion: helm.crossplane.io/v1beta1 + kind: Release + metadata: + name: wordpress-release + namespace: jupyter-{{ spawner.user.name }} + spec: + forProvider: + chart: + name: wordpress + repository: oci://registry-1.docker.io/bitnamicharts/ + version: 24.1.4 + namespace: jupyter-{{ spawner.user.name }} + values: + service: + type: ClusterIP + providerConfigRef: + name: helm-provider + key: manifests + name: manifests + persist: false node_selector: {} pod_env_vars: CONDA_ENVS_PATH: ' /workspace/.envs' HOME: /workspace role_bindings: null + secret_mounts: + - mount_path: /workspace/.aws + name: aws-credentials volumes: - access_modes: - ReadWriteMany @@ -174,6 +1136,8 @@ profiles: mem_guarantee: 4G mem_limit: 6G slug: eoepca_demo_init_script + env_from_config_maps: null + env_from_secrets: null groups: - group-a - group-b @@ -201,6 +1165,7 @@ profiles: HOME: /workspace XDG_RUNTIME_DIR: /workspace/.local role_bindings: null + secret_mounts: null volumes: - access_modes: - ReadWriteMany @@ -237,6 +1202,8 @@ profiles: mem_guarantee: 4G mem_limit: 6G slug: eoepca_jupyter_lab + env_from_config_maps: null + env_from_secrets: null groups: - group-c id: profile_jupyter_lab @@ -249,6 +1216,7 @@ profiles: XDG_CONFIG_HOME: /workspace/.config XDG_RUNTIME_DIR: /workspace/.local role_bindings: null + secret_mounts: null volumes: - access_modes: - ReadWriteOnce @@ -275,21 +1243,497 @@ profiles: mem_guarantee: 4G mem_limit: 6G slug: eoepca_jupyter_lab_2 + env_from_config_maps: null + env_from_secrets: null groups: - group-c id: profile_jupyter_lab_2 image_pull_secrets: - - data: null + - data: '' name: cr-config persist: false init_containers: [] - manifests: null + manifests: + - content: + - apiVersion: v1 + kind: ServiceAccount + metadata: + name: localstack + - apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + name: localstack + rules: + - apiGroups: + - '' + resources: + - pods + verbs: + - '*' + - apiGroups: + - '' + resources: + - pods/log + verbs: + - get + - apiGroups: + - '' + resources: + - pods/exec + verbs: + - get + - create + - apiGroups: + - '' + resources: + - services + verbs: + - get + - list + - apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + name: localstack + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: localstack + subjects: + - kind: ServiceAccount + name: localstack + - apiVersion: v1 + kind: Service + metadata: + name: localstack + spec: + ports: + - name: edge + port: 4566 + targetPort: 4566 + - name: external-service-port-4510 + port: 4510 + targetPort: ext-svc-4510 + - name: external-service-port-4511 + port: 4511 + targetPort: ext-svc-4511 + - name: external-service-port-4512 + port: 4512 + targetPort: ext-svc-4512 + - name: external-service-port-4513 + port: 4513 + targetPort: ext-svc-4513 + - name: external-service-port-4514 + port: 4514 + targetPort: ext-svc-4514 + - name: external-service-port-4515 + port: 4515 + targetPort: ext-svc-4515 + - name: external-service-port-4516 + port: 4516 + targetPort: ext-svc-4516 + - name: external-service-port-4517 + port: 4517 + targetPort: ext-svc-4517 + - name: external-service-port-4518 + port: 4518 + targetPort: ext-svc-4518 + - name: external-service-port-4519 + port: 4519 + targetPort: ext-svc-4519 + - name: external-service-port-4520 + port: 4520 + targetPort: ext-svc-4520 + - name: external-service-port-4521 + port: 4521 + targetPort: ext-svc-4521 + - name: external-service-port-4522 + port: 4522 + targetPort: ext-svc-4522 + - name: external-service-port-4523 + port: 4523 + targetPort: ext-svc-4523 + - name: external-service-port-4524 + port: 4524 + targetPort: ext-svc-4524 + - name: external-service-port-4525 + port: 4525 + targetPort: ext-svc-4525 + - name: external-service-port-4526 + port: 4526 + targetPort: ext-svc-4526 + - name: external-service-port-4527 + port: 4527 + targetPort: ext-svc-4527 + - name: external-service-port-4528 + port: 4528 + targetPort: ext-svc-4528 + - name: external-service-port-4529 + port: 4529 + targetPort: ext-svc-4529 + - name: external-service-port-4530 + port: 4530 + targetPort: ext-svc-4530 + - name: external-service-port-4531 + port: 4531 + targetPort: ext-svc-4531 + - name: external-service-port-4532 + port: 4532 + targetPort: ext-svc-4532 + - name: external-service-port-4533 + port: 4533 + targetPort: ext-svc-4533 + - name: external-service-port-4534 + port: 4534 + targetPort: ext-svc-4534 + - name: external-service-port-4535 + port: 4535 + targetPort: ext-svc-4535 + - name: external-service-port-4536 + port: 4536 + targetPort: ext-svc-4536 + - name: external-service-port-4537 + port: 4537 + targetPort: ext-svc-4537 + - name: external-service-port-4538 + port: 4538 + targetPort: ext-svc-4538 + - name: external-service-port-4539 + port: 4539 + targetPort: ext-svc-4539 + - name: external-service-port-4540 + port: 4540 + targetPort: ext-svc-4540 + - name: external-service-port-4541 + port: 4541 + targetPort: ext-svc-4541 + - name: external-service-port-4542 + port: 4542 + targetPort: ext-svc-4542 + - name: external-service-port-4543 + port: 4543 + targetPort: ext-svc-4543 + - name: external-service-port-4544 + port: 4544 + targetPort: ext-svc-4544 + - name: external-service-port-4545 + port: 4545 + targetPort: ext-svc-4545 + - name: external-service-port-4546 + port: 4546 + targetPort: ext-svc-4546 + - name: external-service-port-4547 + port: 4547 + targetPort: ext-svc-4547 + - name: external-service-port-4548 + port: 4548 + targetPort: ext-svc-4548 + - name: external-service-port-4549 + port: 4549 + targetPort: ext-svc-4549 + - name: external-service-port-4550 + port: 4550 + targetPort: ext-svc-4550 + - name: external-service-port-4551 + port: 4551 + targetPort: ext-svc-4551 + - name: external-service-port-4552 + port: 4552 + targetPort: ext-svc-4552 + - name: external-service-port-4553 + port: 4553 + targetPort: ext-svc-4553 + - name: external-service-port-4554 + port: 4554 + targetPort: ext-svc-4554 + - name: external-service-port-4555 + port: 4555 + targetPort: ext-svc-4555 + - name: external-service-port-4556 + port: 4556 + targetPort: ext-svc-4556 + - name: external-service-port-4557 + port: 4557 + targetPort: ext-svc-4557 + - name: external-service-port-4558 + port: 4558 + targetPort: ext-svc-4558 + - name: external-service-port-4559 + port: 4559 + targetPort: ext-svc-4559 + selector: + app.kubernetes.io/instance: localstack + app.kubernetes.io/name: localstack + type: ClusterIP + - apiVersion: apps/v1 + kind: Deployment + metadata: + name: localstack + spec: + replicas: 1 + selector: + matchLabels: + app.kubernetes.io/instance: localstack + app.kubernetes.io/name: localstack + strategy: + type: RollingUpdate + template: + metadata: + labels: + app: localstack-{{ spawner.user.name }} + app.kubernetes.io/instance: localstack + app.kubernetes.io/name: localstack + spec: + containers: + - env: + - name: DEBUG + value: '0' + - name: EXTERNAL_SERVICE_PORTS_START + value: '4510' + - name: EXTERNAL_SERVICE_PORTS_END + value: '4560' + - name: LOCALSTACK_K8S_SERVICE_NAME + value: localstack + - name: LOCALSTACK_K8S_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: LAMBDA_RUNTIME_EXECUTOR + value: docker + - name: LAMBDA_K8S_IMAGE_PREFIX + value: localstack/lambda- + - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT + value: '60' + - name: OVERRIDE_IN_DOCKER + value: '1' + image: localstack/localstack:latest + imagePullPolicy: IfNotPresent + livenessProbe: + failureThreshold: 3 + httpGet: + path: /_localstack/health + port: edge + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + name: localstack + ports: + - containerPort: 4566 + name: edge + protocol: TCP + - containerPort: 4510 + name: ext-svc-4510 + protocol: TCP + - containerPort: 4511 + name: ext-svc-4511 + protocol: TCP + - containerPort: 4512 + name: ext-svc-4512 + protocol: TCP + - containerPort: 4513 + name: ext-svc-4513 + protocol: TCP + - containerPort: 4514 + name: ext-svc-4514 + protocol: TCP + - containerPort: 4515 + name: ext-svc-4515 + protocol: TCP + - containerPort: 4516 + name: ext-svc-4516 + protocol: TCP + - containerPort: 4517 + name: ext-svc-4517 + protocol: TCP + - containerPort: 4518 + name: ext-svc-4518 + protocol: TCP + - containerPort: 4519 + name: ext-svc-4519 + protocol: TCP + - containerPort: 4520 + name: ext-svc-4520 + protocol: TCP + - containerPort: 4521 + name: ext-svc-4521 + protocol: TCP + - containerPort: 4522 + name: ext-svc-4522 + protocol: TCP + - containerPort: 4523 + name: ext-svc-4523 + protocol: TCP + - containerPort: 4524 + name: ext-svc-4524 + protocol: TCP + - containerPort: 4525 + name: ext-svc-4525 + protocol: TCP + - containerPort: 4526 + name: ext-svc-4526 + protocol: TCP + - containerPort: 4527 + name: ext-svc-4527 + protocol: TCP + - containerPort: 4528 + name: ext-svc-4528 + protocol: TCP + - containerPort: 4529 + name: ext-svc-4529 + protocol: TCP + - containerPort: 4530 + name: ext-svc-4530 + protocol: TCP + - containerPort: 4531 + name: ext-svc-4531 + protocol: TCP + - containerPort: 4532 + name: ext-svc-4532 + protocol: TCP + - containerPort: 4533 + name: ext-svc-4533 + protocol: TCP + - containerPort: 4534 + name: ext-svc-4534 + protocol: TCP + - containerPort: 4535 + name: ext-svc-4535 + protocol: TCP + - containerPort: 4536 + name: ext-svc-4536 + protocol: TCP + - containerPort: 4537 + name: ext-svc-4537 + protocol: TCP + - containerPort: 4538 + name: ext-svc-4538 + protocol: TCP + - containerPort: 4539 + name: ext-svc-4539 + protocol: TCP + - containerPort: 4540 + name: ext-svc-4540 + protocol: TCP + - containerPort: 4541 + name: ext-svc-4541 + protocol: TCP + - containerPort: 4542 + name: ext-svc-4542 + protocol: TCP + - containerPort: 4543 + name: ext-svc-4543 + protocol: TCP + - containerPort: 4544 + name: ext-svc-4544 + protocol: TCP + - containerPort: 4545 + name: ext-svc-4545 + protocol: TCP + - containerPort: 4546 + name: ext-svc-4546 + protocol: TCP + - containerPort: 4547 + name: ext-svc-4547 + protocol: TCP + - containerPort: 4548 + name: ext-svc-4548 + protocol: TCP + - containerPort: 4549 + name: ext-svc-4549 + protocol: TCP + - containerPort: 4550 + name: ext-svc-4550 + protocol: TCP + - containerPort: 4551 + name: ext-svc-4551 + protocol: TCP + - containerPort: 4552 + name: ext-svc-4552 + protocol: TCP + - containerPort: 4553 + name: ext-svc-4553 + protocol: TCP + - containerPort: 4554 + name: ext-svc-4554 + protocol: TCP + - containerPort: 4555 + name: ext-svc-4555 + protocol: TCP + - containerPort: 4556 + name: ext-svc-4556 + protocol: TCP + - containerPort: 4557 + name: ext-svc-4557 + protocol: TCP + - containerPort: 4558 + name: ext-svc-4558 + protocol: TCP + - containerPort: 4559 + name: ext-svc-4559 + protocol: TCP + readinessProbe: + failureThreshold: 3 + httpGet: + path: /_localstack/health + port: edge + initialDelaySeconds: 0 + periodSeconds: 10 + successThreshold: 1 + timeoutSeconds: 1 + resources: {} + securityContext: {} + securityContext: {} + serviceAccountName: localstack + volumes: [] + - apiVersion: v1 + data: + ENV_VAR1: value1 + ENV_VAR2: value2 + kind: ConfigMap + metadata: + name: my-config + - apiVersion: v1 + data: + SECRET_KEY1: dmFsdWUx + SECRET_KEY2: dmFsdWUy + kind: Secret + metadata: + name: my-secret + type: Opaque + - apiVersion: v1 + data: + credentials: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg= + kind: Secret + metadata: + name: aws-credentials + type: Opaque + - apiVersion: helm.crossplane.io/v1beta1 + kind: Release + metadata: + name: wordpress-release + namespace: jupyter-{{ spawner.user.name }} + spec: + forProvider: + chart: + name: wordpress + repository: oci://registry-1.docker.io/bitnamicharts/ + version: 24.1.4 + namespace: jupyter-{{ spawner.user.name }} + values: + service: + type: ClusterIP + providerConfigRef: + name: helm-provider + key: manifests + name: manifests + persist: false node_selector: {} pod_env_vars: HOME: /workspace XDG_CONFIG_HOME: /workspace/.config XDG_RUNTIME_DIR: /workspace/.local role_bindings: null + secret_mounts: null volumes: - access_modes: - ReadWriteOnce From 2d53789ed6a507d65038ce4edc9592293be64300 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Sat, 21 Dec 2024 15:33:09 +0100 Subject: [PATCH 42/65] adds render to config map and object names --- application_hub_context/app_hub_context.py | 76 ++++++++++--------- .../config-generator-eoepca-demo.ipynb | 18 ++--- config-generator/config-maps/bash-rc | 2 + config-generator/manifests/manifest.yaml | 2 +- files/hub/config.yml | 10 +-- 5 files changed, 56 insertions(+), 52 deletions(-) diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index 84e226f..f55c385 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -577,11 +577,11 @@ def unapply_manifests(self, manifest_content): for k8_object in manifest_content: kind = k8_object.get("kind") self.spawner.log.info( - f"Deleting {kind} {k8_object.get('metadata', {}).get('name')}" + f"Deleting {kind} {self.render(k8_object.get('metadata', {}).get('name'))}" ) metadata = k8_object.get("metadata", {}) namespace = metadata.get("namespace", self.namespace) - name = metadata.get("name") + name = self.render(metadata.get("name")) if not kind or not name: continue @@ -631,6 +631,11 @@ def __init__(self, namespace, spawner, config_path: str, kubeconfig_file: TextIO def get_profile_list(self): return self.config_parser.get_profiles() + def render(self, manifest): + # render the manifest using the spawner object + template = Template(yaml.dump(manifest)) + return yaml.safe_load(template.render(spawner=self.spawner)) + def initialise(self): # set the spawner timeout to 10 minutes self.spawner.http_timeout = 600 @@ -698,10 +703,7 @@ def initialise(self): profile_id=profile_id ) self.set_pod_env_vars(**(config_env_vars or {})) - - # process the config maps - config_maps = self.config_parser.get_profile_config_maps(profile_id=profile_id) - + if not self.skip_namespace_check: self.spawner.log.info(f"Checking namespace {self.namespace}") # check the namespace @@ -711,6 +713,38 @@ def initialise(self): else: self.spawner.log.info(f"Skipping namespace check") + # process the config maps + config_maps = self.config_parser.get_profile_config_maps(profile_id=profile_id) + + # TODO move the manifests before dealing with the config objects + # process the manifests + manifests = self.config_parser.get_profile_manifests(profile_id=profile_id) + + if manifests: + for manifest in manifests: + self.spawner.log.info(f"Apply manifest {manifest.name}") + + + for k8_object in manifest.content: + try: + # Log the object and its type + self.spawner.log.info(f"K8 Object: {k8_object}") + self.spawner.log.info(f"Object Type: {type(k8_object)}") + + # Check and log the 'kind' of the Kubernetes object + if 'kind' in k8_object: + self.spawner.log.info(f"Applying manifest of kind: {k8_object['kind']}") + self.apply_manifest(k8_object) # Apply the manifest + else: + self.spawner.log.warning(f"Manifest does not contain a 'kind': {k8_object}") + + except Exception as err: + self.spawner.log.error(f"Unexpected {err}, {type(err)}") + self.spawner.log.error( + f"Skipping creation of manifest {manifest.name}" + ) + + if config_maps: for config_map in config_maps: try: @@ -719,7 +753,7 @@ def initialise(self): self.create_configmap( name=config_map.name, key=config_map.key, - content=config_map.content, + content=self.template_manifest(config_map.content), annotations=None, labels=None, ) @@ -898,33 +932,7 @@ def initialise(self): f"Skipping creation of init container {init_container.name}" ) - # process the manifests - manifests = self.config_parser.get_profile_manifests(profile_id=profile_id) - - if manifests: - for manifest in manifests: - self.spawner.log.info(f"Apply manifest {manifest.name}") - - - for k8_object in manifest.content: - try: - # Log the object and its type - self.spawner.log.info(f"K8 Object: {k8_object}") - self.spawner.log.info(f"Object Type: {type(k8_object)}") - - # Check and log the 'kind' of the Kubernetes object - if 'kind' in k8_object: - self.spawner.log.info(f"Applying manifest of kind: {k8_object['kind']}") - self.apply_manifest(k8_object) # Apply the manifest - else: - self.spawner.log.warning(f"Manifest does not contain a 'kind': {k8_object}") - - except Exception as err: - self.spawner.log.error(f"Unexpected {err}, {type(err)}") - self.spawner.log.error( - f"Skipping creation of manifest {manifest.name}" - ) - + # process the pod env vars from config maps env_from_config_maps = self.config_parser.get_profile_env_from_config_maps( profile_id=profile_id diff --git a/config-generator/config-generator-eoepca-demo.ipynb b/config-generator/config-generator-eoepca-demo.ipynb index 582d83e..57905c8 100644 --- a/config-generator/config-generator-eoepca-demo.ipynb +++ b/config-generator/config-generator-eoepca-demo.ipynb @@ -434,7 +434,7 @@ "image_pull_secret = ImagePullSecret(\n", " name=\"cr-config\",\n", " persist=False,\n", - " data=\"\",\n", + " data=\"ewogICAgImF1dGhzIjogewogICAgICAgICJjci50ZXJyYWR1ZS5jb20iOiB7CiAgICAgICAgICAgICJ1c2VybmFtZSI6ICJyb2JvdCRlb2VwY2EtcGx1cy1ybyIsCiAgICAgICAgICAgICJwYXNzd29yZCI6ICJQMlE4TnkyZ0lHODhkZkxveXlLN05QVUZVbHJOekFZSiIsCiAgICAgICAgICAgICJlbWFpbCI6ICJlb2VwY2EtcGx1c0B0ZXJyYWR1ZS5jb20iLAogICAgICAgICAgICAiYXV0aCI6ICJjbTlpYjNRa1pXOWxjR05oTFhCc2RYTXRjbTg2VURKUk9FNTVNbWRKUnpnNFpHWk1iM2w1U3pkT1VGVkdWV3h5VG5wQldVbz0iCiAgICAgICAgfQogICAgfQp9\",\n", ")" ] }, @@ -601,7 +601,7 @@ " mount_path=\"/opt/init/.init.sh\",\n", ")\n", "\n", - "image = \"eoepca/iga-remote-desktop-qgis:1.1.2\"\n", + "image = \"eoepca/iga-remote-desktop-qgis:1.1.3\"\n", "\n", "qgis_profile = Profile(\n", " id=\"profile_studio_desktop_qgis\",\n", @@ -619,18 +619,12 @@ " ),\n", " node_selector={},\n", " volumes=[workspace_volume],\n", - " config_maps=[init_qgis_cm, bash_rc_cm, bash_login_cm],\n", + " config_maps=[bash_rc_cm, bash_login_cm],\n", " pod_env_vars={\n", - " \"HOME\": \"/workspace\",\n", - " \"AWS_REGION\": \"fr-par\",\n", - " \"AWS_S3_ENDPOINT\": \"s3.fr-par.scw.cloud\",\n", - " \"AWS_SECRET_ACCESS_KEY\": \"a9a7dc0c-8b0a-47b1-a311-b134b258dbb3\",\n", - " \"AWS_VIRTUAL_HOSTING\": \"true\",\n", - " \"AWS_ACCESS_KEY_ID\": \"SCWMRGD5GA0Y68XRKFW8\",\n", - " \"AWS_VIRTUAL_HOSTING\": \"true\",\n", + " \"HOME\": \"/workspace\"\n", " },\n", " default_url=\"desktop\",\n", - " init_containers=[init_container]\n", + " init_containers=[]\n", " )\n", "\n", "profiles.append(qgis_profile)" @@ -693,7 +687,7 @@ ], "metadata": { "kernelspec": { - "display_name": "base", + "display_name": ".env-config-generator", "language": "python", "name": "python3" }, diff --git a/config-generator/config-maps/bash-rc b/config-generator/config-maps/bash-rc index 4dff3fa..0d387fd 100644 --- a/config-generator/config-maps/bash-rc +++ b/config-generator/config-maps/bash-rc @@ -23,3 +23,5 @@ if [ -f "/opt/conda/etc/profile.d/mamba.sh" ]; then . "/opt/conda/etc/profile.d/mamba.sh" fi # <<< conda initialize <<< + +a={{spawner.user.name}} \ No newline at end of file diff --git a/config-generator/manifests/manifest.yaml b/config-generator/manifests/manifest.yaml index 2d07c63..efeb0b6 100644 --- a/config-generator/manifests/manifest.yaml +++ b/config-generator/manifests/manifest.yaml @@ -449,7 +449,7 @@ data: apiVersion: helm.crossplane.io/v1beta1 kind: Release metadata: - name: wordpress-release + name: wordpress-jupyter-{{ spawner.user.name }} namespace: jupyter-{{ spawner.user.name }} spec: forProvider: diff --git a/files/hub/config.yml b/files/hub/config.yml index efcd8eb..db10e81 100644 --- a/files/hub/config.yml +++ b/files/hub/config.yml @@ -11,7 +11,7 @@ profiles: \ ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\n else\n \ \ export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset __conda_setup\n\n\ if [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n . \"/opt/conda/etc/profile.d/mamba.sh\"\ - \nfi\n# <<< conda initialize <<<\n" + \nfi\n# <<< conda initialize <<<\n\na={{spawner.user.name}}" default_mode: null key: bash-rc mount_path: /workspace/.bashrc @@ -499,7 +499,7 @@ profiles: - apiVersion: helm.crossplane.io/v1beta1 kind: Release metadata: - name: wordpress-release + name: wordpress-jupyter-{{ spawner.user.name }} namespace: jupyter-{{ spawner.user.name }} spec: forProvider: @@ -557,7 +557,7 @@ profiles: \ ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\n else\n \ \ export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset __conda_setup\n\n\ if [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n . \"/opt/conda/etc/profile.d/mamba.sh\"\ - \nfi\n# <<< conda initialize <<<\n" + \nfi\n# <<< conda initialize <<<\n\na={{spawner.user.name}}" default_mode: null key: bash-rc mount_path: /workspace/.bashrc @@ -1045,7 +1045,7 @@ profiles: - apiVersion: helm.crossplane.io/v1beta1 kind: Release metadata: - name: wordpress-release + name: wordpress-jupyter-{{ spawner.user.name }} namespace: jupyter-{{ spawner.user.name }} spec: forProvider: @@ -1710,7 +1710,7 @@ profiles: - apiVersion: helm.crossplane.io/v1beta1 kind: Release metadata: - name: wordpress-release + name: wordpress-jupyter-{{ spawner.user.name }} namespace: jupyter-{{ spawner.user.name }} spec: forProvider: From 91b4ec7f1c48027f8d8079f7e1fbd78d77ec40b5 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Sat, 21 Dec 2024 15:56:09 +0100 Subject: [PATCH 43/65] adds render to all object names --- application_hub_context/app_hub_context.py | 97 +++++++++++----------- config-generator/config-generator.ipynb | 2 +- config-generator/manifests/manifest.yaml | 2 +- files/hub/config.yml | 10 +-- files/hub/jupyterhub_config.py | 3 +- 5 files changed, 56 insertions(+), 58 deletions(-) diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index f55c385..2997c05 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -559,7 +559,6 @@ def apply_manifest(self, manifest): self.custom_objects_api.create_cluster_custom_object( group=group, version=version, - #namespace=namespace, plural=plural, body=yaml.safe_load(rendered_manifest) ) @@ -751,9 +750,9 @@ def initialise(self): if not self.is_config_map_created(name=config_map.name): self.spawner.log.info(f"Creating configmap {config_map.name}") self.create_configmap( - name=config_map.name, + name=self.render(config_map.name), key=config_map.key, - content=self.template_manifest(config_map.content), + content=self.render(config_map.content), annotations=None, labels=None, ) @@ -763,7 +762,7 @@ def initialise(self): self.spawner.volume_mounts.extend( [ { - "name": config_map.name, + "name": self.render(config_map.name), "mountPath": config_map.mount_path, "subPath": config_map.key, }, @@ -772,9 +771,9 @@ def initialise(self): self.spawner.volumes.extend( [ { - "name": config_map.name, + "name": self.render(config_map.name), "configMap": { - "name": config_map.name, + "name": self.render(config_map.name), "defaultMode": int(config_map.default_mode, 8) if config_map.default_mode else 0o644, # noqa: E501 @@ -783,12 +782,12 @@ def initialise(self): ] ) self.spawner.log.info( - f"Mounted configmap {config_map.name} (key {config_map.key})" # noqa: E501 + f"Mounted configmap {self.render(config_map.name)} (key {config_map.key})" # noqa: E501 ) except Exception as err: self.spawner.log.error(f"Unexpected {err=}, {type(err)=}") self.spawner.log.error( - f"Skipping creation of configmap {config_map.name}" + f"Skipping creation of configmap {self.render(config_map.name)}" ) # process the volumes @@ -796,25 +795,25 @@ def initialise(self): if volumes: for volume in volumes: - self.spawner.log.info(f"Mounting volume {volume.name}") + self.spawner.log.info(f"Mounting volume {self.render(volume.name)}") try: - if not self.is_pvc_created(name=volume.claim_name): + if not self.is_pvc_created(name=self.render(volume.claim_name)): self.spawner.log.info( - f"Creating volume claim {volume.claim_name})" + f"Creating volume claim {self.render(volume.claim_name)})" ) self.create_pvc( - name=volume.claim_name, + name=self.render(volume.claim_name), access_modes=volume.access_modes, size=volume.size, storage_class=volume.storage_class, ) self.spawner.log.info( - f"Mounting volume {volume.name} (claim {volume.claim_name}))" + f"Mounting volume {self.render(volume.name)} (claim {self.render(volume.claim_name)}))" ) self.spawner.volume_mounts.extend( [ { - "name": volume.volume_mount.name, + "name": self.render(volume.volume_mount.name), "mountPath": volume.volume_mount.mount_path, }, ] @@ -823,9 +822,9 @@ def initialise(self): self.spawner.volumes.extend( [ { - "name": volume.name, + "name": self.render(volume.name), "persistentVolumeClaim": { - "claimName": volume.claim_name + "claimName": self.render(volume.claim_name) }, } ] @@ -833,7 +832,7 @@ def initialise(self): except Exception as err: self.spawner.log.error(f"Unexpected {err}, {type(err)}") - self.spawner.log.error(f"Skipping creation of volume {volume.name}") + self.spawner.log.error(f"Skipping creation of volume {self.render(volume.name)}") # process the role bindings role_bindings = self.config_parser.get_profile_role_bindings( @@ -842,17 +841,17 @@ def initialise(self): if role_bindings: for role_binding in role_bindings: - self.spawner.log.info(f"Creating role binding {role_binding.name}") + self.spawner.log.info(f"Creating role binding {self.render(role_binding.name)}") try: # checking if role binding is already created - if not self.is_role_binding_created(name=role_binding.name): + if not self.is_role_binding_created(name=self.render(role_binding.name)): # checking if role is already created - if not self.is_role_created(name=role_binding.role.name): + if not self.is_role_created(name=self.render(role_binding.role.name)): self.spawner.log.info( - f"Creating role {role_binding.role.name}" + f"Creating role {self.render(role_binding.role.name)}" ) self.create_role( - name=role_binding.role.name, + name=self.render(role_binding.role.name), verbs=role_binding.role.verbs, resources=role_binding.role.resources, api_groups=role_binding.role.api_groups, @@ -860,7 +859,7 @@ def initialise(self): # creating role binding self.create_role_binding( - name=role_binding.name, + name=self.render(role_binding.name), role=role_binding.role, subjects=role_binding.subjects, ) @@ -868,7 +867,7 @@ def initialise(self): except Exception as err: self.spawner.log.error(f"Unexpected {err}, {type(err)}") self.spawner.log.error( - f"Skipping creation of role binding {role_binding.name}" + f"Skipping creation of role binding {self.render(role_binding.name)}" ) # process the role bindings image_pull_secrets = self.config_parser.get_profile_image_pull_secrets( @@ -878,27 +877,27 @@ def initialise(self): if image_pull_secrets: for image_pull_secret in image_pull_secrets: self.spawner.log.info( - f"Create image pull secret {image_pull_secret.name}" + f"Create image pull secret {self.render(image_pull_secret.name)}" ) try: if not self.is_image_pull_secret_created( - name=image_pull_secret.name + name=self.render(image_pull_secret.name) ): self.spawner.log.info( - f"Creating image pull secret {image_pull_secret.name})" + f"Creating image pull secret {self.render(image_pull_secret.name)})" ) self.create_image_pull_secret( - name=image_pull_secret.name, + name=self.render(image_pull_secret.name), data=image_pull_secret.data, ) except Exception as err: self.spawner.log.error(f"Unexpected {err}, {type(err)}") self.spawner.log.error( "Skipping creation of image pull secret" - f" {image_pull_secret.name}" + f" {self.render(image_pull_secret.name)}" ) - self.patch_service_account(secret_name=image_pull_secret.name) + self.patch_service_account(secret_name=self.render(image_pull_secret.name)) # process the init containers init_containers = self.config_parser.get_profile_init_containers( @@ -908,13 +907,13 @@ def initialise(self): if init_containers: for init_container in init_containers: self.spawner.log.info( - f"Create init container {init_container.name}" + f"Create init container {self.render(init_container.name)}" ) try: self.spawner.init_containers.extend( [ { - "name": init_container.name, + "name": self.render(init_container.name), "image": init_container.image, "command": init_container.command, "volumeMounts": [ @@ -929,7 +928,7 @@ def initialise(self): except Exception as err: self.spawner.log.error(f"Unexpected {err}, {type(err)}") self.spawner.log.error( - f"Skipping creation of init container {init_container.name}" + f"Skipping creation of init container {self.render(init_container.name)}" ) @@ -946,7 +945,7 @@ def initialise(self): self.spawner.extra_container_config["env_from"].append( { "configMapRef": { - "name": env_from_config_map, + "name": self.render(env_from_config_map), }}) self.spawner.log.info(f"extra_container_config {self.spawner.extra_container_config}") @@ -963,7 +962,7 @@ def initialise(self): self.spawner.extra_container_config["env_from"].append( { "secretRef": { - "name": env_from_secret, + "name": self.render(env_from_secret), }}) self.spawner.log.info(f"extra_container_config {self.spawner.extra_container_config}") @@ -974,11 +973,11 @@ def initialise(self): if secret_mounts: for secret_mount in secret_mounts: - self.spawner.log.info(f"Mounting secret {secret_mount.name}") + self.spawner.log.info(f"Mounting secret {self.render(secret_mount.name)}") self.spawner.volume_mounts.extend( [ { - "name": secret_mount.name, + "name": self.render(secret_mount.name), "mountPath": secret_mount.mount_path, }, ] @@ -987,15 +986,15 @@ def initialise(self): self.spawner.volumes.extend( [ { - "name": secret_mount.name, + "name": self.render(secret_mount.name), "secret": { - "secretName": secret_mount.name, + "secretName": self.render(secret_mount.name), }, } ] ) - self.spawner.log.info(f"Mounted secret {secret_mount.name}") + self.spawner.log.info(f"Mounted secret {self.render(secret_mount.name)}") def dispose(self): profile_id = self.config_parser.get_profile_by_slug(slug=self.profile_slug).id @@ -1009,9 +1008,9 @@ def dispose(self): if config_maps: for config_map in config_maps: if not config_map.persist: - self.spawner.log.info(f"Dispose config map {config_map.name}") + self.spawner.log.info(f"Dispose config map {self.render(config_map.name)}") - self.delete_config_map(name=config_map.name) + self.delete_config_map(name=self.render(config_map.name)) # deal with the volumes volumes = self.config_parser.get_profile_volumes(profile_id=profile_id) @@ -1020,10 +1019,10 @@ def dispose(self): for volume in volumes: if not volume.persist: self.spawner.log.info( - f"Dispose volume {volume.name}, claim {volume.claim_name}" + f"Dispose volume {self.render(volume.name)}, claim {self.render(volume.claim_name)}" ) - self.delete_pvc(name=volume.claim_name) + self.delete_pvc(name=self.render(volume.claim_name)) # deal with the role bindings role_bindings = self.config_parser.get_profile_role_bindings( @@ -1033,7 +1032,7 @@ def dispose(self): if role_bindings: for role_binding in role_bindings: if not role_binding.persist: - self.spawner.log.info(f"Dispose role binding {role_binding.name}") + self.spawner.log.info(f"Dispose role binding {self.render(role_binding.name)}") self.delete_role_binding(role_binding=role_binding) # process the manifests @@ -1053,9 +1052,9 @@ def dispose(self): for image_pull_secret in image_pull_secrets: if not image_pull_secret.persist: self.spawner.log.info( - f"Dispose image pull secret {image_pull_secret.name}" + f"Dispose image pull secret {self.render(image_pull_secret.name)}" ) - self.delete_image_pull_secret(name=image_pull_secret.name) + self.delete_image_pull_secret(name=self.render(image_pull_secret.name)) service_account_body = ( self.core_v1_api.read_namespaced_service_account( @@ -1063,13 +1062,13 @@ def dispose(self): ) ) for elem in service_account_body.image_pull_secrets: - if elem.name == image_pull_secret.name: + if elem.name == self.render(image_pull_secret.name): service_account_body.image_pull_secrets.remove( {"name": elem.name} ) self.spawner.log.info( - f"Remove image pull secret {image_pull_secret.name}" + f"Remove image pull secret {self.render(image_pull_secret.name)}" " from default service account" ) diff --git a/config-generator/config-generator.ipynb b/config-generator/config-generator.ipynb index fcd6aa2..66b0f91 100644 --- a/config-generator/config-generator.ipynb +++ b/config-generator/config-generator.ipynb @@ -255,7 +255,7 @@ " manifests=[localstack_manifest],\n", " env_from_config_maps=[\"my-config\"],\n", " env_from_secrets=[\"my-secret\"],\n", - " secret_mounts=[SecretMount(name=\"aws-credentials\", mount_path=\"/workspace/.aws\")],\n", + " secret_mounts=[SecretMount(name=\"aws-credentials-{{ spawner.user.name }}\", mount_path=\"/workspace/.aws\")],\n", " )\n", "\n", " profiles.append(coder_profile)" diff --git a/config-generator/manifests/manifest.yaml b/config-generator/manifests/manifest.yaml index efeb0b6..1a2b228 100644 --- a/config-generator/manifests/manifest.yaml +++ b/config-generator/manifests/manifest.yaml @@ -441,7 +441,7 @@ data: apiVersion: v1 kind: Secret metadata: - name: aws-credentials + name: aws-credentials-{{ spawner.user.name }} type: Opaque data: credentials: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg= diff --git a/files/hub/config.yml b/files/hub/config.yml index db10e81..60ab795 100644 --- a/files/hub/config.yml +++ b/files/hub/config.yml @@ -494,7 +494,7 @@ profiles: credentials: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg= kind: Secret metadata: - name: aws-credentials + name: aws-credentials-{{ spawner.user.name }} type: Opaque - apiVersion: helm.crossplane.io/v1beta1 kind: Release @@ -523,7 +523,7 @@ profiles: role_bindings: null secret_mounts: - mount_path: /workspace/.aws - name: aws-credentials + name: aws-credentials-{{ spawner.user.name }} volumes: - access_modes: - ReadWriteMany @@ -1040,7 +1040,7 @@ profiles: credentials: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg= kind: Secret metadata: - name: aws-credentials + name: aws-credentials-{{ spawner.user.name }} type: Opaque - apiVersion: helm.crossplane.io/v1beta1 kind: Release @@ -1069,7 +1069,7 @@ profiles: role_bindings: null secret_mounts: - mount_path: /workspace/.aws - name: aws-credentials + name: aws-credentials-{{ spawner.user.name }} volumes: - access_modes: - ReadWriteMany @@ -1705,7 +1705,7 @@ profiles: credentials: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg= kind: Secret metadata: - name: aws-credentials + name: aws-credentials-{{ spawner.user.name }} type: Opaque - apiVersion: helm.crossplane.io/v1beta1 kind: Release diff --git a/files/hub/jupyterhub_config.py b/files/hub/jupyterhub_config.py index 56395ea..499b2fa 100644 --- a/files/hub/jupyterhub_config.py +++ b/files/hub/jupyterhub_config.py @@ -6,7 +6,6 @@ from application_hub_context.app_hub_context import DefaultApplicationHubContext - configuration_directory = os.path.dirname(os.path.realpath(__file__)) sys.path.insert(0, configuration_directory) @@ -96,7 +95,7 @@ def post_stop_hook(spawner): } jupyterhub_env = os.environ["JUPYTERHUB_ENV"].upper() -jupyterhub_hub_host = "hub.jupyter" +jupyterhub_hub_host = "application-hub-hub.jupyter" jupyterhub_single_user_image = os.environ["JUPYTERHUB_SINGLE_USER_IMAGE_NOTEBOOKS"] # Authentication From 09e0f1cc153acfc365cc3cb90be7f07e6c33eb9f Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Sat, 21 Dec 2024 15:56:31 +0100 Subject: [PATCH 44/65] tidy requirements.txt --- requirements.txt | 107 ----------------------------------------------- 1 file changed, 107 deletions(-) diff --git a/requirements.txt b/requirements.txt index 763f5a8..e35fc56 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,109 +1,2 @@ -# addict==2.4.0 -# aiohappyeyeballs==2.4.0 -# aiohttp==3.10.6 -# aiosignal==1.3.1 -# alembic==1.13.3 -# annotated-types==0.7.0 -# arrow==1.3.0 -# async-generator==1.10 -# async-timeout==4.0.3 -# attrs==24.2.0 -# bcrypt==4.2.0 -# cachetools==5.5.0 -# certifi==2024.8.30 -# certipy==0.2.1 -# cffi==1.17.1 -# charset-normalizer==3.3.2 -# coverage==7.6.1 -# cryptography==43.0.1 -# durationpy==0.7 -# escapism==1.0.1 -# exceptiongroup==1.2.2 -# fqdn==1.5.1 -# frozenlist==1.4.1 -# google-auth==2.35.0 -# greenlet==3.1.1 -# httplib2==0.22.0 -# idna==3.10 -# importlib_metadata==8.5.0 -# importlib_resources==6.4.5 -# iniconfig==2.0.0 -# isoduration==20.11.0 -# Jinja2==3.1.4 -# jsonpointer==3.0.0 -# jsonschema==4.23.0 -# jsonschema-specifications==2023.12.1 -# jupyter-events==0.10.0 -# jupyter-telemetry==0.1.0 -# jupyterhub==5.1.0 -# jupyterhub-firstuseauthenticator==1.1.0 -# jupyterhub-hmacauthenticator==1.0 -# jupyterhub-idle-culler==1.4.0 -# jupyterhub-kubespawner==6.2.0 -# jupyterhub-ldapauthenticator==1.3.2 -# jupyterhub-ltiauthenticator==1.6.2 -# jupyterhub-nativeauthenticator==1.3.0 -# jupyterhub-tmpauthenticator==1.0.0 kubernetes==31.0.0 -# kubernetes_asyncio==24.2.3 -# ldap3==2.9.1 loguru==0.7.2 -# Mako==1.3.5 -# MarkupSafe==2.1.5 -# multidict==6.1.0 -# mwoauth==0.4.0 -# nullauthenticator==1.0.0 -# oauthenticator==17.0.0 -# oauthlib==3.2.2 -# onetimepass==1.0.1 -# packaging==24.1 -# pamela==1.2.0 -# pkgutil_resolve_name==1.3.10 -# pluggy==1.5.0 -# prometheus_client==0.21.0 -# psycopg2-binary==2.9.9 -# py-spy==0.3.14 -# pyasn1==0.6.1 -# pyasn1_modules==0.4.1 -# pycparser==2.22 -# pycurl==7.45.3 -# pydantic==2.9.2 -# pydantic_core==2.23.4 -# PyJWT==2.9.0 -# PyMySQL==1.1.1 -# pyOpenSSL==24.2.1 -# pyparsing==3.1.4 -# pyrsistent==0.20.0 -# pytest==8.3.3 -# pytest-asyncio==0.24.0 -# pytest-cov==5.0.0 -# python-dateutil==2.9.0.post0 -# python-json-logger==2.0.7 -# python-slugify==8.0.4 -# PyYAML==6.0.2 -# referencing==0.35.1 -# requests==2.32.3 -# requests-mock==1.12.1 -# requests-oauthlib==2.0.0 -# rfc3339-validator==0.1.4 -# rfc3986-validator==0.1.1 -# rpds-py==0.20.0 -# rsa==4.9 -# ruamel.yaml==0.18.6 -# ruamel.yaml.clib==0.2.8 -# six==1.16.0 -# SQLAlchemy==2.0.35 -# sqlalchemy-cockroachdb==2.0.2 -# statsd==4.0.1 -# text-unidecode==1.3 -# tomli==2.0.1 -# tornado==6.4.2 -# traitlets==5.14.3 -# types-python-dateutil==2.9.0.20240906 -# typing_extensions==4.12.2 -# uri-template==1.3.0 -# urllib3==2.2.3 -# webcolors==24.8.0 -# websocket-client==1.8.0 -# yarl==1.12.1 -# zipp==3.20.2 From 2804078c0d5f3257fb4d533ae18f65692dd44d40 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Sat, 21 Dec 2024 15:57:14 +0100 Subject: [PATCH 45/65] fixes hub init script (skaffold) --- sk-k8s/script.yaml | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sk-k8s/script.yaml b/sk-k8s/script.yaml index 49bed58..0afd3d7 100644 --- a/sk-k8s/script.yaml +++ b/sk-k8s/script.yaml @@ -9,14 +9,14 @@ data: apt update apt install -y jq curl - token=`curl -X POST -d '{"auth": {"username": "jovyan", "token": "12345"}}' http://hub.jupyter.svc.cluster.local:8081/hub/api/users/jovyan/tokens | jq -r '.token'` + token=`curl -X POST -d '{"auth": {"username": "jovyan", "token": "12345"}}' http://application-hub-hub.jupyter.svc.cluster.local:8081/hub/api/users/jovyan/tokens | jq -r '.token'` - curl --header "Authorization: Bearer $token" http://hub.jupyter.svc.cluster.local:8081/hub/api/groups + curl --header "Authorization: Bearer $token" http://application-hub-hub.jupyter.svc.cluster.local:8081/hub/api/groups # create groups for group in group-a group-b group-c do - curl --request POST --location http://hub.jupyter.svc.cluster.local:8081/hub/api/groups/${group} --header "Authorization: Bearer $token" --header 'Content-Type: application/json' + curl --request POST --location http://application-hub-hub.jupyter.svc.cluster.local:8081/hub/api/groups/${group} --header "Authorization: Bearer $token" --header 'Content-Type: application/json' # add user to group - curl --request POST --location http://hub.jupyter.svc.cluster.local:8081/hub/api/groups/${group}/users --header "Authorization: Bearer $token" --header 'Content-Type: application/json' --data '{"users": ["jovyan"]}' - done + curl --request POST --location http://application-hub-hub.jupyter.svc.cluster.local:8081/hub/api/groups/${group}/users --header "Authorization: Bearer $token" --header 'Content-Type: application/json' --data '{"users": ["jovyan"]}' + done \ No newline at end of file From 8ef533477aa3a238f056b60bc03da886f89a5f6f Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Sat, 21 Dec 2024 16:01:41 +0100 Subject: [PATCH 46/65] add external secrets (WIP) --- application_hub_context/app_hub_context.py | 33 ++++++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index 2997c05..d96f6d7 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -548,12 +548,11 @@ def apply_manifest(self, manifest): if yaml.safe_load(rendered_manifest)["kind"] in "Release": # see https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/CustomObjectsApi.md#create_namespaced_custom_object # Define the Crossplane HelmRelease details - group = "helm.crossplane.io" # API group for Crossplane HelmRelease - version = "v1beta1" # API version - namespace = f"jupyter-{self.spawner.user.name}" # Namespace of the HelmRelease - plural = "releases" # Resource type + group = "helm.crossplane.io" + version = "v1beta1" + plural = "releases" - self.spawner.log.info(f"Creating Crossplane HelmRelease in namespace {namespace}") + self.spawner.log.info("Creating Crossplane HelmRelease") # Create the Crossplane HelmRelease self.custom_objects_api.create_cluster_custom_object( @@ -562,6 +561,21 @@ def apply_manifest(self, manifest): plural=plural, body=yaml.safe_load(rendered_manifest) ) + elif yaml.safe_load(rendered_manifest)["kind"] in "ExternalSecret": + # Define the ExternalSecret details + group = "external-secrets.io" + version = "v1beta1" + namespace = f"jupyter-{self.spawner.user.name}" + plural = "externalsecrets" + + self.custom_objects_api.create_namespaced_custom_object( + group=group, + version=version, + namespace=namespace, + plural=plural, + body=yaml.safe_load(rendered_manifest) + ) + else: self.spawner.log.info(f"Creating K8s object in namespace {self.namespace}") create_from_dict( @@ -611,6 +625,14 @@ def unapply_manifests(self, manifest_content): plural="releases", name=name, ) + elif kind == "ExternalSecret": + self.custom_objects_api.delete_namespaced_custom_object( + group="external-secrets.io", + version="v1beta1", + namespace=namespace, + plural="externalsecrets", + name=name, + ) # Add other kinds as needed else: self.spawner.log.error(f"Unsupported kind: {kind}") @@ -715,7 +737,6 @@ def initialise(self): # process the config maps config_maps = self.config_parser.get_profile_config_maps(profile_id=profile_id) - # TODO move the manifests before dealing with the config objects # process the manifests manifests = self.config_parser.get_profile_manifests(profile_id=profile_id) From e797c929f60eaefe8394de1c7e76803f892bef23 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 24 Dec 2024 13:27:00 +0100 Subject: [PATCH 47/65] adds namespace labels --- application_hub_context/app_hub_context.py | 23 +++++++++++++++++++--- files/hub/jupyterhub_config.py | 5 +++-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index d96f6d7..309fcec 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -209,6 +209,11 @@ def create_namespace( self, labels: dict = None, annotations: dict = None ) -> client.V1Namespace: + if labels is None: + labels = self.spawner.user_namespace_labels + else: + labels = {**labels, **self.spawner.user_namespace_labels} + if self.is_namespace_created(): self.spawner.log.info( f"namespace {self.namespace} exists, skipping creation" @@ -545,14 +550,14 @@ def apply_manifest(self, manifest): self.spawner.log.info(f"Applying manifest: {yaml.safe_load(rendered_manifest)}") - if yaml.safe_load(rendered_manifest)["kind"] in "Release": + if yaml.safe_load(rendered_manifest)["kind"] in ["Release"]: # see https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/CustomObjectsApi.md#create_namespaced_custom_object # Define the Crossplane HelmRelease details group = "helm.crossplane.io" version = "v1beta1" plural = "releases" - self.spawner.log.info("Creating Crossplane HelmRelease") + self.spawner.log.info(f"Creating Crossplane HelmRelease {yaml.safe_load(rendered_manifest).get('metadata').get('name')}") # Create the Crossplane HelmRelease self.custom_objects_api.create_cluster_custom_object( @@ -561,7 +566,7 @@ def apply_manifest(self, manifest): plural=plural, body=yaml.safe_load(rendered_manifest) ) - elif yaml.safe_load(rendered_manifest)["kind"] in "ExternalSecret": + elif yaml.safe_load(rendered_manifest)["kind"] in ["ExternalSecret"]: # Define the ExternalSecret details group = "external-secrets.io" version = "v1beta1" @@ -576,6 +581,15 @@ def apply_manifest(self, manifest): body=yaml.safe_load(rendered_manifest) ) + elif yaml.safe_load(rendered_manifest)["kind"] in ["Secret"]: + + self.spawner.log.info(f"Creating K8s Secret {yaml.safe_load(rendered_manifest).get('metadata').get('name')}") + create_from_dict( + k8s_client=self.api_client, + data=yaml.safe_load(rendered_manifest), + verbose=True, + namespace=self.namespace, + ) else: self.spawner.log.info(f"Creating K8s object in namespace {self.namespace}") create_from_dict( @@ -647,6 +661,9 @@ def __init__(self, namespace, spawner, config_path: str, kubeconfig_file: TextIO # skip_namespace_check is a flag to skip the namespace check and creation self.skip_namespace_check = skip_namespace_check + # add kwargs as members of the class + self.__dict__.update(kwargs) + super().__init__(namespace, spawner, config_path, kubeconfig_file, **kwargs) def get_profile_list(self): diff --git a/files/hub/jupyterhub_config.py b/files/hub/jupyterhub_config.py index 499b2fa..44fdd69 100644 --- a/files/hub/jupyterhub_config.py +++ b/files/hub/jupyterhub_config.py @@ -50,7 +50,7 @@ def pre_spawn_hook(spawner): namespace = f"{namespace_prefix}-{spawner.user.name}" workspace = DefaultApplicationHubContext( - namespace=namespace, spawner=spawner, config_path=config_path + namespace=namespace, spawner=spawner, config_path=config_path, skip_namespace_check=False, ) workspace.initialise() @@ -102,7 +102,7 @@ def post_stop_hook(spawner): c.LocalAuthenticator.create_system_users = True c.Authenticator.admin_users = {"jovyan"} # Deprecated -c.Authenticator.allowed_users = {"jovyan"} +c.Authenticator.allowed_users = {"jovyan", "alice", "bob"} c.JupyterHub.authenticator_class = "dummy" # HTTP Proxy auth token @@ -158,6 +158,7 @@ def post_stop_hook(spawner): # User namespace c.KubeSpawner.enable_user_namespaces = True +c.KubeSpawner.user_namespace_labels = {"eso": "enabled"} # Volumes # volumes are managed by the pre_spawn_hook/post_stop_hook From 74ff47d384332e2f82040f528bcfc1782bfdd6a4 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 24 Dec 2024 16:56:07 +0100 Subject: [PATCH 48/65] adds eso secrets - needs a review --- application_hub_context/app_hub_context.py | 22 ++-- config-generator/config-generator.ipynb | 7 +- config-generator/manifests/manifest.yaml | 40 +++++++ files/hub/config.yml | 128 ++++++++++++++++++++- sk-k8s/script.yaml | 2 +- skaffold.yaml | 56 ++++++--- 6 files changed, 226 insertions(+), 29 deletions(-) diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index 309fcec..672e20b 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -548,7 +548,7 @@ def apply_manifest(self, manifest): template = Template(yaml.dump(manifest)) rendered_manifest = template.render(spawner=self.spawner) - self.spawner.log.info(f"Applying manifest: {yaml.safe_load(rendered_manifest)}") + self.spawner.log.info(f"Applying manifest name: {yaml.safe_load(rendered_manifest).get('metadata').get('name')}") if yaml.safe_load(rendered_manifest)["kind"] in ["Release"]: # see https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/CustomObjectsApi.md#create_namespaced_custom_object @@ -567,6 +567,7 @@ def apply_manifest(self, manifest): body=yaml.safe_load(rendered_manifest) ) elif yaml.safe_load(rendered_manifest)["kind"] in ["ExternalSecret"]: + self.spawner.log.info(f"Creating ExternalSecret {yaml.safe_load(rendered_manifest).get('metadata').get('name')} in namespace {self.namespace}") # Define the ExternalSecret details group = "external-secrets.io" version = "v1beta1" @@ -583,7 +584,7 @@ def apply_manifest(self, manifest): elif yaml.safe_load(rendered_manifest)["kind"] in ["Secret"]: - self.spawner.log.info(f"Creating K8s Secret {yaml.safe_load(rendered_manifest).get('metadata').get('name')}") + self.spawner.log.info(f"Creating Secret {yaml.safe_load(rendered_manifest).get('metadata').get('name')} in namespace {self.namespace}") create_from_dict( k8s_client=self.api_client, data=yaml.safe_load(rendered_manifest), @@ -591,7 +592,7 @@ def apply_manifest(self, manifest): namespace=self.namespace, ) else: - self.spawner.log.info(f"Creating K8s object in namespace {self.namespace}") + self.spawner.log.info(f"Creating object of kind {yaml.safe_load(rendered_manifest).get('kind')} name {yaml.safe_load(rendered_manifest).get('metadata').get('name')} in namespace {self.namespace}") create_from_dict( k8s_client=self.api_client, data=yaml.safe_load(rendered_manifest), @@ -633,17 +634,21 @@ def unapply_manifests(self, manifest_content): elif kind == "Secret": self.core_v1_api.delete_namespaced_secret(name, namespace) elif kind == "Release": - self.custom_objects_api.delete_cluster_custom_object( + self.spawner.log.info(f"Deleting Crossplane HelmRelease {name}") + response = self.custom_objects_api.delete_cluster_custom_object( group="helm.crossplane.io", version="v1beta1", plural="releases", name=name, ) + self.spawner.log.info(f"Response: {response}") + elif kind == "ExternalSecret": + self.spawner.log.info(f"Deleting ExternalSecret {name} from namespace {self.render(namespace)}") self.custom_objects_api.delete_namespaced_custom_object( group="external-secrets.io", version="v1beta1", - namespace=namespace, + namespace=self.render(namespace), plural="externalsecrets", name=name, ) @@ -764,9 +769,6 @@ def initialise(self): for k8_object in manifest.content: try: - # Log the object and its type - self.spawner.log.info(f"K8 Object: {k8_object}") - self.spawner.log.info(f"Object Type: {type(k8_object)}") # Check and log the 'kind' of the Kubernetes object if 'kind' in k8_object: @@ -934,7 +936,9 @@ def initialise(self): "Skipping creation of image pull secret" f" {self.render(image_pull_secret.name)}" ) - + self.spawner.log.info( + f"Patch service account with image pull secret {self.render(image_pull_secret.name)}" + ) self.patch_service_account(secret_name=self.render(image_pull_secret.name)) # process the init containers diff --git a/config-generator/config-generator.ipynb b/config-generator/config-generator.ipynb index 66b0f91..9afb17b 100644 --- a/config-generator/config-generator.ipynb +++ b/config-generator/config-generator.ipynb @@ -254,8 +254,11 @@ " },\n", " manifests=[localstack_manifest],\n", " env_from_config_maps=[\"my-config\"],\n", - " env_from_secrets=[\"my-secret\"],\n", - " secret_mounts=[SecretMount(name=\"aws-credentials-{{ spawner.user.name }}\", mount_path=\"/workspace/.aws\")],\n", + " env_from_secrets=[\"my-secret\", \"data-by-name\"],\n", + " secret_mounts=[SecretMount(name=\"aws-credentials-{{ spawner.user.name }}\", mount_path=\"/workspace/.aws\"), \n", + " SecretMount(name=\"data-by-name\", mount_path=\"/workspace/.data-by-name\"), \n", + " SecretMount(name=\"eoepca-plus-secret-ro\", mount_path=\"/workspace/.docker\"),],\n", + " image_pull_secrets=[ImagePullSecret(name=\"eoepca-plus-secret-ro\")],\n", " )\n", "\n", " profiles.append(coder_profile)" diff --git a/config-generator/manifests/manifest.yaml b/config-generator/manifests/manifest.yaml index 1a2b228..9ab5c8b 100644 --- a/config-generator/manifests/manifest.yaml +++ b/config-generator/manifests/manifest.yaml @@ -463,3 +463,43 @@ spec: type: ClusterIP providerConfigRef: name: helm-provider +--- +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: data-by-name + namespace: jupyter-{{ spawner.user.name }} +spec: + refreshInterval: 15s + secretStoreRef: + kind: ClusterSecretStore + name: k8s-secret-store + target: + name: data-by-name + creationPolicy: Owner + + data: + - secretKey: secret-value + remoteRef: + key: secret-one # name of the secret + property: "the-key" # key in the secret +--- +apiVersion: external-secrets.io/v1beta1 +kind: ExternalSecret +metadata: + name: eoepca-plus-secret-ro + namespace: jupyter-{{ spawner.user.name }} +spec: + refreshInterval: 15s + secretStoreRef: + kind: ClusterSecretStore + name: k8s-secret-store + target: + name: eoepca-plus-secret-ro + creationPolicy: Owner + + data: + - secretKey: .dockerconfigjson + remoteRef: + key: eoepca-plus-secret-ro # name of the secret + property: ".dockerconfigjson" # key in the secret \ No newline at end of file diff --git a/files/hub/config.yml b/files/hub/config.yml index 60ab795..ac7ed97 100644 --- a/files/hub/config.yml +++ b/files/hub/config.yml @@ -36,11 +36,15 @@ profiles: - my-config env_from_secrets: - my-secret + - data-by-name groups: - group-a - group-b id: profile_studio_coder1 - image_pull_secrets: [] + image_pull_secrets: + - data: null + name: eoepca-plus-secret-ro + persist: true init_containers: [] manifests: - content: @@ -513,6 +517,42 @@ profiles: type: ClusterIP providerConfigRef: name: helm-provider + - apiVersion: external-secrets.io/v1beta1 + kind: ExternalSecret + metadata: + name: data-by-name + namespace: jupyter-{{ spawner.user.name }} + spec: + data: + - remoteRef: + key: secret-one + property: the-key + secretKey: secret-value + refreshInterval: 15s + secretStoreRef: + kind: ClusterSecretStore + name: k8s-secret-store + target: + creationPolicy: Owner + name: data-by-name + - apiVersion: external-secrets.io/v1beta1 + kind: ExternalSecret + metadata: + name: eoepca-plus-secret-ro + namespace: jupyter-{{ spawner.user.name }} + spec: + data: + - remoteRef: + key: eoepca-plus-secret-ro + property: .dockerconfigjson + secretKey: .dockerconfigjson + refreshInterval: 15s + secretStoreRef: + kind: ClusterSecretStore + name: k8s-secret-store + target: + creationPolicy: Owner + name: eoepca-plus-secret-ro key: manifests name: manifests persist: false @@ -524,6 +564,10 @@ profiles: secret_mounts: - mount_path: /workspace/.aws name: aws-credentials-{{ spawner.user.name }} + - mount_path: /workspace/.data-by-name + name: data-by-name + - mount_path: /workspace/.docker + name: eoepca-plus-secret-ro volumes: - access_modes: - ReadWriteMany @@ -582,11 +626,15 @@ profiles: - my-config env_from_secrets: - my-secret + - data-by-name groups: - group-a - group-b id: profile_studio_coder2 - image_pull_secrets: [] + image_pull_secrets: + - data: null + name: eoepca-plus-secret-ro + persist: true init_containers: [] manifests: - content: @@ -1059,6 +1107,42 @@ profiles: type: ClusterIP providerConfigRef: name: helm-provider + - apiVersion: external-secrets.io/v1beta1 + kind: ExternalSecret + metadata: + name: data-by-name + namespace: jupyter-{{ spawner.user.name }} + spec: + data: + - remoteRef: + key: secret-one + property: the-key + secretKey: secret-value + refreshInterval: 15s + secretStoreRef: + kind: ClusterSecretStore + name: k8s-secret-store + target: + creationPolicy: Owner + name: data-by-name + - apiVersion: external-secrets.io/v1beta1 + kind: ExternalSecret + metadata: + name: eoepca-plus-secret-ro + namespace: jupyter-{{ spawner.user.name }} + spec: + data: + - remoteRef: + key: eoepca-plus-secret-ro + property: .dockerconfigjson + secretKey: .dockerconfigjson + refreshInterval: 15s + secretStoreRef: + kind: ClusterSecretStore + name: k8s-secret-store + target: + creationPolicy: Owner + name: eoepca-plus-secret-ro key: manifests name: manifests persist: false @@ -1070,6 +1154,10 @@ profiles: secret_mounts: - mount_path: /workspace/.aws name: aws-credentials-{{ spawner.user.name }} + - mount_path: /workspace/.data-by-name + name: data-by-name + - mount_path: /workspace/.docker + name: eoepca-plus-secret-ro volumes: - access_modes: - ReadWriteMany @@ -1724,6 +1812,42 @@ profiles: type: ClusterIP providerConfigRef: name: helm-provider + - apiVersion: external-secrets.io/v1beta1 + kind: ExternalSecret + metadata: + name: data-by-name + namespace: jupyter-{{ spawner.user.name }} + spec: + data: + - remoteRef: + key: secret-one + property: the-key + secretKey: secret-value + refreshInterval: 15s + secretStoreRef: + kind: ClusterSecretStore + name: k8s-secret-store + target: + creationPolicy: Owner + name: data-by-name + - apiVersion: external-secrets.io/v1beta1 + kind: ExternalSecret + metadata: + name: eoepca-plus-secret-ro + namespace: jupyter-{{ spawner.user.name }} + spec: + data: + - remoteRef: + key: eoepca-plus-secret-ro + property: .dockerconfigjson + secretKey: .dockerconfigjson + refreshInterval: 15s + secretStoreRef: + kind: ClusterSecretStore + name: k8s-secret-store + target: + creationPolicy: Owner + name: eoepca-plus-secret-ro key: manifests name: manifests persist: false diff --git a/sk-k8s/script.yaml b/sk-k8s/script.yaml index 0afd3d7..0eb2a35 100644 --- a/sk-k8s/script.yaml +++ b/sk-k8s/script.yaml @@ -18,5 +18,5 @@ data: do curl --request POST --location http://application-hub-hub.jupyter.svc.cluster.local:8081/hub/api/groups/${group} --header "Authorization: Bearer $token" --header 'Content-Type: application/json' # add user to group - curl --request POST --location http://application-hub-hub.jupyter.svc.cluster.local:8081/hub/api/groups/${group}/users --header "Authorization: Bearer $token" --header 'Content-Type: application/json' --data '{"users": ["jovyan"]}' + curl --request POST --location http://application-hub-hub.jupyter.svc.cluster.local:8081/hub/api/groups/${group}/users --header "Authorization: Bearer $token" --header 'Content-Type: application/json' --data '{"users": ["jovyan", "alice", "bob"]}' done \ No newline at end of file diff --git a/skaffold.yaml b/skaffold.yaml index 534d786..02a88ec 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -1,8 +1,6 @@ apiVersion: skaffold/v4beta9 kind: Config build: - # tagPolicy: - # sha256: {} artifacts: - image: hubimage @@ -11,27 +9,55 @@ profiles: deploy: helm: releases: - - name: application-hub - #chartPath: jupyterhub + - name: jupyterhub remoteChart: eoepca/application-hub namespace: jupyter - version: 4.0.0 + version: 4.0.1 createNamespace: true valuesFiles: [] - # - demo-values.yaml setValueTemplates: - jupyterhub.hub.image.name: "{{.IMAGE_REPO_hubimage}}/{{.IMAGE_TAG_hubimage}}@{{.IMAGE_DIGEST_hubimage}}" + jupyterhub.hub.image.name: "{{.IMAGE_REPO_hubimage}}" + jupyterhub.hub.image.tag: "{{.IMAGE_TAG_hubimage}}@{{.IMAGE_DIGEST_hubimage}}" setValues: + jupyterhub.hub.image.pullSecrets: ["eoepca-plus-secret-ro"] + jupyterhub.hub.db.pvc.storageClassName: scw-bssd + jupyterhub.hub.extraEnv.STORAGE_CLASS: scw-bssd + jupyterhub.proxy.secretToken: "032d5bfe141a7eab86d57587879b33c5d168617cacb339823d7f47fe2933f880" + jupyterhub.hub.extraEnv.APP_HUB_NAMESPACE: jupyter + jupyterhub.hub.networkPolicy.enabled: false + setFiles: + configYml: ./files/hub/config.yml + jupyterConfig: ./files/hub/jupyterhub_config.py + + manifests: + rawYaml: + - sk-k8s/cluster-role-binding.yaml + - sk-k8s/script.yaml + - sk-k8s/job.yaml + + - name: minikube + deploy: + helm: + releases: + - name: jupyterhub + remoteChart: eoepca/application-hub + namespace: jupyter + version: 4.0.1 + createNamespace: true + valuesFiles: [] + setValueTemplates: + jupyterhub.hub.image.name: "{{.IMAGE_REPO_hubimage}}" + jupyterhub.hub.image.tag: "{{.IMAGE_TAG_hubimage}}@{{.IMAGE_DIGEST_hubimage}}" + setValues: + jupyterhub.hub.image.pullSecrets: [] jupyterhub.hub.db.pvc.storageClassName: standard - jupyterhub.hub.extraEnv.JUPYTERHUB_CRYPT_KEY: 032d5bfe141a7eab86d57587879b33c5d168617cacb339823d7f47fe2933f880 + jupyterhub.hub.extraEnv.STORAGE_CLASS: standard + jupyterhub.proxy.secretToken: "032d5bfe141a7eab86d57587879b33c5d168617cacb339823d7f47fe2933f880" jupyterhub.hub.extraEnv.APP_HUB_NAMESPACE: jupyter - setFiles: { - appHubConfig: ./files/hub/config.yml, - jupHubConfig: ./files/hub/jupyterhub_config.py, - #pageTheme: ./files/theme/page.html, - #spawnPendingTheme: ./files/theme/spawn_pending.html, - #spawnTheme: ./files/theme/spawn.html - } + jupyterhub.hub.networkPolicy.enabled: false + setFiles: + configYml: ./files/hub/config.yml + jupyterConfig: ./files/hub/jupyterhub_config.py manifests: rawYaml: From 89e72338c8cfecc55471fb4c25757042d26d981f Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 24 Dec 2024 16:57:29 +0100 Subject: [PATCH 49/65] removes old helm chart --- jupyterhub/.helmignore | 31 - jupyterhub/Chart.yaml | 41 - jupyterhub/README.md | 18 - jupyterhub/files/hub/config.yml | 2151 ----------------- jupyterhub/files/hub/jupyterhub_config.py | 188 -- jupyterhub/files/hub/z2jh.py | 121 - jupyterhub/files/theme/page.html | 5 - jupyterhub/files/theme/spawn.html | 7 - jupyterhub/files/theme/spawn_pending.html | 94 - jupyterhub/requirements.txt | 0 jupyterhub/templates/NOTES.txt | 153 -- jupyterhub/templates/_helpers-names.tpl | 306 --- jupyterhub/templates/_helpers-netpol.tpl | 86 - jupyterhub/templates/_helpers.tpl | 402 --- .../templates/hub/_helpers-passwords.tpl | 92 - jupyterhub/templates/hub/configmap-theme.yaml | 25 - jupyterhub/templates/hub/configmap.yaml | 34 - jupyterhub/templates/hub/deployment.yaml | 252 -- jupyterhub/templates/hub/netpol.yaml | 84 - jupyterhub/templates/hub/pdb.yaml | 23 - jupyterhub/templates/hub/pvc.yaml | 25 - jupyterhub/templates/hub/rbac.yaml | 30 - jupyterhub/templates/hub/secret.yaml | 50 - jupyterhub/templates/hub/service.yaml | 37 - jupyterhub/templates/hub/serviceaccount.yaml | 12 - jupyterhub/templates/image-pull-secret.yaml | 15 - .../image-puller/_helpers-daemonset.tpl | 251 -- .../image-puller/daemonset-continuous.yaml | 8 - .../image-puller/daemonset-hook.yaml | 9 - jupyterhub/templates/image-puller/job.yaml | 76 - .../templates/image-puller/priorityclass.yaml | 18 - jupyterhub/templates/image-puller/rbac.yaml | 45 - .../image-puller/serviceaccount.yaml | 21 - jupyterhub/templates/ingress.yaml | 35 - .../templates/proxy/autohttps/_README.txt | 9 - .../proxy/autohttps/_configmap-dynamic.yaml | 109 - .../proxy/autohttps/_configmap-traefik.yaml | 68 - .../templates/proxy/autohttps/configmap.yaml | 28 - .../templates/proxy/autohttps/deployment.yaml | 154 -- .../templates/proxy/autohttps/netpol.yaml | 78 - jupyterhub/templates/proxy/autohttps/pdb.yaml | 23 - .../templates/proxy/autohttps/rbac.yaml | 35 - .../templates/proxy/autohttps/service.yaml | 25 - .../proxy/autohttps/serviceaccount.yaml | 12 - jupyterhub/templates/proxy/deployment.yaml | 178 -- jupyterhub/templates/proxy/netpol.yaml | 108 - jupyterhub/templates/proxy/pdb.yaml | 23 - jupyterhub/templates/proxy/secret.yaml | 13 - jupyterhub/templates/proxy/service.yaml | 80 - .../scheduling/_scheduling-helpers.tpl | 138 -- .../templates/scheduling/priorityclass.yaml | 15 - .../scheduling/user-placeholder/pdb.yaml | 22 - .../user-placeholder/priorityclass.yaml | 16 - .../user-placeholder/statefulset.yaml | 80 - .../scheduling/user-scheduler/configmap.yaml | 95 - .../scheduling/user-scheduler/deployment.yaml | 109 - .../scheduling/user-scheduler/pdb.yaml | 23 - .../scheduling/user-scheduler/rbac.yaml | 233 -- .../user-scheduler/serviceaccount.yaml | 14 - jupyterhub/templates/singleuser/netpol.yaml | 99 - jupyterhub/templates/singleuser/secret.yaml | 17 - requirements.in | 24 - 62 files changed, 6573 deletions(-) delete mode 100644 jupyterhub/.helmignore delete mode 100644 jupyterhub/Chart.yaml delete mode 100644 jupyterhub/README.md delete mode 100644 jupyterhub/files/hub/config.yml delete mode 100644 jupyterhub/files/hub/jupyterhub_config.py delete mode 100644 jupyterhub/files/hub/z2jh.py delete mode 100644 jupyterhub/files/theme/page.html delete mode 100644 jupyterhub/files/theme/spawn.html delete mode 100644 jupyterhub/files/theme/spawn_pending.html delete mode 100644 jupyterhub/requirements.txt delete mode 100644 jupyterhub/templates/NOTES.txt delete mode 100644 jupyterhub/templates/_helpers-names.tpl delete mode 100644 jupyterhub/templates/_helpers-netpol.tpl delete mode 100644 jupyterhub/templates/_helpers.tpl delete mode 100644 jupyterhub/templates/hub/_helpers-passwords.tpl delete mode 100644 jupyterhub/templates/hub/configmap-theme.yaml delete mode 100644 jupyterhub/templates/hub/configmap.yaml delete mode 100644 jupyterhub/templates/hub/deployment.yaml delete mode 100644 jupyterhub/templates/hub/netpol.yaml delete mode 100644 jupyterhub/templates/hub/pdb.yaml delete mode 100644 jupyterhub/templates/hub/pvc.yaml delete mode 100644 jupyterhub/templates/hub/rbac.yaml delete mode 100644 jupyterhub/templates/hub/secret.yaml delete mode 100644 jupyterhub/templates/hub/service.yaml delete mode 100644 jupyterhub/templates/hub/serviceaccount.yaml delete mode 100644 jupyterhub/templates/image-pull-secret.yaml delete mode 100644 jupyterhub/templates/image-puller/_helpers-daemonset.tpl delete mode 100644 jupyterhub/templates/image-puller/daemonset-continuous.yaml delete mode 100644 jupyterhub/templates/image-puller/daemonset-hook.yaml delete mode 100644 jupyterhub/templates/image-puller/job.yaml delete mode 100644 jupyterhub/templates/image-puller/priorityclass.yaml delete mode 100644 jupyterhub/templates/image-puller/rbac.yaml delete mode 100644 jupyterhub/templates/image-puller/serviceaccount.yaml delete mode 100644 jupyterhub/templates/ingress.yaml delete mode 100644 jupyterhub/templates/proxy/autohttps/_README.txt delete mode 100644 jupyterhub/templates/proxy/autohttps/_configmap-dynamic.yaml delete mode 100644 jupyterhub/templates/proxy/autohttps/_configmap-traefik.yaml delete mode 100644 jupyterhub/templates/proxy/autohttps/configmap.yaml delete mode 100644 jupyterhub/templates/proxy/autohttps/deployment.yaml delete mode 100644 jupyterhub/templates/proxy/autohttps/netpol.yaml delete mode 100644 jupyterhub/templates/proxy/autohttps/pdb.yaml delete mode 100644 jupyterhub/templates/proxy/autohttps/rbac.yaml delete mode 100644 jupyterhub/templates/proxy/autohttps/service.yaml delete mode 100644 jupyterhub/templates/proxy/autohttps/serviceaccount.yaml delete mode 100644 jupyterhub/templates/proxy/deployment.yaml delete mode 100644 jupyterhub/templates/proxy/netpol.yaml delete mode 100644 jupyterhub/templates/proxy/pdb.yaml delete mode 100644 jupyterhub/templates/proxy/secret.yaml delete mode 100644 jupyterhub/templates/proxy/service.yaml delete mode 100644 jupyterhub/templates/scheduling/_scheduling-helpers.tpl delete mode 100644 jupyterhub/templates/scheduling/priorityclass.yaml delete mode 100644 jupyterhub/templates/scheduling/user-placeholder/pdb.yaml delete mode 100644 jupyterhub/templates/scheduling/user-placeholder/priorityclass.yaml delete mode 100644 jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml delete mode 100644 jupyterhub/templates/scheduling/user-scheduler/configmap.yaml delete mode 100644 jupyterhub/templates/scheduling/user-scheduler/deployment.yaml delete mode 100644 jupyterhub/templates/scheduling/user-scheduler/pdb.yaml delete mode 100644 jupyterhub/templates/scheduling/user-scheduler/rbac.yaml delete mode 100644 jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml delete mode 100644 jupyterhub/templates/singleuser/netpol.yaml delete mode 100644 jupyterhub/templates/singleuser/secret.yaml delete mode 100644 requirements.in diff --git a/jupyterhub/.helmignore b/jupyterhub/.helmignore deleted file mode 100644 index 05f3c98..0000000 --- a/jupyterhub/.helmignore +++ /dev/null @@ -1,31 +0,0 @@ -# Anything within the root folder of the Helm chart, where Chart.yaml resides, -# will be embedded into the packaged Helm chart. This is reasonable since only -# when the templates render after the chart has been packaged and distributed, -# will the templates logic evaluate that determines if other files were -# referenced, such as our our files/hub/jupyterhub_config.py. -# -# Here are files that we intentionally ignore to avoid them being packaged, -# because we don't want to reference them from our templates anyhow. -schema.yaml - -# Patterns to ignore when building packages. -# This supports shell glob matching, relative path matching, and -# negation (prefixed with !). Only one pattern per line. -.DS_Store -# Common VCS dirs -.git/ -.gitignore -.bzr/ -.bzrignore -.hg/ -.hgignore -.svn/ -# Common backup files -*.swp -*.bak -*.tmp -*~ -# Various IDEs -.project -.idea/ -*.tmproj diff --git a/jupyterhub/Chart.yaml b/jupyterhub/Chart.yaml deleted file mode 100644 index a6e5198..0000000 --- a/jupyterhub/Chart.yaml +++ /dev/null @@ -1,41 +0,0 @@ -annotations: - artifacthub.io/images: | - - image: jupyterhub/configurable-http-proxy:4.5.3 - name: configurable-http-proxy - - image: jupyterhub/k8s-hub:2.0.0 - name: k8s-hub - - image: jupyterhub/k8s-image-awaiter:2.0.0 - name: k8s-image-awaiter - - image: jupyterhub/k8s-network-tools:2.0.0 - name: k8s-network-tools - - image: jupyterhub/k8s-secret-sync:2.0.0 - name: k8s-secret-sync - - image: jupyterhub/k8s-singleuser-sample:2.0.0 - name: k8s-singleuser-sample - - image: k8s.gcr.io/kube-scheduler:v1.23.10 - name: kube-scheduler - - image: k8s.gcr.io/pause:3.8 - name: pause - - image: k8s.gcr.io/pause:3.8 - name: pause - - image: traefik:v2.8.4 - name: traefik -apiVersion: v2 -appVersion: 3.0.0 -description: Multi-user Jupyter installation -home: https://z2jh.jupyter.org -icon: https://jupyterhub.github.io/helm-chart/images/hublogo.svg -keywords: -- jupyter -- jupyterhub -- z2jh -kubeVersion: '>=1.20.0-0' -maintainers: -- email: erik@sundellopensource.se - name: Erik Sundell -- name: Simon Li - url: https://github.com/manics/ -name: jupyterhub -sources: -- https://github.com/jupyterhub/zero-to-jupyterhub-k8s -version: 2.0.0 diff --git a/jupyterhub/README.md b/jupyterhub/README.md deleted file mode 100644 index bb85ebb..0000000 --- a/jupyterhub/README.md +++ /dev/null @@ -1,18 +0,0 @@ -# JupyterHub Helm chart - -[![Documentation](https://img.shields.io/badge/Documentation-z2jh.jupyter.org-blue?logo=read-the-docs&logoColor=white)](https://z2jh.jupyter.org) -[![GitHub](https://img.shields.io/badge/Source_code-github-blue?logo=github&logoColor=white)](https://github.com/jupyterhub/zero-to-jupyterhub-k8s) -[![Discourse](https://img.shields.io/badge/Help_forum-discourse-blue?logo=discourse&logoColor=white)](https://discourse.jupyter.org/c/jupyterhub/z2jh-k8s) -[![Gitter](https://img.shields.io/badge/Social_chat-gitter-blue?logo=gitter&logoColor=white)](https://gitter.im/jupyterhub/jupyterhub) -
-[![Latest stable release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=Latest%20stable%20release&url=https://jupyterhub.github.io/helm-chart/info.json&query=$.jupyterhub.stable&logo=helm&logoColor=white)](https://jupyterhub.github.io/helm-chart#jupyterhub) -[![Latest pre-release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=Latest%20pre-release&url=https://jupyterhub.github.io/helm-chart/info.json&query=$.jupyterhub.pre&logo=helm&logoColor=white)](https://jupyterhub.github.io/helm-chart#development-releases-jupyterhub) -[![Latest development release of the Helm chart](https://img.shields.io/badge/dynamic/json.svg?label=Latest%20dev%20release&url=https://jupyterhub.github.io/helm-chart/info.json&query=$.jupyterhub.latest&logo=helm&logoColor=white)](https://jupyterhub.github.io/helm-chart#development-releases-jupyterhub) - -The JupyterHub Helm chart is accompanied with an installation guide at [z2jh.jupyter.org](https://z2jh.jupyter.org). Together they enable you to deploy [JupyterHub](https://jupyterhub.readthedocs.io) in a Kubernetes cluster that can make Jupyter environments available to several thousands of simultaneous users. - -## History - -Much of the initial groundwork for this documentation is information learned from the successful use of JupyterHub and Kubernetes at UC Berkeley in their [Data 8](http://data8.org/) program. - -![](https://raw.githubusercontent.com/jupyterhub/zero-to-jupyterhub-k8s/HEAD/docs/source/_static/images/data8_massive_audience.jpg) diff --git a/jupyterhub/files/hub/config.yml b/jupyterhub/files/hub/config.yml deleted file mode 100644 index 6aa3fea..0000000 --- a/jupyterhub/files/hub/config.yml +++ /dev/null @@ -1,2151 +0,0 @@ -profiles: -- config_maps: - - content: "set -x \n\ncd /workspace\n\ngit clone 'https://github.com/eoap/cwl-eoap.git'\n - \ncode-server --install-extension ms-python.python \ncode-server --install-extension - redhat.vscode-yaml\ncode-server --install-extension sbg-rabix.benten-cwl\ncode-server - --install-extension ms-toolsai.jupyter\n\nln -s /workspace/.local/share/code-server/extensions - /workspace/extensions\n\nmkdir -p /workspace/User/\n\necho '{\"workbench.colorTheme\"\ - : \"Visual Studio Dark\"}' > /workspace/User/settings.json\n\npython -m venv - /workspace/.venv\nsource /workspace/.venv/bin/activate\n/workspace/.venv/bin/python - -m pip install --no-cache-dir stactools rasterio requests stac-asset click-logging - tabulate tqdm pystac-client ipykernel loguru scikit-image rio_stac\n\n/workspace/.venv/bin/python - -m ipykernel install --user --name stac_env --display-name \"Python (STAC)\"\ - \nexit 0" - default_mode: null - key: init - mount_path: /opt/init/.init.sh - name: init - persist: false - readonly: true - - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors - /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram - 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ - \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc - \n# >>> conda initialize >>>\n# !! Contents within this block are managed by - 'conda init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' - 2> /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n\ - \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ - \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset - __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ - \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n\n - export PATH=/workspace/.venv/bin:$PATH" - default_mode: null - key: bash-rc - mount_path: /workspace/.bashrc - name: bash-rc - persist: false - readonly: true - - content: source /workspace/.bashrc - default_mode: null - key: bash-login - mount_path: /workspace/.bash_login - name: bash-login - persist: false - readonly: true - default_url: null - definition: - default: false - description: Common Workflow Language (CWL) as a solution for EO Application Packaging - display_name: CWL as a solution for EO Application Portability - kubespawner_override: - cpu_guarantee: 1 - cpu_limit: 2 - extra_resource_guarantees: {} - extra_resource_limits: {} - image: - cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 - mem_guarantee: 4G - mem_limit: 6G - slug: ellip_studio_coder_slug_cwl - groups: - - bids23-coder - - bids23-fair - - cwl-eoap - id: profile_studio_coder_cwl_eoap - image_pull_secrets: - - data: - eyJhdXRocyI6eyJjci50ZXJyYWR1ZS5jb20iOnsidXNlcm5hbWUiOiJyb2JvdCRlbGxpcC1zdHVkaW8iLCJwYXNzd29yZCI6IjIzRlpXSm5PeEtad05RVGVYeWNzb1VnNmxVbTZIeXhTIiwiZW1haWwiOiJlbGxpcC1zdHVkaW9AdGVycmFkdWUuY29tIiwiYXV0aCI6ImNtOWliM1FrWld4c2FYQXRjM1IxWkdsdk9qSXpSbHBYU201UGVFdGFkMDVSVkdWWWVXTnpiMVZuTm14VmJUWkllWGhUIn19fQ== - name: cr-config - persist: false - init_containers: - - command: - - sh - - -c - - sh /opt/init/.init.sh - image: - cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 - name: init-file-on-volume - volume_mounts: - - mount_path: /workspace - name: workspace-volume - - mount_path: /opt/init/.init.sh - name: init - sub_path: init - manifests: - - content: "---\n# Source: localstack/templates/serviceaccount.yaml\napiVersion: - v1\nkind: ServiceAccount\nmetadata:\n name: localstack\n# ---\n# # Source: - localstack/templates/role.yaml\n# apiVersion: rbac.authorization.k8s.io/v1\n - # kind: Role\n# metadata:\n# name: localstack\n# rules:\n# - apiGroups: [\"\ - \"] # \"\" indicates the core API group\n# resources: [\"pods\"]\n# verbs: - [\"*\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/log\"]\n# verbs: [\"\ - get\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/exec\"]\n# verbs: [\"\ - get\", \"create\"]\n# - apiGroups: [\"\"]\n# resources: [\"services\"]\n#\ - \ verbs: [\"get\", \"list\"]\n# ---\n# # Source: localstack/templates/rolebinding.yaml\n - # apiVersion: rbac.authorization.k8s.io/v1\n# kind: RoleBinding\n# metadata:\n\ - # name: localstack\n# subjects:\n# # You can specify more than one \"subject\"\ - \n# - kind: ServiceAccount\n# name: localstack\n# roleRef:\n# kind: Role\n\ - # name: localstack\n# apiGroup: rbac.authorization.k8s.io\n---\n# Source: - localstack/templates/service.yaml\napiVersion: v1\nkind: Service\nmetadata:\n\ - \ name: localstack\n \n \nspec:\n type: ClusterIP\n ports:\n - name: - edge\n port: 4566\n targetPort: 4566\n - name: \"external-service-port-4510\"\ - \n port: 4510\n targetPort: \"ext-svc-4510\"\n - name: \"external-service-port-4511\"\ - \n port: 4511\n targetPort: \"ext-svc-4511\"\n - name: \"external-service-port-4512\"\ - \n port: 4512\n targetPort: \"ext-svc-4512\"\n - name: \"external-service-port-4513\"\ - \n port: 4513\n targetPort: \"ext-svc-4513\"\n - name: \"external-service-port-4514\"\ - \n port: 4514\n targetPort: \"ext-svc-4514\"\n - name: \"external-service-port-4515\"\ - \n port: 4515\n targetPort: \"ext-svc-4515\"\n - name: \"external-service-port-4516\"\ - \n port: 4516\n targetPort: \"ext-svc-4516\"\n - name: \"external-service-port-4517\"\ - \n port: 4517\n targetPort: \"ext-svc-4517\"\n - name: \"external-service-port-4518\"\ - \n port: 4518\n targetPort: \"ext-svc-4518\"\n - name: \"external-service-port-4519\"\ - \n port: 4519\n targetPort: \"ext-svc-4519\"\n - name: \"external-service-port-4520\"\ - \n port: 4520\n targetPort: \"ext-svc-4520\"\n - name: \"external-service-port-4521\"\ - \n port: 4521\n targetPort: \"ext-svc-4521\"\n - name: \"external-service-port-4522\"\ - \n port: 4522\n targetPort: \"ext-svc-4522\"\n - name: \"external-service-port-4523\"\ - \n port: 4523\n targetPort: \"ext-svc-4523\"\n - name: \"external-service-port-4524\"\ - \n port: 4524\n targetPort: \"ext-svc-4524\"\n - name: \"external-service-port-4525\"\ - \n port: 4525\n targetPort: \"ext-svc-4525\"\n - name: \"external-service-port-4526\"\ - \n port: 4526\n targetPort: \"ext-svc-4526\"\n - name: \"external-service-port-4527\"\ - \n port: 4527\n targetPort: \"ext-svc-4527\"\n - name: \"external-service-port-4528\"\ - \n port: 4528\n targetPort: \"ext-svc-4528\"\n - name: \"external-service-port-4529\"\ - \n port: 4529\n targetPort: \"ext-svc-4529\"\n - name: \"external-service-port-4530\"\ - \n port: 4530\n targetPort: \"ext-svc-4530\"\n - name: \"external-service-port-4531\"\ - \n port: 4531\n targetPort: \"ext-svc-4531\"\n - name: \"external-service-port-4532\"\ - \n port: 4532\n targetPort: \"ext-svc-4532\"\n - name: \"external-service-port-4533\"\ - \n port: 4533\n targetPort: \"ext-svc-4533\"\n - name: \"external-service-port-4534\"\ - \n port: 4534\n targetPort: \"ext-svc-4534\"\n - name: \"external-service-port-4535\"\ - \n port: 4535\n targetPort: \"ext-svc-4535\"\n - name: \"external-service-port-4536\"\ - \n port: 4536\n targetPort: \"ext-svc-4536\"\n - name: \"external-service-port-4537\"\ - \n port: 4537\n targetPort: \"ext-svc-4537\"\n - name: \"external-service-port-4538\"\ - \n port: 4538\n targetPort: \"ext-svc-4538\"\n - name: \"external-service-port-4539\"\ - \n port: 4539\n targetPort: \"ext-svc-4539\"\n - name: \"external-service-port-4540\"\ - \n port: 4540\n targetPort: \"ext-svc-4540\"\n - name: \"external-service-port-4541\"\ - \n port: 4541\n targetPort: \"ext-svc-4541\"\n - name: \"external-service-port-4542\"\ - \n port: 4542\n targetPort: \"ext-svc-4542\"\n - name: \"external-service-port-4543\"\ - \n port: 4543\n targetPort: \"ext-svc-4543\"\n - name: \"external-service-port-4544\"\ - \n port: 4544\n targetPort: \"ext-svc-4544\"\n - name: \"external-service-port-4545\"\ - \n port: 4545\n targetPort: \"ext-svc-4545\"\n - name: \"external-service-port-4546\"\ - \n port: 4546\n targetPort: \"ext-svc-4546\"\n - name: \"external-service-port-4547\"\ - \n port: 4547\n targetPort: \"ext-svc-4547\"\n - name: \"external-service-port-4548\"\ - \n port: 4548\n targetPort: \"ext-svc-4548\"\n - name: \"external-service-port-4549\"\ - \n port: 4549\n targetPort: \"ext-svc-4549\"\n - name: \"external-service-port-4550\"\ - \n port: 4550\n targetPort: \"ext-svc-4550\"\n - name: \"external-service-port-4551\"\ - \n port: 4551\n targetPort: \"ext-svc-4551\"\n - name: \"external-service-port-4552\"\ - \n port: 4552\n targetPort: \"ext-svc-4552\"\n - name: \"external-service-port-4553\"\ - \n port: 4553\n targetPort: \"ext-svc-4553\"\n - name: \"external-service-port-4554\"\ - \n port: 4554\n targetPort: \"ext-svc-4554\"\n - name: \"external-service-port-4555\"\ - \n port: 4555\n targetPort: \"ext-svc-4555\"\n - name: \"external-service-port-4556\"\ - \n port: 4556\n targetPort: \"ext-svc-4556\"\n - name: \"external-service-port-4557\"\ - \n port: 4557\n targetPort: \"ext-svc-4557\"\n - name: \"external-service-port-4558\"\ - \n port: 4558\n targetPort: \"ext-svc-4558\"\n - name: \"external-service-port-4559\"\ - \n port: 4559\n targetPort: \"ext-svc-4559\"\n selector:\n app.kubernetes.io/name: - localstack\n app.kubernetes.io/instance: localstack\n---\n# Source: localstack/templates/deployment.yaml\n - apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: localstack\n \n\ - spec:\n replicas: 1\n strategy:\n type: RollingUpdate\n selector:\n \ - \ matchLabels:\n app.kubernetes.io/name: localstack\n app.kubernetes.io/instance: - localstack\n template:\n metadata:\n labels:\n app.kubernetes.io/name: - localstack\n app.kubernetes.io/instance: localstack\n spec:\n \ - \ serviceAccountName: localstack\n securityContext:\n {}\n \ - \ containers:\n - name: localstack\n securityContext:\n \ - \ {}\n image: \"localstack/localstack:latest\"\n imagePullPolicy: - IfNotPresent\n ports:\n - name: edge\n containerPort: - 4566\n protocol: TCP\n - name: \"ext-svc-4510\"\n \ - \ containerPort: 4510\n protocol: TCP\n - - name: \"ext-svc-4511\"\n containerPort: 4511\n protocol: - TCP\n - name: \"ext-svc-4512\"\n containerPort: 4512\n\ - \ protocol: TCP\n - name: \"ext-svc-4513\"\n \ - \ containerPort: 4513\n protocol: TCP\n - name: - \"ext-svc-4514\"\n containerPort: 4514\n protocol: - TCP\n - name: \"ext-svc-4515\"\n containerPort: 4515\n\ - \ protocol: TCP\n - name: \"ext-svc-4516\"\n \ - \ containerPort: 4516\n protocol: TCP\n - name: - \"ext-svc-4517\"\n containerPort: 4517\n protocol: - TCP\n - name: \"ext-svc-4518\"\n containerPort: 4518\n\ - \ protocol: TCP\n - name: \"ext-svc-4519\"\n \ - \ containerPort: 4519\n protocol: TCP\n - name: - \"ext-svc-4520\"\n containerPort: 4520\n protocol: - TCP\n - name: \"ext-svc-4521\"\n containerPort: 4521\n\ - \ protocol: TCP\n - name: \"ext-svc-4522\"\n \ - \ containerPort: 4522\n protocol: TCP\n - name: - \"ext-svc-4523\"\n containerPort: 4523\n protocol: - TCP\n - name: \"ext-svc-4524\"\n containerPort: 4524\n\ - \ protocol: TCP\n - name: \"ext-svc-4525\"\n \ - \ containerPort: 4525\n protocol: TCP\n - name: - \"ext-svc-4526\"\n containerPort: 4526\n protocol: - TCP\n - name: \"ext-svc-4527\"\n containerPort: 4527\n\ - \ protocol: TCP\n - name: \"ext-svc-4528\"\n \ - \ containerPort: 4528\n protocol: TCP\n - name: - \"ext-svc-4529\"\n containerPort: 4529\n protocol: - TCP\n - name: \"ext-svc-4530\"\n containerPort: 4530\n\ - \ protocol: TCP\n - name: \"ext-svc-4531\"\n \ - \ containerPort: 4531\n protocol: TCP\n - name: - \"ext-svc-4532\"\n containerPort: 4532\n protocol: - TCP\n - name: \"ext-svc-4533\"\n containerPort: 4533\n\ - \ protocol: TCP\n - name: \"ext-svc-4534\"\n \ - \ containerPort: 4534\n protocol: TCP\n - name: - \"ext-svc-4535\"\n containerPort: 4535\n protocol: - TCP\n - name: \"ext-svc-4536\"\n containerPort: 4536\n\ - \ protocol: TCP\n - name: \"ext-svc-4537\"\n \ - \ containerPort: 4537\n protocol: TCP\n - name: - \"ext-svc-4538\"\n containerPort: 4538\n protocol: - TCP\n - name: \"ext-svc-4539\"\n containerPort: 4539\n\ - \ protocol: TCP\n - name: \"ext-svc-4540\"\n \ - \ containerPort: 4540\n protocol: TCP\n - name: - \"ext-svc-4541\"\n containerPort: 4541\n protocol: - TCP\n - name: \"ext-svc-4542\"\n containerPort: 4542\n\ - \ protocol: TCP\n - name: \"ext-svc-4543\"\n \ - \ containerPort: 4543\n protocol: TCP\n - name: - \"ext-svc-4544\"\n containerPort: 4544\n protocol: - TCP\n - name: \"ext-svc-4545\"\n containerPort: 4545\n\ - \ protocol: TCP\n - name: \"ext-svc-4546\"\n \ - \ containerPort: 4546\n protocol: TCP\n - name: - \"ext-svc-4547\"\n containerPort: 4547\n protocol: - TCP\n - name: \"ext-svc-4548\"\n containerPort: 4548\n\ - \ protocol: TCP\n - name: \"ext-svc-4549\"\n \ - \ containerPort: 4549\n protocol: TCP\n - name: - \"ext-svc-4550\"\n containerPort: 4550\n protocol: - TCP\n - name: \"ext-svc-4551\"\n containerPort: 4551\n\ - \ protocol: TCP\n - name: \"ext-svc-4552\"\n \ - \ containerPort: 4552\n protocol: TCP\n - name: - \"ext-svc-4553\"\n containerPort: 4553\n protocol: - TCP\n - name: \"ext-svc-4554\"\n containerPort: 4554\n\ - \ protocol: TCP\n - name: \"ext-svc-4555\"\n \ - \ containerPort: 4555\n protocol: TCP\n - name: - \"ext-svc-4556\"\n containerPort: 4556\n protocol: - TCP\n - name: \"ext-svc-4557\"\n containerPort: 4557\n\ - \ protocol: TCP\n - name: \"ext-svc-4558\"\n \ - \ containerPort: 4558\n protocol: TCP\n - name: - \"ext-svc-4559\"\n containerPort: 4559\n protocol: - TCP\n livenessProbe:\n failureThreshold: 3\n \ - \ initialDelaySeconds: 0\n periodSeconds: 10\n successThreshold: - 1\n timeoutSeconds: 1\n httpGet:\n path: - /_localstack/health\n port: edge\n readinessProbe:\n \ - \ failureThreshold: 3\n initialDelaySeconds: 0\n \ - \ periodSeconds: 10\n successThreshold: 1\n timeoutSeconds: - 1\n httpGet:\n path: /_localstack/health\n \ - \ port: edge\n resources: {}\n env:\n - name: - DEBUG\n value: \"0\"\n - name: EXTERNAL_SERVICE_PORTS_START\n\ - \ value: \"4510\"\n - name: EXTERNAL_SERVICE_PORTS_END\n\ - \ value: \"4560\"\n - name: LOCALSTACK_K8S_SERVICE_NAME\n\ - \ value: localstack\n - name: LOCALSTACK_K8S_NAMESPACE\n\ - \ valueFrom:\n fieldRef:\n fieldPath: - metadata.namespace\n - name: LAMBDA_RUNTIME_EXECUTOR\n \ - \ value: \"docker\"\n - name: LAMBDA_K8S_IMAGE_PREFIX\n \ - \ value: \"localstack/lambda-\"\n - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT\n\ - \ value: \"60\"\n - name: OVERRIDE_IN_DOCKER\n \ - \ value: \"1\"\n volumes: []\n" - key: manifests - name: manifests - persist: false - node_selector: - k8s.scaleway.com/pool-name: processing-node-pool - pod_env_vars: - CODE_SERVER_WS: /workspace/cwl-eoap - CONDARC: /workspace/.condarc - CONDA_ENVS_PATH: /workspace/.envs - CWLTOOL_OPTIONS: --podman - HOME: /workspace - XDG_CONFIG_HOME: /workspace/.local - XDG_DATA_HOME: /workspace/.local/share/ - XDG_RUNTIME_DIR: /workspace/.local - role_bindings: [] - volumes: - - access_modes: - - ReadWriteOnce - claim_name: workspace-claim - name: workspace-volume - persist: false - size: 10Gi - storage_class: scw-bssd - volume_mount: - mount_path: /workspace - name: workspace-volume -- config_maps: - - content: "set -x \n\ncd /workspace\n\ngit clone 'https://github.com/eoap/stac-eoap.git'\n - \ncode-server --install-extension ms-python.python \ncode-server --install-extension - redhat.vscode-yaml\ncode-server --install-extension sbg-rabix.benten-cwl\ncode-server - --install-extension ms-toolsai.jupyter\n\nln -s /workspace/.local/share/code-server/extensions - /workspace/extensions\n\nmkdir -p /workspace/User/\n\necho '{\"workbench.colorTheme\"\ - : \"Visual Studio Dark\"}' > /workspace/User/settings.json\n\npython -m venv - /workspace/.venv\nsource /workspace/.venv/bin/activate\n/workspace/.venv/bin/python - -m pip install --no-cache-dir stactools rasterio requests stac-asset click-logging - tabulate tqdm pystac-client ipykernel loguru scikit-image rio_stac\n\n/workspace/.venv/bin/python - -m ipykernel install --user --name stac_env --display-name \"Python (STAC)\"\ - \nexit 0" - default_mode: null - key: init - mount_path: /opt/init/.init.sh - name: init - persist: false - readonly: true - - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors - /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram - 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ - \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc - \n# >>> conda initialize >>>\n# !! Contents within this block are managed by - 'conda init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' - 2> /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n\ - \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ - \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset - __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ - \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n\n - export PATH=/workspace/.venv/bin:$PATH" - default_mode: null - key: bash-rc - mount_path: /workspace/.bashrc - name: bash-rc - persist: false - readonly: true - - content: source /workspace/.bashrc - default_mode: null - key: bash-login - mount_path: /workspace/.bash_login - name: bash-login - persist: false - readonly: true - default_url: null - definition: - default: false - description: Understand the role of STAC in input/output data manifests in EO - data processing workflows - display_name: Understanding STAC for input/output data modelling - kubespawner_override: - cpu_guarantee: 1 - cpu_limit: 2 - extra_resource_guarantees: {} - extra_resource_limits: {} - image: - cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 - mem_guarantee: 4G - mem_limit: 6G - slug: ellip_studio_coder_slug_stac - groups: - - bids23-coder - - bids23-fair - - stac - id: profile_studio_coder_stac - image_pull_secrets: - - data: - eyJhdXRocyI6eyJjci50ZXJyYWR1ZS5jb20iOnsidXNlcm5hbWUiOiJyb2JvdCRlbGxpcC1zdHVkaW8iLCJwYXNzd29yZCI6IjIzRlpXSm5PeEtad05RVGVYeWNzb1VnNmxVbTZIeXhTIiwiZW1haWwiOiJlbGxpcC1zdHVkaW9AdGVycmFkdWUuY29tIiwiYXV0aCI6ImNtOWliM1FrWld4c2FYQXRjM1IxWkdsdk9qSXpSbHBYU201UGVFdGFkMDVSVkdWWWVXTnpiMVZuTm14VmJUWkllWGhUIn19fQ== - name: cr-config - persist: false - init_containers: - - command: - - sh - - -c - - sh /opt/init/.init.sh - image: - cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 - name: init-file-on-volume - volume_mounts: - - mount_path: /workspace - name: workspace-volume - - mount_path: /opt/init/.init.sh - name: init - sub_path: init - manifests: - - content: "---\n# Source: localstack/templates/serviceaccount.yaml\napiVersion: - v1\nkind: ServiceAccount\nmetadata:\n name: localstack\n# ---\n# # Source: - localstack/templates/role.yaml\n# apiVersion: rbac.authorization.k8s.io/v1\n - # kind: Role\n# metadata:\n# name: localstack\n# rules:\n# - apiGroups: [\"\ - \"] # \"\" indicates the core API group\n# resources: [\"pods\"]\n# verbs: - [\"*\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/log\"]\n# verbs: [\"\ - get\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/exec\"]\n# verbs: [\"\ - get\", \"create\"]\n# - apiGroups: [\"\"]\n# resources: [\"services\"]\n#\ - \ verbs: [\"get\", \"list\"]\n# ---\n# # Source: localstack/templates/rolebinding.yaml\n - # apiVersion: rbac.authorization.k8s.io/v1\n# kind: RoleBinding\n# metadata:\n\ - # name: localstack\n# subjects:\n# # You can specify more than one \"subject\"\ - \n# - kind: ServiceAccount\n# name: localstack\n# roleRef:\n# kind: Role\n\ - # name: localstack\n# apiGroup: rbac.authorization.k8s.io\n---\n# Source: - localstack/templates/service.yaml\napiVersion: v1\nkind: Service\nmetadata:\n\ - \ name: localstack\n \n \nspec:\n type: ClusterIP\n ports:\n - name: - edge\n port: 4566\n targetPort: 4566\n - name: \"external-service-port-4510\"\ - \n port: 4510\n targetPort: \"ext-svc-4510\"\n - name: \"external-service-port-4511\"\ - \n port: 4511\n targetPort: \"ext-svc-4511\"\n - name: \"external-service-port-4512\"\ - \n port: 4512\n targetPort: \"ext-svc-4512\"\n - name: \"external-service-port-4513\"\ - \n port: 4513\n targetPort: \"ext-svc-4513\"\n - name: \"external-service-port-4514\"\ - \n port: 4514\n targetPort: \"ext-svc-4514\"\n - name: \"external-service-port-4515\"\ - \n port: 4515\n targetPort: \"ext-svc-4515\"\n - name: \"external-service-port-4516\"\ - \n port: 4516\n targetPort: \"ext-svc-4516\"\n - name: \"external-service-port-4517\"\ - \n port: 4517\n targetPort: \"ext-svc-4517\"\n - name: \"external-service-port-4518\"\ - \n port: 4518\n targetPort: \"ext-svc-4518\"\n - name: \"external-service-port-4519\"\ - \n port: 4519\n targetPort: \"ext-svc-4519\"\n - name: \"external-service-port-4520\"\ - \n port: 4520\n targetPort: \"ext-svc-4520\"\n - name: \"external-service-port-4521\"\ - \n port: 4521\n targetPort: \"ext-svc-4521\"\n - name: \"external-service-port-4522\"\ - \n port: 4522\n targetPort: \"ext-svc-4522\"\n - name: \"external-service-port-4523\"\ - \n port: 4523\n targetPort: \"ext-svc-4523\"\n - name: \"external-service-port-4524\"\ - \n port: 4524\n targetPort: \"ext-svc-4524\"\n - name: \"external-service-port-4525\"\ - \n port: 4525\n targetPort: \"ext-svc-4525\"\n - name: \"external-service-port-4526\"\ - \n port: 4526\n targetPort: \"ext-svc-4526\"\n - name: \"external-service-port-4527\"\ - \n port: 4527\n targetPort: \"ext-svc-4527\"\n - name: \"external-service-port-4528\"\ - \n port: 4528\n targetPort: \"ext-svc-4528\"\n - name: \"external-service-port-4529\"\ - \n port: 4529\n targetPort: \"ext-svc-4529\"\n - name: \"external-service-port-4530\"\ - \n port: 4530\n targetPort: \"ext-svc-4530\"\n - name: \"external-service-port-4531\"\ - \n port: 4531\n targetPort: \"ext-svc-4531\"\n - name: \"external-service-port-4532\"\ - \n port: 4532\n targetPort: \"ext-svc-4532\"\n - name: \"external-service-port-4533\"\ - \n port: 4533\n targetPort: \"ext-svc-4533\"\n - name: \"external-service-port-4534\"\ - \n port: 4534\n targetPort: \"ext-svc-4534\"\n - name: \"external-service-port-4535\"\ - \n port: 4535\n targetPort: \"ext-svc-4535\"\n - name: \"external-service-port-4536\"\ - \n port: 4536\n targetPort: \"ext-svc-4536\"\n - name: \"external-service-port-4537\"\ - \n port: 4537\n targetPort: \"ext-svc-4537\"\n - name: \"external-service-port-4538\"\ - \n port: 4538\n targetPort: \"ext-svc-4538\"\n - name: \"external-service-port-4539\"\ - \n port: 4539\n targetPort: \"ext-svc-4539\"\n - name: \"external-service-port-4540\"\ - \n port: 4540\n targetPort: \"ext-svc-4540\"\n - name: \"external-service-port-4541\"\ - \n port: 4541\n targetPort: \"ext-svc-4541\"\n - name: \"external-service-port-4542\"\ - \n port: 4542\n targetPort: \"ext-svc-4542\"\n - name: \"external-service-port-4543\"\ - \n port: 4543\n targetPort: \"ext-svc-4543\"\n - name: \"external-service-port-4544\"\ - \n port: 4544\n targetPort: \"ext-svc-4544\"\n - name: \"external-service-port-4545\"\ - \n port: 4545\n targetPort: \"ext-svc-4545\"\n - name: \"external-service-port-4546\"\ - \n port: 4546\n targetPort: \"ext-svc-4546\"\n - name: \"external-service-port-4547\"\ - \n port: 4547\n targetPort: \"ext-svc-4547\"\n - name: \"external-service-port-4548\"\ - \n port: 4548\n targetPort: \"ext-svc-4548\"\n - name: \"external-service-port-4549\"\ - \n port: 4549\n targetPort: \"ext-svc-4549\"\n - name: \"external-service-port-4550\"\ - \n port: 4550\n targetPort: \"ext-svc-4550\"\n - name: \"external-service-port-4551\"\ - \n port: 4551\n targetPort: \"ext-svc-4551\"\n - name: \"external-service-port-4552\"\ - \n port: 4552\n targetPort: \"ext-svc-4552\"\n - name: \"external-service-port-4553\"\ - \n port: 4553\n targetPort: \"ext-svc-4553\"\n - name: \"external-service-port-4554\"\ - \n port: 4554\n targetPort: \"ext-svc-4554\"\n - name: \"external-service-port-4555\"\ - \n port: 4555\n targetPort: \"ext-svc-4555\"\n - name: \"external-service-port-4556\"\ - \n port: 4556\n targetPort: \"ext-svc-4556\"\n - name: \"external-service-port-4557\"\ - \n port: 4557\n targetPort: \"ext-svc-4557\"\n - name: \"external-service-port-4558\"\ - \n port: 4558\n targetPort: \"ext-svc-4558\"\n - name: \"external-service-port-4559\"\ - \n port: 4559\n targetPort: \"ext-svc-4559\"\n selector:\n app.kubernetes.io/name: - localstack\n app.kubernetes.io/instance: localstack\n---\n# Source: localstack/templates/deployment.yaml\n - apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: localstack\n \n\ - spec:\n replicas: 1\n strategy:\n type: RollingUpdate\n selector:\n \ - \ matchLabels:\n app.kubernetes.io/name: localstack\n app.kubernetes.io/instance: - localstack\n template:\n metadata:\n labels:\n app.kubernetes.io/name: - localstack\n app.kubernetes.io/instance: localstack\n spec:\n \ - \ serviceAccountName: localstack\n securityContext:\n {}\n \ - \ containers:\n - name: localstack\n securityContext:\n \ - \ {}\n image: \"localstack/localstack:latest\"\n imagePullPolicy: - IfNotPresent\n ports:\n - name: edge\n containerPort: - 4566\n protocol: TCP\n - name: \"ext-svc-4510\"\n \ - \ containerPort: 4510\n protocol: TCP\n - - name: \"ext-svc-4511\"\n containerPort: 4511\n protocol: - TCP\n - name: \"ext-svc-4512\"\n containerPort: 4512\n\ - \ protocol: TCP\n - name: \"ext-svc-4513\"\n \ - \ containerPort: 4513\n protocol: TCP\n - name: - \"ext-svc-4514\"\n containerPort: 4514\n protocol: - TCP\n - name: \"ext-svc-4515\"\n containerPort: 4515\n\ - \ protocol: TCP\n - name: \"ext-svc-4516\"\n \ - \ containerPort: 4516\n protocol: TCP\n - name: - \"ext-svc-4517\"\n containerPort: 4517\n protocol: - TCP\n - name: \"ext-svc-4518\"\n containerPort: 4518\n\ - \ protocol: TCP\n - name: \"ext-svc-4519\"\n \ - \ containerPort: 4519\n protocol: TCP\n - name: - \"ext-svc-4520\"\n containerPort: 4520\n protocol: - TCP\n - name: \"ext-svc-4521\"\n containerPort: 4521\n\ - \ protocol: TCP\n - name: \"ext-svc-4522\"\n \ - \ containerPort: 4522\n protocol: TCP\n - name: - \"ext-svc-4523\"\n containerPort: 4523\n protocol: - TCP\n - name: \"ext-svc-4524\"\n containerPort: 4524\n\ - \ protocol: TCP\n - name: \"ext-svc-4525\"\n \ - \ containerPort: 4525\n protocol: TCP\n - name: - \"ext-svc-4526\"\n containerPort: 4526\n protocol: - TCP\n - name: \"ext-svc-4527\"\n containerPort: 4527\n\ - \ protocol: TCP\n - name: \"ext-svc-4528\"\n \ - \ containerPort: 4528\n protocol: TCP\n - name: - \"ext-svc-4529\"\n containerPort: 4529\n protocol: - TCP\n - name: \"ext-svc-4530\"\n containerPort: 4530\n\ - \ protocol: TCP\n - name: \"ext-svc-4531\"\n \ - \ containerPort: 4531\n protocol: TCP\n - name: - \"ext-svc-4532\"\n containerPort: 4532\n protocol: - TCP\n - name: \"ext-svc-4533\"\n containerPort: 4533\n\ - \ protocol: TCP\n - name: \"ext-svc-4534\"\n \ - \ containerPort: 4534\n protocol: TCP\n - name: - \"ext-svc-4535\"\n containerPort: 4535\n protocol: - TCP\n - name: \"ext-svc-4536\"\n containerPort: 4536\n\ - \ protocol: TCP\n - name: \"ext-svc-4537\"\n \ - \ containerPort: 4537\n protocol: TCP\n - name: - \"ext-svc-4538\"\n containerPort: 4538\n protocol: - TCP\n - name: \"ext-svc-4539\"\n containerPort: 4539\n\ - \ protocol: TCP\n - name: \"ext-svc-4540\"\n \ - \ containerPort: 4540\n protocol: TCP\n - name: - \"ext-svc-4541\"\n containerPort: 4541\n protocol: - TCP\n - name: \"ext-svc-4542\"\n containerPort: 4542\n\ - \ protocol: TCP\n - name: \"ext-svc-4543\"\n \ - \ containerPort: 4543\n protocol: TCP\n - name: - \"ext-svc-4544\"\n containerPort: 4544\n protocol: - TCP\n - name: \"ext-svc-4545\"\n containerPort: 4545\n\ - \ protocol: TCP\n - name: \"ext-svc-4546\"\n \ - \ containerPort: 4546\n protocol: TCP\n - name: - \"ext-svc-4547\"\n containerPort: 4547\n protocol: - TCP\n - name: \"ext-svc-4548\"\n containerPort: 4548\n\ - \ protocol: TCP\n - name: \"ext-svc-4549\"\n \ - \ containerPort: 4549\n protocol: TCP\n - name: - \"ext-svc-4550\"\n containerPort: 4550\n protocol: - TCP\n - name: \"ext-svc-4551\"\n containerPort: 4551\n\ - \ protocol: TCP\n - name: \"ext-svc-4552\"\n \ - \ containerPort: 4552\n protocol: TCP\n - name: - \"ext-svc-4553\"\n containerPort: 4553\n protocol: - TCP\n - name: \"ext-svc-4554\"\n containerPort: 4554\n\ - \ protocol: TCP\n - name: \"ext-svc-4555\"\n \ - \ containerPort: 4555\n protocol: TCP\n - name: - \"ext-svc-4556\"\n containerPort: 4556\n protocol: - TCP\n - name: \"ext-svc-4557\"\n containerPort: 4557\n\ - \ protocol: TCP\n - name: \"ext-svc-4558\"\n \ - \ containerPort: 4558\n protocol: TCP\n - name: - \"ext-svc-4559\"\n containerPort: 4559\n protocol: - TCP\n livenessProbe:\n failureThreshold: 3\n \ - \ initialDelaySeconds: 0\n periodSeconds: 10\n successThreshold: - 1\n timeoutSeconds: 1\n httpGet:\n path: - /_localstack/health\n port: edge\n readinessProbe:\n \ - \ failureThreshold: 3\n initialDelaySeconds: 0\n \ - \ periodSeconds: 10\n successThreshold: 1\n timeoutSeconds: - 1\n httpGet:\n path: /_localstack/health\n \ - \ port: edge\n resources: {}\n env:\n - name: - DEBUG\n value: \"0\"\n - name: EXTERNAL_SERVICE_PORTS_START\n\ - \ value: \"4510\"\n - name: EXTERNAL_SERVICE_PORTS_END\n\ - \ value: \"4560\"\n - name: LOCALSTACK_K8S_SERVICE_NAME\n\ - \ value: localstack\n - name: LOCALSTACK_K8S_NAMESPACE\n\ - \ valueFrom:\n fieldRef:\n fieldPath: - metadata.namespace\n - name: LAMBDA_RUNTIME_EXECUTOR\n \ - \ value: \"docker\"\n - name: LAMBDA_K8S_IMAGE_PREFIX\n \ - \ value: \"localstack/lambda-\"\n - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT\n\ - \ value: \"60\"\n - name: OVERRIDE_IN_DOCKER\n \ - \ value: \"1\"\n volumes: []\n" - key: manifests - name: manifests - persist: false - node_selector: - k8s.scaleway.com/pool-name: processing-node-pool - pod_env_vars: - AWS_ACCESS_KEY_ID: test - AWS_DEFAULT_REGION: us-east-1 - AWS_SECRET_ACCESS_KEY: test - CODE_SERVER_WS: /workspace/stac-eoap - CONDARC: /workspace/.condarc - CONDA_ENVS_PATH: /workspace/.envs - CWLTOOL_OPTIONS: --podman - HOME: /workspace - XDG_CONFIG_HOME: /workspace/.local - XDG_DATA_HOME: /workspace/.local/share/ - XDG_RUNTIME_DIR: /workspace/.local - role_bindings: [] - volumes: - - access_modes: - - ReadWriteOnce - claim_name: workspace-claim - name: workspace-volume - persist: false - size: 10Gi - storage_class: scw-bssd - volume_mount: - mount_path: /workspace - name: workspace-volume -- config_maps: - - content: "set -x \n\ncd /workspace\n\ngit clone 'https://github.com/eoap/quickwin-inline-code.git'\n - \ncode-server --install-extension ms-python.python \ncode-server --install-extension - redhat.vscode-yaml\ncode-server --install-extension sbg-rabix.benten-cwl\ncode-server - --install-extension ms-toolsai.jupyter\n\npip install -r /workspace/quickwin-inline-code/requirements.txt\n - \nln -s /workspace/.local/share/code-server/extensions /workspace/extensions\n - \nmkdir -p /workspace/User/\n\necho '{\"workbench.colorTheme\": \"Visual Studio - Dark\"}' > /workspace/User/settings.json\n\nexit 0" - default_mode: null - key: init - mount_path: /opt/init/.init.sh - name: init - persist: false - readonly: true - - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors - /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram - 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ - \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc - \n# >>> conda initialize >>>\n# !! Contents within this block are managed by - 'conda init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' - 2> /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n\ - \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ - \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset - __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ - \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n\n - export PATH=/workspace/.venv/bin:$PATH" - default_mode: null - key: bash-rc - mount_path: /workspace/.bashrc - name: bash-rc - persist: false - readonly: true - - content: source /workspace/.bashrc - default_mode: null - key: bash-login - mount_path: /workspace/.bash_login - name: bash-login - persist: false - readonly: true - default_url: null - definition: - default: false - description: This tutorial is designed for developers, scientists, and Earth observation - enthusiasts who want to get started with the EO Application Package. - display_name: Quickwin - An Application Package with inline Python code - kubespawner_override: - cpu_guarantee: 1 - cpu_limit: 2 - extra_resource_guarantees: {} - extra_resource_limits: {} - image: - cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 - mem_guarantee: 4G - mem_limit: 6G - slug: ellip_studio_coder_slug_inline - groups: - - future - - inline-code - id: profile_studio_coder_inline - image_pull_secrets: - - data: - eyJhdXRocyI6eyJjci50ZXJyYWR1ZS5jb20iOnsidXNlcm5hbWUiOiJyb2JvdCRlbGxpcC1zdHVkaW8iLCJwYXNzd29yZCI6IjIzRlpXSm5PeEtad05RVGVYeWNzb1VnNmxVbTZIeXhTIiwiZW1haWwiOiJlbGxpcC1zdHVkaW9AdGVycmFkdWUuY29tIiwiYXV0aCI6ImNtOWliM1FrWld4c2FYQXRjM1IxWkdsdk9qSXpSbHBYU201UGVFdGFkMDVSVkdWWWVXTnpiMVZuTm14VmJUWkllWGhUIn19fQ== - name: cr-config - persist: false - init_containers: - - command: - - sh - - -c - - sh /opt/init/.init.sh - image: - cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 - name: init-file-on-volume - volume_mounts: - - mount_path: /workspace - name: workspace-volume - - mount_path: /opt/init/.init.sh - name: init - sub_path: init - manifests: - - content: "---\n# Source: localstack/templates/serviceaccount.yaml\napiVersion: - v1\nkind: ServiceAccount\nmetadata:\n name: localstack\n# ---\n# # Source: - localstack/templates/role.yaml\n# apiVersion: rbac.authorization.k8s.io/v1\n - # kind: Role\n# metadata:\n# name: localstack\n# rules:\n# - apiGroups: [\"\ - \"] # \"\" indicates the core API group\n# resources: [\"pods\"]\n# verbs: - [\"*\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/log\"]\n# verbs: [\"\ - get\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/exec\"]\n# verbs: [\"\ - get\", \"create\"]\n# - apiGroups: [\"\"]\n# resources: [\"services\"]\n#\ - \ verbs: [\"get\", \"list\"]\n# ---\n# # Source: localstack/templates/rolebinding.yaml\n - # apiVersion: rbac.authorization.k8s.io/v1\n# kind: RoleBinding\n# metadata:\n\ - # name: localstack\n# subjects:\n# # You can specify more than one \"subject\"\ - \n# - kind: ServiceAccount\n# name: localstack\n# roleRef:\n# kind: Role\n\ - # name: localstack\n# apiGroup: rbac.authorization.k8s.io\n---\n# Source: - localstack/templates/service.yaml\napiVersion: v1\nkind: Service\nmetadata:\n\ - \ name: localstack\n \n \nspec:\n type: ClusterIP\n ports:\n - name: - edge\n port: 4566\n targetPort: 4566\n - name: \"external-service-port-4510\"\ - \n port: 4510\n targetPort: \"ext-svc-4510\"\n - name: \"external-service-port-4511\"\ - \n port: 4511\n targetPort: \"ext-svc-4511\"\n - name: \"external-service-port-4512\"\ - \n port: 4512\n targetPort: \"ext-svc-4512\"\n - name: \"external-service-port-4513\"\ - \n port: 4513\n targetPort: \"ext-svc-4513\"\n - name: \"external-service-port-4514\"\ - \n port: 4514\n targetPort: \"ext-svc-4514\"\n - name: \"external-service-port-4515\"\ - \n port: 4515\n targetPort: \"ext-svc-4515\"\n - name: \"external-service-port-4516\"\ - \n port: 4516\n targetPort: \"ext-svc-4516\"\n - name: \"external-service-port-4517\"\ - \n port: 4517\n targetPort: \"ext-svc-4517\"\n - name: \"external-service-port-4518\"\ - \n port: 4518\n targetPort: \"ext-svc-4518\"\n - name: \"external-service-port-4519\"\ - \n port: 4519\n targetPort: \"ext-svc-4519\"\n - name: \"external-service-port-4520\"\ - \n port: 4520\n targetPort: \"ext-svc-4520\"\n - name: \"external-service-port-4521\"\ - \n port: 4521\n targetPort: \"ext-svc-4521\"\n - name: \"external-service-port-4522\"\ - \n port: 4522\n targetPort: \"ext-svc-4522\"\n - name: \"external-service-port-4523\"\ - \n port: 4523\n targetPort: \"ext-svc-4523\"\n - name: \"external-service-port-4524\"\ - \n port: 4524\n targetPort: \"ext-svc-4524\"\n - name: \"external-service-port-4525\"\ - \n port: 4525\n targetPort: \"ext-svc-4525\"\n - name: \"external-service-port-4526\"\ - \n port: 4526\n targetPort: \"ext-svc-4526\"\n - name: \"external-service-port-4527\"\ - \n port: 4527\n targetPort: \"ext-svc-4527\"\n - name: \"external-service-port-4528\"\ - \n port: 4528\n targetPort: \"ext-svc-4528\"\n - name: \"external-service-port-4529\"\ - \n port: 4529\n targetPort: \"ext-svc-4529\"\n - name: \"external-service-port-4530\"\ - \n port: 4530\n targetPort: \"ext-svc-4530\"\n - name: \"external-service-port-4531\"\ - \n port: 4531\n targetPort: \"ext-svc-4531\"\n - name: \"external-service-port-4532\"\ - \n port: 4532\n targetPort: \"ext-svc-4532\"\n - name: \"external-service-port-4533\"\ - \n port: 4533\n targetPort: \"ext-svc-4533\"\n - name: \"external-service-port-4534\"\ - \n port: 4534\n targetPort: \"ext-svc-4534\"\n - name: \"external-service-port-4535\"\ - \n port: 4535\n targetPort: \"ext-svc-4535\"\n - name: \"external-service-port-4536\"\ - \n port: 4536\n targetPort: \"ext-svc-4536\"\n - name: \"external-service-port-4537\"\ - \n port: 4537\n targetPort: \"ext-svc-4537\"\n - name: \"external-service-port-4538\"\ - \n port: 4538\n targetPort: \"ext-svc-4538\"\n - name: \"external-service-port-4539\"\ - \n port: 4539\n targetPort: \"ext-svc-4539\"\n - name: \"external-service-port-4540\"\ - \n port: 4540\n targetPort: \"ext-svc-4540\"\n - name: \"external-service-port-4541\"\ - \n port: 4541\n targetPort: \"ext-svc-4541\"\n - name: \"external-service-port-4542\"\ - \n port: 4542\n targetPort: \"ext-svc-4542\"\n - name: \"external-service-port-4543\"\ - \n port: 4543\n targetPort: \"ext-svc-4543\"\n - name: \"external-service-port-4544\"\ - \n port: 4544\n targetPort: \"ext-svc-4544\"\n - name: \"external-service-port-4545\"\ - \n port: 4545\n targetPort: \"ext-svc-4545\"\n - name: \"external-service-port-4546\"\ - \n port: 4546\n targetPort: \"ext-svc-4546\"\n - name: \"external-service-port-4547\"\ - \n port: 4547\n targetPort: \"ext-svc-4547\"\n - name: \"external-service-port-4548\"\ - \n port: 4548\n targetPort: \"ext-svc-4548\"\n - name: \"external-service-port-4549\"\ - \n port: 4549\n targetPort: \"ext-svc-4549\"\n - name: \"external-service-port-4550\"\ - \n port: 4550\n targetPort: \"ext-svc-4550\"\n - name: \"external-service-port-4551\"\ - \n port: 4551\n targetPort: \"ext-svc-4551\"\n - name: \"external-service-port-4552\"\ - \n port: 4552\n targetPort: \"ext-svc-4552\"\n - name: \"external-service-port-4553\"\ - \n port: 4553\n targetPort: \"ext-svc-4553\"\n - name: \"external-service-port-4554\"\ - \n port: 4554\n targetPort: \"ext-svc-4554\"\n - name: \"external-service-port-4555\"\ - \n port: 4555\n targetPort: \"ext-svc-4555\"\n - name: \"external-service-port-4556\"\ - \n port: 4556\n targetPort: \"ext-svc-4556\"\n - name: \"external-service-port-4557\"\ - \n port: 4557\n targetPort: \"ext-svc-4557\"\n - name: \"external-service-port-4558\"\ - \n port: 4558\n targetPort: \"ext-svc-4558\"\n - name: \"external-service-port-4559\"\ - \n port: 4559\n targetPort: \"ext-svc-4559\"\n selector:\n app.kubernetes.io/name: - localstack\n app.kubernetes.io/instance: localstack\n---\n# Source: localstack/templates/deployment.yaml\n - apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: localstack\n \n\ - spec:\n replicas: 1\n strategy:\n type: RollingUpdate\n selector:\n \ - \ matchLabels:\n app.kubernetes.io/name: localstack\n app.kubernetes.io/instance: - localstack\n template:\n metadata:\n labels:\n app.kubernetes.io/name: - localstack\n app.kubernetes.io/instance: localstack\n spec:\n \ - \ serviceAccountName: localstack\n securityContext:\n {}\n \ - \ containers:\n - name: localstack\n securityContext:\n \ - \ {}\n image: \"localstack/localstack:latest\"\n imagePullPolicy: - IfNotPresent\n ports:\n - name: edge\n containerPort: - 4566\n protocol: TCP\n - name: \"ext-svc-4510\"\n \ - \ containerPort: 4510\n protocol: TCP\n - - name: \"ext-svc-4511\"\n containerPort: 4511\n protocol: - TCP\n - name: \"ext-svc-4512\"\n containerPort: 4512\n\ - \ protocol: TCP\n - name: \"ext-svc-4513\"\n \ - \ containerPort: 4513\n protocol: TCP\n - name: - \"ext-svc-4514\"\n containerPort: 4514\n protocol: - TCP\n - name: \"ext-svc-4515\"\n containerPort: 4515\n\ - \ protocol: TCP\n - name: \"ext-svc-4516\"\n \ - \ containerPort: 4516\n protocol: TCP\n - name: - \"ext-svc-4517\"\n containerPort: 4517\n protocol: - TCP\n - name: \"ext-svc-4518\"\n containerPort: 4518\n\ - \ protocol: TCP\n - name: \"ext-svc-4519\"\n \ - \ containerPort: 4519\n protocol: TCP\n - name: - \"ext-svc-4520\"\n containerPort: 4520\n protocol: - TCP\n - name: \"ext-svc-4521\"\n containerPort: 4521\n\ - \ protocol: TCP\n - name: \"ext-svc-4522\"\n \ - \ containerPort: 4522\n protocol: TCP\n - name: - \"ext-svc-4523\"\n containerPort: 4523\n protocol: - TCP\n - name: \"ext-svc-4524\"\n containerPort: 4524\n\ - \ protocol: TCP\n - name: \"ext-svc-4525\"\n \ - \ containerPort: 4525\n protocol: TCP\n - name: - \"ext-svc-4526\"\n containerPort: 4526\n protocol: - TCP\n - name: \"ext-svc-4527\"\n containerPort: 4527\n\ - \ protocol: TCP\n - name: \"ext-svc-4528\"\n \ - \ containerPort: 4528\n protocol: TCP\n - name: - \"ext-svc-4529\"\n containerPort: 4529\n protocol: - TCP\n - name: \"ext-svc-4530\"\n containerPort: 4530\n\ - \ protocol: TCP\n - name: \"ext-svc-4531\"\n \ - \ containerPort: 4531\n protocol: TCP\n - name: - \"ext-svc-4532\"\n containerPort: 4532\n protocol: - TCP\n - name: \"ext-svc-4533\"\n containerPort: 4533\n\ - \ protocol: TCP\n - name: \"ext-svc-4534\"\n \ - \ containerPort: 4534\n protocol: TCP\n - name: - \"ext-svc-4535\"\n containerPort: 4535\n protocol: - TCP\n - name: \"ext-svc-4536\"\n containerPort: 4536\n\ - \ protocol: TCP\n - name: \"ext-svc-4537\"\n \ - \ containerPort: 4537\n protocol: TCP\n - name: - \"ext-svc-4538\"\n containerPort: 4538\n protocol: - TCP\n - name: \"ext-svc-4539\"\n containerPort: 4539\n\ - \ protocol: TCP\n - name: \"ext-svc-4540\"\n \ - \ containerPort: 4540\n protocol: TCP\n - name: - \"ext-svc-4541\"\n containerPort: 4541\n protocol: - TCP\n - name: \"ext-svc-4542\"\n containerPort: 4542\n\ - \ protocol: TCP\n - name: \"ext-svc-4543\"\n \ - \ containerPort: 4543\n protocol: TCP\n - name: - \"ext-svc-4544\"\n containerPort: 4544\n protocol: - TCP\n - name: \"ext-svc-4545\"\n containerPort: 4545\n\ - \ protocol: TCP\n - name: \"ext-svc-4546\"\n \ - \ containerPort: 4546\n protocol: TCP\n - name: - \"ext-svc-4547\"\n containerPort: 4547\n protocol: - TCP\n - name: \"ext-svc-4548\"\n containerPort: 4548\n\ - \ protocol: TCP\n - name: \"ext-svc-4549\"\n \ - \ containerPort: 4549\n protocol: TCP\n - name: - \"ext-svc-4550\"\n containerPort: 4550\n protocol: - TCP\n - name: \"ext-svc-4551\"\n containerPort: 4551\n\ - \ protocol: TCP\n - name: \"ext-svc-4552\"\n \ - \ containerPort: 4552\n protocol: TCP\n - name: - \"ext-svc-4553\"\n containerPort: 4553\n protocol: - TCP\n - name: \"ext-svc-4554\"\n containerPort: 4554\n\ - \ protocol: TCP\n - name: \"ext-svc-4555\"\n \ - \ containerPort: 4555\n protocol: TCP\n - name: - \"ext-svc-4556\"\n containerPort: 4556\n protocol: - TCP\n - name: \"ext-svc-4557\"\n containerPort: 4557\n\ - \ protocol: TCP\n - name: \"ext-svc-4558\"\n \ - \ containerPort: 4558\n protocol: TCP\n - name: - \"ext-svc-4559\"\n containerPort: 4559\n protocol: - TCP\n livenessProbe:\n failureThreshold: 3\n \ - \ initialDelaySeconds: 0\n periodSeconds: 10\n successThreshold: - 1\n timeoutSeconds: 1\n httpGet:\n path: - /_localstack/health\n port: edge\n readinessProbe:\n \ - \ failureThreshold: 3\n initialDelaySeconds: 0\n \ - \ periodSeconds: 10\n successThreshold: 1\n timeoutSeconds: - 1\n httpGet:\n path: /_localstack/health\n \ - \ port: edge\n resources: {}\n env:\n - name: - DEBUG\n value: \"0\"\n - name: EXTERNAL_SERVICE_PORTS_START\n\ - \ value: \"4510\"\n - name: EXTERNAL_SERVICE_PORTS_END\n\ - \ value: \"4560\"\n - name: LOCALSTACK_K8S_SERVICE_NAME\n\ - \ value: localstack\n - name: LOCALSTACK_K8S_NAMESPACE\n\ - \ valueFrom:\n fieldRef:\n fieldPath: - metadata.namespace\n - name: LAMBDA_RUNTIME_EXECUTOR\n \ - \ value: \"docker\"\n - name: LAMBDA_K8S_IMAGE_PREFIX\n \ - \ value: \"localstack/lambda-\"\n - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT\n\ - \ value: \"60\"\n - name: OVERRIDE_IN_DOCKER\n \ - \ value: \"1\"\n volumes: []\n" - key: manifests - name: manifests - persist: false - node_selector: - k8s.scaleway.com/pool-name: processing-node-pool - pod_env_vars: - CODE_SERVER_WS: /workspace/quickwin-inline-code - CONDARC: /workspace/.condarc - CONDA_ENVS_PATH: /workspace/.envs - CWLTOOL_OPTIONS: --podman - HOME: /workspace - XDG_CONFIG_HOME: /workspace/.local - XDG_DATA_HOME: /workspace/.local/share/ - XDG_RUNTIME_DIR: /workspace/.local - role_bindings: - - name: log-reader-role-binding - persist: false - role: - api_groups: - - '' - name: log-reader-role - resources: - - pods - - pods/log - verbs: - - get - - list - - watch - subjects: - - kind: ServiceAccount - name: default - - name: pod-reader-role-binding - persist: false - role: - api_groups: - - '' - name: pod-manager-role - resources: - - pods - verbs: - - create - - patch - - delete - - list - - watch - subjects: - - kind: ServiceAccount - name: default - volumes: - - access_modes: - - ReadWriteMany - claim_name: calrissian-claim - name: calrissian-volume - persist: false - size: 50Gi - storage_class: openebs-kernel-nfs-scw - volume_mount: - mount_path: /calrissian - name: calrissian-volume - - access_modes: - - ReadWriteOnce - claim_name: workspace-claim - name: workspace-volume - persist: false - size: 10Gi - storage_class: scw-bssd - volume_mount: - mount_path: /workspace - name: workspace-volume -- config_maps: - - content: "set -x \n\ncd /workspace\n\ngit clone 'https://github.com/eoap/quickwin.git'\n - \ncode-server --install-extension ms-python.python \ncode-server --install-extension - redhat.vscode-yaml\ncode-server --install-extension sbg-rabix.benten-cwl\ncode-server - --install-extension ms-toolsai.jupyter\n\nln -s /workspace/.local/share/code-server/extensions - /workspace/extensions\n\nmkdir -p /workspace/User/\necho '{\"workbench.colorTheme\"\ - : \"Visual Studio Dark\"}' > /workspace/User/settings.json\n\npython -m venv - /workspace/.venv\nsource /workspace/.venv/bin/activate\n/workspace/.venv/bin/python - -m pip install --no-cache-dir rasterio click pystac loguru pyproj shapely scikit-image - pystac rio_stac ipykernel stactools[validate]\n/workspace/.venv/bin/python -m - ipykernel install --user --name quickwin_env --display-name \"Python (Quickwin)\"\ - \n\nexit 0" - default_mode: null - key: init - mount_path: /opt/init/.init.sh - name: init - persist: false - readonly: true - - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors - /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram - 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ - \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc - \n# >>> conda initialize >>>\n# !! Contents within this block are managed by - 'conda init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' - 2> /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n\ - \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ - \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset - __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ - \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n\n - export PATH=/workspace/.venv/bin:$PATH" - default_mode: null - key: bash-rc - mount_path: /workspace/.bashrc - name: bash-rc - persist: false - readonly: true - - content: source /workspace/.bashrc - default_mode: null - key: bash-login - mount_path: /workspace/.bash_login - name: bash-login - persist: false - readonly: true - default_url: null - definition: - default: false - description: This tutorial is designed for developers, scientists, and Earth observation - enthusiasts who want to get started with the EO Application Package. - display_name: Getting started on Earth Observation Application Packaging with - CWL - kubespawner_override: - cpu_guarantee: 1 - cpu_limit: 2 - extra_resource_guarantees: {} - extra_resource_limits: {} - image: - cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 - mem_guarantee: 4G - mem_limit: 6G - slug: ellip_studio_coder_slug_quickwin - groups: - - bids23-coder - - bids23-fair - - quickwin - id: profile_studio_coder_quickwin - image_pull_secrets: - - data: - eyJhdXRocyI6eyJjci50ZXJyYWR1ZS5jb20iOnsidXNlcm5hbWUiOiJyb2JvdCRlbGxpcC1zdHVkaW8iLCJwYXNzd29yZCI6IjIzRlpXSm5PeEtad05RVGVYeWNzb1VnNmxVbTZIeXhTIiwiZW1haWwiOiJlbGxpcC1zdHVkaW9AdGVycmFkdWUuY29tIiwiYXV0aCI6ImNtOWliM1FrWld4c2FYQXRjM1IxWkdsdk9qSXpSbHBYU201UGVFdGFkMDVSVkdWWWVXTnpiMVZuTm14VmJUWkllWGhUIn19fQ== - name: cr-config - persist: false - init_containers: - - command: - - sh - - -c - - sh /opt/init/.init.sh - image: - cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 - name: init-file-on-volume - volume_mounts: - - mount_path: /workspace - name: workspace-volume - - mount_path: /opt/init/.init.sh - name: init - sub_path: init - manifests: - - content: "---\n# Source: localstack/templates/serviceaccount.yaml\napiVersion: - v1\nkind: ServiceAccount\nmetadata:\n name: localstack\n# ---\n# # Source: - localstack/templates/role.yaml\n# apiVersion: rbac.authorization.k8s.io/v1\n - # kind: Role\n# metadata:\n# name: localstack\n# rules:\n# - apiGroups: [\"\ - \"] # \"\" indicates the core API group\n# resources: [\"pods\"]\n# verbs: - [\"*\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/log\"]\n# verbs: [\"\ - get\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/exec\"]\n# verbs: [\"\ - get\", \"create\"]\n# - apiGroups: [\"\"]\n# resources: [\"services\"]\n#\ - \ verbs: [\"get\", \"list\"]\n# ---\n# # Source: localstack/templates/rolebinding.yaml\n - # apiVersion: rbac.authorization.k8s.io/v1\n# kind: RoleBinding\n# metadata:\n\ - # name: localstack\n# subjects:\n# # You can specify more than one \"subject\"\ - \n# - kind: ServiceAccount\n# name: localstack\n# roleRef:\n# kind: Role\n\ - # name: localstack\n# apiGroup: rbac.authorization.k8s.io\n---\n# Source: - localstack/templates/service.yaml\napiVersion: v1\nkind: Service\nmetadata:\n\ - \ name: localstack\n \n \nspec:\n type: ClusterIP\n ports:\n - name: - edge\n port: 4566\n targetPort: 4566\n - name: \"external-service-port-4510\"\ - \n port: 4510\n targetPort: \"ext-svc-4510\"\n - name: \"external-service-port-4511\"\ - \n port: 4511\n targetPort: \"ext-svc-4511\"\n - name: \"external-service-port-4512\"\ - \n port: 4512\n targetPort: \"ext-svc-4512\"\n - name: \"external-service-port-4513\"\ - \n port: 4513\n targetPort: \"ext-svc-4513\"\n - name: \"external-service-port-4514\"\ - \n port: 4514\n targetPort: \"ext-svc-4514\"\n - name: \"external-service-port-4515\"\ - \n port: 4515\n targetPort: \"ext-svc-4515\"\n - name: \"external-service-port-4516\"\ - \n port: 4516\n targetPort: \"ext-svc-4516\"\n - name: \"external-service-port-4517\"\ - \n port: 4517\n targetPort: \"ext-svc-4517\"\n - name: \"external-service-port-4518\"\ - \n port: 4518\n targetPort: \"ext-svc-4518\"\n - name: \"external-service-port-4519\"\ - \n port: 4519\n targetPort: \"ext-svc-4519\"\n - name: \"external-service-port-4520\"\ - \n port: 4520\n targetPort: \"ext-svc-4520\"\n - name: \"external-service-port-4521\"\ - \n port: 4521\n targetPort: \"ext-svc-4521\"\n - name: \"external-service-port-4522\"\ - \n port: 4522\n targetPort: \"ext-svc-4522\"\n - name: \"external-service-port-4523\"\ - \n port: 4523\n targetPort: \"ext-svc-4523\"\n - name: \"external-service-port-4524\"\ - \n port: 4524\n targetPort: \"ext-svc-4524\"\n - name: \"external-service-port-4525\"\ - \n port: 4525\n targetPort: \"ext-svc-4525\"\n - name: \"external-service-port-4526\"\ - \n port: 4526\n targetPort: \"ext-svc-4526\"\n - name: \"external-service-port-4527\"\ - \n port: 4527\n targetPort: \"ext-svc-4527\"\n - name: \"external-service-port-4528\"\ - \n port: 4528\n targetPort: \"ext-svc-4528\"\n - name: \"external-service-port-4529\"\ - \n port: 4529\n targetPort: \"ext-svc-4529\"\n - name: \"external-service-port-4530\"\ - \n port: 4530\n targetPort: \"ext-svc-4530\"\n - name: \"external-service-port-4531\"\ - \n port: 4531\n targetPort: \"ext-svc-4531\"\n - name: \"external-service-port-4532\"\ - \n port: 4532\n targetPort: \"ext-svc-4532\"\n - name: \"external-service-port-4533\"\ - \n port: 4533\n targetPort: \"ext-svc-4533\"\n - name: \"external-service-port-4534\"\ - \n port: 4534\n targetPort: \"ext-svc-4534\"\n - name: \"external-service-port-4535\"\ - \n port: 4535\n targetPort: \"ext-svc-4535\"\n - name: \"external-service-port-4536\"\ - \n port: 4536\n targetPort: \"ext-svc-4536\"\n - name: \"external-service-port-4537\"\ - \n port: 4537\n targetPort: \"ext-svc-4537\"\n - name: \"external-service-port-4538\"\ - \n port: 4538\n targetPort: \"ext-svc-4538\"\n - name: \"external-service-port-4539\"\ - \n port: 4539\n targetPort: \"ext-svc-4539\"\n - name: \"external-service-port-4540\"\ - \n port: 4540\n targetPort: \"ext-svc-4540\"\n - name: \"external-service-port-4541\"\ - \n port: 4541\n targetPort: \"ext-svc-4541\"\n - name: \"external-service-port-4542\"\ - \n port: 4542\n targetPort: \"ext-svc-4542\"\n - name: \"external-service-port-4543\"\ - \n port: 4543\n targetPort: \"ext-svc-4543\"\n - name: \"external-service-port-4544\"\ - \n port: 4544\n targetPort: \"ext-svc-4544\"\n - name: \"external-service-port-4545\"\ - \n port: 4545\n targetPort: \"ext-svc-4545\"\n - name: \"external-service-port-4546\"\ - \n port: 4546\n targetPort: \"ext-svc-4546\"\n - name: \"external-service-port-4547\"\ - \n port: 4547\n targetPort: \"ext-svc-4547\"\n - name: \"external-service-port-4548\"\ - \n port: 4548\n targetPort: \"ext-svc-4548\"\n - name: \"external-service-port-4549\"\ - \n port: 4549\n targetPort: \"ext-svc-4549\"\n - name: \"external-service-port-4550\"\ - \n port: 4550\n targetPort: \"ext-svc-4550\"\n - name: \"external-service-port-4551\"\ - \n port: 4551\n targetPort: \"ext-svc-4551\"\n - name: \"external-service-port-4552\"\ - \n port: 4552\n targetPort: \"ext-svc-4552\"\n - name: \"external-service-port-4553\"\ - \n port: 4553\n targetPort: \"ext-svc-4553\"\n - name: \"external-service-port-4554\"\ - \n port: 4554\n targetPort: \"ext-svc-4554\"\n - name: \"external-service-port-4555\"\ - \n port: 4555\n targetPort: \"ext-svc-4555\"\n - name: \"external-service-port-4556\"\ - \n port: 4556\n targetPort: \"ext-svc-4556\"\n - name: \"external-service-port-4557\"\ - \n port: 4557\n targetPort: \"ext-svc-4557\"\n - name: \"external-service-port-4558\"\ - \n port: 4558\n targetPort: \"ext-svc-4558\"\n - name: \"external-service-port-4559\"\ - \n port: 4559\n targetPort: \"ext-svc-4559\"\n selector:\n app.kubernetes.io/name: - localstack\n app.kubernetes.io/instance: localstack\n---\n# Source: localstack/templates/deployment.yaml\n - apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: localstack\n \n\ - spec:\n replicas: 1\n strategy:\n type: RollingUpdate\n selector:\n \ - \ matchLabels:\n app.kubernetes.io/name: localstack\n app.kubernetes.io/instance: - localstack\n template:\n metadata:\n labels:\n app.kubernetes.io/name: - localstack\n app.kubernetes.io/instance: localstack\n spec:\n \ - \ serviceAccountName: localstack\n securityContext:\n {}\n \ - \ containers:\n - name: localstack\n securityContext:\n \ - \ {}\n image: \"localstack/localstack:latest\"\n imagePullPolicy: - IfNotPresent\n ports:\n - name: edge\n containerPort: - 4566\n protocol: TCP\n - name: \"ext-svc-4510\"\n \ - \ containerPort: 4510\n protocol: TCP\n - - name: \"ext-svc-4511\"\n containerPort: 4511\n protocol: - TCP\n - name: \"ext-svc-4512\"\n containerPort: 4512\n\ - \ protocol: TCP\n - name: \"ext-svc-4513\"\n \ - \ containerPort: 4513\n protocol: TCP\n - name: - \"ext-svc-4514\"\n containerPort: 4514\n protocol: - TCP\n - name: \"ext-svc-4515\"\n containerPort: 4515\n\ - \ protocol: TCP\n - name: \"ext-svc-4516\"\n \ - \ containerPort: 4516\n protocol: TCP\n - name: - \"ext-svc-4517\"\n containerPort: 4517\n protocol: - TCP\n - name: \"ext-svc-4518\"\n containerPort: 4518\n\ - \ protocol: TCP\n - name: \"ext-svc-4519\"\n \ - \ containerPort: 4519\n protocol: TCP\n - name: - \"ext-svc-4520\"\n containerPort: 4520\n protocol: - TCP\n - name: \"ext-svc-4521\"\n containerPort: 4521\n\ - \ protocol: TCP\n - name: \"ext-svc-4522\"\n \ - \ containerPort: 4522\n protocol: TCP\n - name: - \"ext-svc-4523\"\n containerPort: 4523\n protocol: - TCP\n - name: \"ext-svc-4524\"\n containerPort: 4524\n\ - \ protocol: TCP\n - name: \"ext-svc-4525\"\n \ - \ containerPort: 4525\n protocol: TCP\n - name: - \"ext-svc-4526\"\n containerPort: 4526\n protocol: - TCP\n - name: \"ext-svc-4527\"\n containerPort: 4527\n\ - \ protocol: TCP\n - name: \"ext-svc-4528\"\n \ - \ containerPort: 4528\n protocol: TCP\n - name: - \"ext-svc-4529\"\n containerPort: 4529\n protocol: - TCP\n - name: \"ext-svc-4530\"\n containerPort: 4530\n\ - \ protocol: TCP\n - name: \"ext-svc-4531\"\n \ - \ containerPort: 4531\n protocol: TCP\n - name: - \"ext-svc-4532\"\n containerPort: 4532\n protocol: - TCP\n - name: \"ext-svc-4533\"\n containerPort: 4533\n\ - \ protocol: TCP\n - name: \"ext-svc-4534\"\n \ - \ containerPort: 4534\n protocol: TCP\n - name: - \"ext-svc-4535\"\n containerPort: 4535\n protocol: - TCP\n - name: \"ext-svc-4536\"\n containerPort: 4536\n\ - \ protocol: TCP\n - name: \"ext-svc-4537\"\n \ - \ containerPort: 4537\n protocol: TCP\n - name: - \"ext-svc-4538\"\n containerPort: 4538\n protocol: - TCP\n - name: \"ext-svc-4539\"\n containerPort: 4539\n\ - \ protocol: TCP\n - name: \"ext-svc-4540\"\n \ - \ containerPort: 4540\n protocol: TCP\n - name: - \"ext-svc-4541\"\n containerPort: 4541\n protocol: - TCP\n - name: \"ext-svc-4542\"\n containerPort: 4542\n\ - \ protocol: TCP\n - name: \"ext-svc-4543\"\n \ - \ containerPort: 4543\n protocol: TCP\n - name: - \"ext-svc-4544\"\n containerPort: 4544\n protocol: - TCP\n - name: \"ext-svc-4545\"\n containerPort: 4545\n\ - \ protocol: TCP\n - name: \"ext-svc-4546\"\n \ - \ containerPort: 4546\n protocol: TCP\n - name: - \"ext-svc-4547\"\n containerPort: 4547\n protocol: - TCP\n - name: \"ext-svc-4548\"\n containerPort: 4548\n\ - \ protocol: TCP\n - name: \"ext-svc-4549\"\n \ - \ containerPort: 4549\n protocol: TCP\n - name: - \"ext-svc-4550\"\n containerPort: 4550\n protocol: - TCP\n - name: \"ext-svc-4551\"\n containerPort: 4551\n\ - \ protocol: TCP\n - name: \"ext-svc-4552\"\n \ - \ containerPort: 4552\n protocol: TCP\n - name: - \"ext-svc-4553\"\n containerPort: 4553\n protocol: - TCP\n - name: \"ext-svc-4554\"\n containerPort: 4554\n\ - \ protocol: TCP\n - name: \"ext-svc-4555\"\n \ - \ containerPort: 4555\n protocol: TCP\n - name: - \"ext-svc-4556\"\n containerPort: 4556\n protocol: - TCP\n - name: \"ext-svc-4557\"\n containerPort: 4557\n\ - \ protocol: TCP\n - name: \"ext-svc-4558\"\n \ - \ containerPort: 4558\n protocol: TCP\n - name: - \"ext-svc-4559\"\n containerPort: 4559\n protocol: - TCP\n livenessProbe:\n failureThreshold: 3\n \ - \ initialDelaySeconds: 0\n periodSeconds: 10\n successThreshold: - 1\n timeoutSeconds: 1\n httpGet:\n path: - /_localstack/health\n port: edge\n readinessProbe:\n \ - \ failureThreshold: 3\n initialDelaySeconds: 0\n \ - \ periodSeconds: 10\n successThreshold: 1\n timeoutSeconds: - 1\n httpGet:\n path: /_localstack/health\n \ - \ port: edge\n resources: {}\n env:\n - name: - DEBUG\n value: \"0\"\n - name: EXTERNAL_SERVICE_PORTS_START\n\ - \ value: \"4510\"\n - name: EXTERNAL_SERVICE_PORTS_END\n\ - \ value: \"4560\"\n - name: LOCALSTACK_K8S_SERVICE_NAME\n\ - \ value: localstack\n - name: LOCALSTACK_K8S_NAMESPACE\n\ - \ valueFrom:\n fieldRef:\n fieldPath: - metadata.namespace\n - name: LAMBDA_RUNTIME_EXECUTOR\n \ - \ value: \"docker\"\n - name: LAMBDA_K8S_IMAGE_PREFIX\n \ - \ value: \"localstack/lambda-\"\n - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT\n\ - \ value: \"60\"\n - name: OVERRIDE_IN_DOCKER\n \ - \ value: \"1\"\n volumes: []\n" - key: manifests - name: manifests - persist: false - node_selector: - k8s.scaleway.com/pool-name: processing-node-pool - pod_env_vars: - CODE_SERVER_WS: /workspace/quickwin - CONDARC: /workspace/.condarc - CONDA_ENVS_PATH: /workspace/.envs - HOME: /workspace - XDG_CONFIG_HOME: /workspace - XDG_DATA_HOME: /workspace/.local/share/ - XDG_RUNTIME_DIR: /workspace/.local - role_bindings: - - name: log-reader-role-binding - persist: false - role: - api_groups: - - '' - name: log-reader-role - resources: - - pods - - pods/log - verbs: - - get - - list - - watch - subjects: - - kind: ServiceAccount - name: default - - name: pod-reader-role-binding - persist: false - role: - api_groups: - - '' - name: pod-manager-role - resources: - - pods - verbs: - - create - - patch - - delete - - list - - watch - subjects: - - kind: ServiceAccount - name: default - volumes: - - access_modes: - - ReadWriteOnce - claim_name: workspace-claim - name: workspace-volume - persist: false - size: 10Gi - storage_class: scw-bssd - volume_mount: - mount_path: /workspace - name: workspace-volume -- config_maps: - - content: "set -x \n\ncd /workspace\n\ngit clone 'https://github.com/eoap/mastering-app-package.git'\n - \ncode-server --install-extension ms-python.python \ncode-server --install-extension - redhat.vscode-yaml\ncode-server --install-extension sbg-rabix.benten-cwl\ncode-server - --install-extension ms-toolsai.jupyter\n\nln -s /workspace/.local/share/code-server/extensions - /workspace/extensions\n\nmkdir -p /workspace/User/\n\necho '{\"workbench.colorTheme\"\ - : \"Visual Studio Dark\"}' > /workspace/User/settings.json\n\npython -m venv - /workspace/.venv\nsource /workspace/.venv/bin/activate\n/workspace/.venv/bin/python - -m pip install --no-cache-dir rasterio click pystac loguru pyproj shapely scikit-image - pystac rio_stac ipykernel stactools[validate]\n/workspace/.venv/bin/python -m - ipykernel install --user --name quickwin_env --display-name \"Python (Quickwin)\"\ - \n\nexit 0" - default_mode: null - key: init - mount_path: /opt/init/.init.sh - name: init - persist: false - readonly: true - - content: "'k8s.scaleway.com/pool-name': 'processing-node-pool'" - default_mode: null - key: node-selector - mount_path: /etc/calrissian/pod-node-selector.yaml - name: node-selector - persist: false - readonly: true - - content: "[plugins]\n endpoint = awscli_plugin_endpoint\n[default]\nregion - = nl-ams\ns3 =\n endpoint_url = https://s3.nl-ams.scw.cloud\n signature_version - = s3v4\n max_concurrent_requests = 100\n max_queue_size = 1000\n multipart_threshold - = 50MB\n # Edit the multipart_chunksize value according to the file sizes - that you want to upload. The present configuration allows to upload files up - to 10 GB (1000 requests * 10MB). For example setting it to 5GB allows you to - upload files up to 5TB.\n multipart_chunksize = 10MB\n addressing_style - = virtual\ns3api =\n endpoint_url = https://s3.nl-ams.scw.cloud" - default_mode: null - key: aws-config - mount_path: /workspace/.aws/config - name: aws-config - persist: true - readonly: true - - content: "[default]\n aws_access_key_id=SCW3KE38CBAS0KV481PZ\n aws_secret_access_key=9092aaa8-13e7-4696-a777-8bd158738d84\n" - default_mode: null - key: aws-credentials - mount_path: /workspace/.aws/credentials - name: aws-credentials - persist: true - readonly: true - - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors - /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram - 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ - \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc - \n# >>> conda initialize >>>\n# !! Contents within this block are managed by - 'conda init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' - 2> /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n\ - \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ - \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset - __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ - \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n\n - export PATH=/workspace/.venv/bin:$PATH" - default_mode: null - key: bash-rc - mount_path: /workspace/.bashrc - name: bash-rc - persist: false - readonly: true - - content: source /workspace/.bashrc - default_mode: null - key: bash-login - mount_path: /workspace/.bash_login - name: bash-login - persist: false - readonly: true - default_url: null - definition: - default: false - description: Dive into the world of EO Application Packages and explore how to - effectively package, share, and execute Earth observation workflows using the - Common Workflow Language (CWL) standard. - display_name: Mastering Earth Observation Application Packaging with CWL - kubespawner_override: - cpu_guarantee: 1 - cpu_limit: 2 - extra_resource_guarantees: {} - extra_resource_limits: {} - image: - cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 - mem_guarantee: 4G - mem_limit: 6G - slug: ellip_studio_coder_slug_mastering - groups: - - bids23-coder - - bids23-fair - - mastering - id: profile_studio_coder_mastering - image_pull_secrets: - - data: - eyJhdXRocyI6eyJjci50ZXJyYWR1ZS5jb20iOnsidXNlcm5hbWUiOiJyb2JvdCRlbGxpcC1zdHVkaW8iLCJwYXNzd29yZCI6IjIzRlpXSm5PeEtad05RVGVYeWNzb1VnNmxVbTZIeXhTIiwiZW1haWwiOiJlbGxpcC1zdHVkaW9AdGVycmFkdWUuY29tIiwiYXV0aCI6ImNtOWliM1FrWld4c2FYQXRjM1IxWkdsdk9qSXpSbHBYU201UGVFdGFkMDVSVkdWWWVXTnpiMVZuTm14VmJUWkllWGhUIn19fQ== - name: cr-config - persist: false - init_containers: - - command: - - sh - - -c - - sh /opt/init/.init.sh - image: - cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 - name: init-file-on-volume - volume_mounts: - - mount_path: /workspace - name: workspace-volume - - mount_path: /opt/init/.init.sh - name: init - sub_path: init - manifests: - - content: "---\n# Source: localstack/templates/serviceaccount.yaml\napiVersion: - v1\nkind: ServiceAccount\nmetadata:\n name: localstack\n# ---\n# # Source: - localstack/templates/role.yaml\n# apiVersion: rbac.authorization.k8s.io/v1\n - # kind: Role\n# metadata:\n# name: localstack\n# rules:\n# - apiGroups: [\"\ - \"] # \"\" indicates the core API group\n# resources: [\"pods\"]\n# verbs: - [\"*\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/log\"]\n# verbs: [\"\ - get\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/exec\"]\n# verbs: [\"\ - get\", \"create\"]\n# - apiGroups: [\"\"]\n# resources: [\"services\"]\n#\ - \ verbs: [\"get\", \"list\"]\n# ---\n# # Source: localstack/templates/rolebinding.yaml\n - # apiVersion: rbac.authorization.k8s.io/v1\n# kind: RoleBinding\n# metadata:\n\ - # name: localstack\n# subjects:\n# # You can specify more than one \"subject\"\ - \n# - kind: ServiceAccount\n# name: localstack\n# roleRef:\n# kind: Role\n\ - # name: localstack\n# apiGroup: rbac.authorization.k8s.io\n---\n# Source: - localstack/templates/service.yaml\napiVersion: v1\nkind: Service\nmetadata:\n\ - \ name: localstack\n \n \nspec:\n type: ClusterIP\n ports:\n - name: - edge\n port: 4566\n targetPort: 4566\n - name: \"external-service-port-4510\"\ - \n port: 4510\n targetPort: \"ext-svc-4510\"\n - name: \"external-service-port-4511\"\ - \n port: 4511\n targetPort: \"ext-svc-4511\"\n - name: \"external-service-port-4512\"\ - \n port: 4512\n targetPort: \"ext-svc-4512\"\n - name: \"external-service-port-4513\"\ - \n port: 4513\n targetPort: \"ext-svc-4513\"\n - name: \"external-service-port-4514\"\ - \n port: 4514\n targetPort: \"ext-svc-4514\"\n - name: \"external-service-port-4515\"\ - \n port: 4515\n targetPort: \"ext-svc-4515\"\n - name: \"external-service-port-4516\"\ - \n port: 4516\n targetPort: \"ext-svc-4516\"\n - name: \"external-service-port-4517\"\ - \n port: 4517\n targetPort: \"ext-svc-4517\"\n - name: \"external-service-port-4518\"\ - \n port: 4518\n targetPort: \"ext-svc-4518\"\n - name: \"external-service-port-4519\"\ - \n port: 4519\n targetPort: \"ext-svc-4519\"\n - name: \"external-service-port-4520\"\ - \n port: 4520\n targetPort: \"ext-svc-4520\"\n - name: \"external-service-port-4521\"\ - \n port: 4521\n targetPort: \"ext-svc-4521\"\n - name: \"external-service-port-4522\"\ - \n port: 4522\n targetPort: \"ext-svc-4522\"\n - name: \"external-service-port-4523\"\ - \n port: 4523\n targetPort: \"ext-svc-4523\"\n - name: \"external-service-port-4524\"\ - \n port: 4524\n targetPort: \"ext-svc-4524\"\n - name: \"external-service-port-4525\"\ - \n port: 4525\n targetPort: \"ext-svc-4525\"\n - name: \"external-service-port-4526\"\ - \n port: 4526\n targetPort: \"ext-svc-4526\"\n - name: \"external-service-port-4527\"\ - \n port: 4527\n targetPort: \"ext-svc-4527\"\n - name: \"external-service-port-4528\"\ - \n port: 4528\n targetPort: \"ext-svc-4528\"\n - name: \"external-service-port-4529\"\ - \n port: 4529\n targetPort: \"ext-svc-4529\"\n - name: \"external-service-port-4530\"\ - \n port: 4530\n targetPort: \"ext-svc-4530\"\n - name: \"external-service-port-4531\"\ - \n port: 4531\n targetPort: \"ext-svc-4531\"\n - name: \"external-service-port-4532\"\ - \n port: 4532\n targetPort: \"ext-svc-4532\"\n - name: \"external-service-port-4533\"\ - \n port: 4533\n targetPort: \"ext-svc-4533\"\n - name: \"external-service-port-4534\"\ - \n port: 4534\n targetPort: \"ext-svc-4534\"\n - name: \"external-service-port-4535\"\ - \n port: 4535\n targetPort: \"ext-svc-4535\"\n - name: \"external-service-port-4536\"\ - \n port: 4536\n targetPort: \"ext-svc-4536\"\n - name: \"external-service-port-4537\"\ - \n port: 4537\n targetPort: \"ext-svc-4537\"\n - name: \"external-service-port-4538\"\ - \n port: 4538\n targetPort: \"ext-svc-4538\"\n - name: \"external-service-port-4539\"\ - \n port: 4539\n targetPort: \"ext-svc-4539\"\n - name: \"external-service-port-4540\"\ - \n port: 4540\n targetPort: \"ext-svc-4540\"\n - name: \"external-service-port-4541\"\ - \n port: 4541\n targetPort: \"ext-svc-4541\"\n - name: \"external-service-port-4542\"\ - \n port: 4542\n targetPort: \"ext-svc-4542\"\n - name: \"external-service-port-4543\"\ - \n port: 4543\n targetPort: \"ext-svc-4543\"\n - name: \"external-service-port-4544\"\ - \n port: 4544\n targetPort: \"ext-svc-4544\"\n - name: \"external-service-port-4545\"\ - \n port: 4545\n targetPort: \"ext-svc-4545\"\n - name: \"external-service-port-4546\"\ - \n port: 4546\n targetPort: \"ext-svc-4546\"\n - name: \"external-service-port-4547\"\ - \n port: 4547\n targetPort: \"ext-svc-4547\"\n - name: \"external-service-port-4548\"\ - \n port: 4548\n targetPort: \"ext-svc-4548\"\n - name: \"external-service-port-4549\"\ - \n port: 4549\n targetPort: \"ext-svc-4549\"\n - name: \"external-service-port-4550\"\ - \n port: 4550\n targetPort: \"ext-svc-4550\"\n - name: \"external-service-port-4551\"\ - \n port: 4551\n targetPort: \"ext-svc-4551\"\n - name: \"external-service-port-4552\"\ - \n port: 4552\n targetPort: \"ext-svc-4552\"\n - name: \"external-service-port-4553\"\ - \n port: 4553\n targetPort: \"ext-svc-4553\"\n - name: \"external-service-port-4554\"\ - \n port: 4554\n targetPort: \"ext-svc-4554\"\n - name: \"external-service-port-4555\"\ - \n port: 4555\n targetPort: \"ext-svc-4555\"\n - name: \"external-service-port-4556\"\ - \n port: 4556\n targetPort: \"ext-svc-4556\"\n - name: \"external-service-port-4557\"\ - \n port: 4557\n targetPort: \"ext-svc-4557\"\n - name: \"external-service-port-4558\"\ - \n port: 4558\n targetPort: \"ext-svc-4558\"\n - name: \"external-service-port-4559\"\ - \n port: 4559\n targetPort: \"ext-svc-4559\"\n selector:\n app.kubernetes.io/name: - localstack\n app.kubernetes.io/instance: localstack\n---\n# Source: localstack/templates/deployment.yaml\n - apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: localstack\n \n\ - spec:\n replicas: 1\n strategy:\n type: RollingUpdate\n selector:\n \ - \ matchLabels:\n app.kubernetes.io/name: localstack\n app.kubernetes.io/instance: - localstack\n template:\n metadata:\n labels:\n app.kubernetes.io/name: - localstack\n app.kubernetes.io/instance: localstack\n spec:\n \ - \ serviceAccountName: localstack\n securityContext:\n {}\n \ - \ containers:\n - name: localstack\n securityContext:\n \ - \ {}\n image: \"localstack/localstack:latest\"\n imagePullPolicy: - IfNotPresent\n ports:\n - name: edge\n containerPort: - 4566\n protocol: TCP\n - name: \"ext-svc-4510\"\n \ - \ containerPort: 4510\n protocol: TCP\n - - name: \"ext-svc-4511\"\n containerPort: 4511\n protocol: - TCP\n - name: \"ext-svc-4512\"\n containerPort: 4512\n\ - \ protocol: TCP\n - name: \"ext-svc-4513\"\n \ - \ containerPort: 4513\n protocol: TCP\n - name: - \"ext-svc-4514\"\n containerPort: 4514\n protocol: - TCP\n - name: \"ext-svc-4515\"\n containerPort: 4515\n\ - \ protocol: TCP\n - name: \"ext-svc-4516\"\n \ - \ containerPort: 4516\n protocol: TCP\n - name: - \"ext-svc-4517\"\n containerPort: 4517\n protocol: - TCP\n - name: \"ext-svc-4518\"\n containerPort: 4518\n\ - \ protocol: TCP\n - name: \"ext-svc-4519\"\n \ - \ containerPort: 4519\n protocol: TCP\n - name: - \"ext-svc-4520\"\n containerPort: 4520\n protocol: - TCP\n - name: \"ext-svc-4521\"\n containerPort: 4521\n\ - \ protocol: TCP\n - name: \"ext-svc-4522\"\n \ - \ containerPort: 4522\n protocol: TCP\n - name: - \"ext-svc-4523\"\n containerPort: 4523\n protocol: - TCP\n - name: \"ext-svc-4524\"\n containerPort: 4524\n\ - \ protocol: TCP\n - name: \"ext-svc-4525\"\n \ - \ containerPort: 4525\n protocol: TCP\n - name: - \"ext-svc-4526\"\n containerPort: 4526\n protocol: - TCP\n - name: \"ext-svc-4527\"\n containerPort: 4527\n\ - \ protocol: TCP\n - name: \"ext-svc-4528\"\n \ - \ containerPort: 4528\n protocol: TCP\n - name: - \"ext-svc-4529\"\n containerPort: 4529\n protocol: - TCP\n - name: \"ext-svc-4530\"\n containerPort: 4530\n\ - \ protocol: TCP\n - name: \"ext-svc-4531\"\n \ - \ containerPort: 4531\n protocol: TCP\n - name: - \"ext-svc-4532\"\n containerPort: 4532\n protocol: - TCP\n - name: \"ext-svc-4533\"\n containerPort: 4533\n\ - \ protocol: TCP\n - name: \"ext-svc-4534\"\n \ - \ containerPort: 4534\n protocol: TCP\n - name: - \"ext-svc-4535\"\n containerPort: 4535\n protocol: - TCP\n - name: \"ext-svc-4536\"\n containerPort: 4536\n\ - \ protocol: TCP\n - name: \"ext-svc-4537\"\n \ - \ containerPort: 4537\n protocol: TCP\n - name: - \"ext-svc-4538\"\n containerPort: 4538\n protocol: - TCP\n - name: \"ext-svc-4539\"\n containerPort: 4539\n\ - \ protocol: TCP\n - name: \"ext-svc-4540\"\n \ - \ containerPort: 4540\n protocol: TCP\n - name: - \"ext-svc-4541\"\n containerPort: 4541\n protocol: - TCP\n - name: \"ext-svc-4542\"\n containerPort: 4542\n\ - \ protocol: TCP\n - name: \"ext-svc-4543\"\n \ - \ containerPort: 4543\n protocol: TCP\n - name: - \"ext-svc-4544\"\n containerPort: 4544\n protocol: - TCP\n - name: \"ext-svc-4545\"\n containerPort: 4545\n\ - \ protocol: TCP\n - name: \"ext-svc-4546\"\n \ - \ containerPort: 4546\n protocol: TCP\n - name: - \"ext-svc-4547\"\n containerPort: 4547\n protocol: - TCP\n - name: \"ext-svc-4548\"\n containerPort: 4548\n\ - \ protocol: TCP\n - name: \"ext-svc-4549\"\n \ - \ containerPort: 4549\n protocol: TCP\n - name: - \"ext-svc-4550\"\n containerPort: 4550\n protocol: - TCP\n - name: \"ext-svc-4551\"\n containerPort: 4551\n\ - \ protocol: TCP\n - name: \"ext-svc-4552\"\n \ - \ containerPort: 4552\n protocol: TCP\n - name: - \"ext-svc-4553\"\n containerPort: 4553\n protocol: - TCP\n - name: \"ext-svc-4554\"\n containerPort: 4554\n\ - \ protocol: TCP\n - name: \"ext-svc-4555\"\n \ - \ containerPort: 4555\n protocol: TCP\n - name: - \"ext-svc-4556\"\n containerPort: 4556\n protocol: - TCP\n - name: \"ext-svc-4557\"\n containerPort: 4557\n\ - \ protocol: TCP\n - name: \"ext-svc-4558\"\n \ - \ containerPort: 4558\n protocol: TCP\n - name: - \"ext-svc-4559\"\n containerPort: 4559\n protocol: - TCP\n livenessProbe:\n failureThreshold: 3\n \ - \ initialDelaySeconds: 0\n periodSeconds: 10\n successThreshold: - 1\n timeoutSeconds: 1\n httpGet:\n path: - /_localstack/health\n port: edge\n readinessProbe:\n \ - \ failureThreshold: 3\n initialDelaySeconds: 0\n \ - \ periodSeconds: 10\n successThreshold: 1\n timeoutSeconds: - 1\n httpGet:\n path: /_localstack/health\n \ - \ port: edge\n resources: {}\n env:\n - name: - DEBUG\n value: \"0\"\n - name: EXTERNAL_SERVICE_PORTS_START\n\ - \ value: \"4510\"\n - name: EXTERNAL_SERVICE_PORTS_END\n\ - \ value: \"4560\"\n - name: LOCALSTACK_K8S_SERVICE_NAME\n\ - \ value: localstack\n - name: LOCALSTACK_K8S_NAMESPACE\n\ - \ valueFrom:\n fieldRef:\n fieldPath: - metadata.namespace\n - name: LAMBDA_RUNTIME_EXECUTOR\n \ - \ value: \"docker\"\n - name: LAMBDA_K8S_IMAGE_PREFIX\n \ - \ value: \"localstack/lambda-\"\n - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT\n\ - \ value: \"60\"\n - name: OVERRIDE_IN_DOCKER\n \ - \ value: \"1\"\n volumes: []\n" - key: manifests - name: manifests - persist: false - node_selector: - k8s.scaleway.com/pool-name: processing-node-pool - pod_env_vars: - CODE_SERVER_WS: /workspace/mastering-app-package - CONDARC: /workspace/.condarc - CONDA_ENVS_PATH: /workspace/.envs - CWLTOOL_OPTIONS: --podman - HOME: /workspace - XDG_CONFIG_HOME: /workspace/.local - XDG_DATA_HOME: /workspace/.local/share/ - XDG_RUNTIME_DIR: /workspace/.local - role_bindings: - - name: log-reader-role-binding - persist: false - role: - api_groups: - - '' - name: log-reader-role - resources: - - pods - - pods/log - verbs: - - get - - list - - watch - subjects: - - kind: ServiceAccount - name: default - - name: pod-reader-role-binding - persist: false - role: - api_groups: - - '' - name: pod-manager-role - resources: - - pods - verbs: - - create - - patch - - delete - - list - - watch - subjects: - - kind: ServiceAccount - name: default - - name: job-submitter-role-binding - persist: false - role: - api_groups: - - batch - name: job-submitter-role - resources: - - jobs - verbs: - - create - - delete - - list - - watch - - get - subjects: - - kind: ServiceAccount - name: default - volumes: - - access_modes: - - ReadWriteMany - claim_name: calrissian-claim - name: calrissian-volume - persist: false - size: 50Gi - storage_class: openebs-kernel-nfs-scw - volume_mount: - mount_path: /calrissian - name: calrissian-volume - - access_modes: - - ReadWriteOnce - claim_name: workspace-claim - name: workspace-volume - persist: false - size: 10Gi - storage_class: scw-bssd - volume_mount: - mount_path: /workspace - name: workspace-volume -- config_maps: - - content: "set -x \n\ncd /workspace\n\ngit clone 'https://github.com/eoap/open-reproducible-app-package.git'\n - \ncode-server --install-extension ms-python.python \ncode-server --install-extension - ms-toolsai.jupyter\n\nln -s /workspace/.local/share/code-server/extensions /workspace/extensions\n - \nmamba create -y -p /workspace/.envs/e-learn 'python==3.10' pip\n\n/workspace/.envs/e-learn/bin/pip3 - install ipykernel stac-asset pystac pystac-client ipyleaflet pydantic-yaml cwltool - pydantic black[jupyter] rasterio cwl-utils\n\nexit 0" - default_mode: null - key: init - mount_path: /opt/init/.init.sh - name: init - persist: false - readonly: true - - content: "auto_update_conda: false\nshow_channel_urls: true\nchannels:\n - conda-forge\n\ - \ - terradue" - default_mode: null - key: conda-rc - mount_path: /workspace/.condarc - name: conda-rc - persist: false - readonly: true - - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors - /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram - 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ - \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc - \n# >>> conda initialize >>>\n# !! Contents within this block are managed by - 'conda init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' - 2> /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n\ - \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ - \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset - __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ - \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n\n - export PATH=/workspace/.venv/bin:$PATH" - default_mode: null - key: bash-rc - mount_path: /workspace/.bashrc - name: bash-rc - persist: false - readonly: true - - content: source /workspace/.bashrc - default_mode: null - key: bash-login - mount_path: /workspace/.bash_login - name: bash-login - persist: false - readonly: true - default_url: null - definition: - default: false - description: Learn how Bob reproduces Alice's Application Package (image pull)) - display_name: Open Reproducible EO Application Package - kubespawner_override: - cpu_guarantee: 1 - cpu_limit: 2 - extra_resource_guarantees: {} - extra_resource_limits: {} - image: - cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 - mem_guarantee: 4G - mem_limit: 6G - slug: ellip_studio_open_reproducible_app_package - groups: - - bids23-coder - - bids23-fair - - reproducible - id: profile_studio_coder_reproducible - image_pull_secrets: - - data: - eyJhdXRocyI6eyJjci50ZXJyYWR1ZS5jb20iOnsidXNlcm5hbWUiOiJyb2JvdCRlbGxpcC1zdHVkaW8iLCJwYXNzd29yZCI6IjIzRlpXSm5PeEtad05RVGVYeWNzb1VnNmxVbTZIeXhTIiwiZW1haWwiOiJlbGxpcC1zdHVkaW9AdGVycmFkdWUuY29tIiwiYXV0aCI6ImNtOWliM1FrWld4c2FYQXRjM1IxWkdsdk9qSXpSbHBYU201UGVFdGFkMDVSVkdWWWVXTnpiMVZuTm14VmJUWkllWGhUIn19fQ== - name: cr-config - persist: false - - data: - ewoJImF1dGhzIjogewoJCSJjci50ZXJyYWR1ZS5jb20iOiB7CgkJCSJhdXRoIjogIlptSnlhWFJ2T21ZNVZFNUNaVTlIVEE9PSIKCQl9Cgl9Cn0= - name: empty-cr-secret - persist: false - init_containers: - - command: - - sh - - -c - - sh /opt/init/.init.sh - image: - cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 - name: init-file-on-volume - volume_mounts: - - mount_path: /workspace - name: workspace-volume - - mount_path: /opt/init/.init.sh - name: init - sub_path: init - manifests: - - content: "---\n# Source: localstack/templates/serviceaccount.yaml\napiVersion: - v1\nkind: ServiceAccount\nmetadata:\n name: localstack\n# ---\n# # Source: - localstack/templates/role.yaml\n# apiVersion: rbac.authorization.k8s.io/v1\n - # kind: Role\n# metadata:\n# name: localstack\n# rules:\n# - apiGroups: [\"\ - \"] # \"\" indicates the core API group\n# resources: [\"pods\"]\n# verbs: - [\"*\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/log\"]\n# verbs: [\"\ - get\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/exec\"]\n# verbs: [\"\ - get\", \"create\"]\n# - apiGroups: [\"\"]\n# resources: [\"services\"]\n#\ - \ verbs: [\"get\", \"list\"]\n# ---\n# # Source: localstack/templates/rolebinding.yaml\n - # apiVersion: rbac.authorization.k8s.io/v1\n# kind: RoleBinding\n# metadata:\n\ - # name: localstack\n# subjects:\n# # You can specify more than one \"subject\"\ - \n# - kind: ServiceAccount\n# name: localstack\n# roleRef:\n# kind: Role\n\ - # name: localstack\n# apiGroup: rbac.authorization.k8s.io\n---\n# Source: - localstack/templates/service.yaml\napiVersion: v1\nkind: Service\nmetadata:\n\ - \ name: localstack\n \n \nspec:\n type: ClusterIP\n ports:\n - name: - edge\n port: 4566\n targetPort: 4566\n - name: \"external-service-port-4510\"\ - \n port: 4510\n targetPort: \"ext-svc-4510\"\n - name: \"external-service-port-4511\"\ - \n port: 4511\n targetPort: \"ext-svc-4511\"\n - name: \"external-service-port-4512\"\ - \n port: 4512\n targetPort: \"ext-svc-4512\"\n - name: \"external-service-port-4513\"\ - \n port: 4513\n targetPort: \"ext-svc-4513\"\n - name: \"external-service-port-4514\"\ - \n port: 4514\n targetPort: \"ext-svc-4514\"\n - name: \"external-service-port-4515\"\ - \n port: 4515\n targetPort: \"ext-svc-4515\"\n - name: \"external-service-port-4516\"\ - \n port: 4516\n targetPort: \"ext-svc-4516\"\n - name: \"external-service-port-4517\"\ - \n port: 4517\n targetPort: \"ext-svc-4517\"\n - name: \"external-service-port-4518\"\ - \n port: 4518\n targetPort: \"ext-svc-4518\"\n - name: \"external-service-port-4519\"\ - \n port: 4519\n targetPort: \"ext-svc-4519\"\n - name: \"external-service-port-4520\"\ - \n port: 4520\n targetPort: \"ext-svc-4520\"\n - name: \"external-service-port-4521\"\ - \n port: 4521\n targetPort: \"ext-svc-4521\"\n - name: \"external-service-port-4522\"\ - \n port: 4522\n targetPort: \"ext-svc-4522\"\n - name: \"external-service-port-4523\"\ - \n port: 4523\n targetPort: \"ext-svc-4523\"\n - name: \"external-service-port-4524\"\ - \n port: 4524\n targetPort: \"ext-svc-4524\"\n - name: \"external-service-port-4525\"\ - \n port: 4525\n targetPort: \"ext-svc-4525\"\n - name: \"external-service-port-4526\"\ - \n port: 4526\n targetPort: \"ext-svc-4526\"\n - name: \"external-service-port-4527\"\ - \n port: 4527\n targetPort: \"ext-svc-4527\"\n - name: \"external-service-port-4528\"\ - \n port: 4528\n targetPort: \"ext-svc-4528\"\n - name: \"external-service-port-4529\"\ - \n port: 4529\n targetPort: \"ext-svc-4529\"\n - name: \"external-service-port-4530\"\ - \n port: 4530\n targetPort: \"ext-svc-4530\"\n - name: \"external-service-port-4531\"\ - \n port: 4531\n targetPort: \"ext-svc-4531\"\n - name: \"external-service-port-4532\"\ - \n port: 4532\n targetPort: \"ext-svc-4532\"\n - name: \"external-service-port-4533\"\ - \n port: 4533\n targetPort: \"ext-svc-4533\"\n - name: \"external-service-port-4534\"\ - \n port: 4534\n targetPort: \"ext-svc-4534\"\n - name: \"external-service-port-4535\"\ - \n port: 4535\n targetPort: \"ext-svc-4535\"\n - name: \"external-service-port-4536\"\ - \n port: 4536\n targetPort: \"ext-svc-4536\"\n - name: \"external-service-port-4537\"\ - \n port: 4537\n targetPort: \"ext-svc-4537\"\n - name: \"external-service-port-4538\"\ - \n port: 4538\n targetPort: \"ext-svc-4538\"\n - name: \"external-service-port-4539\"\ - \n port: 4539\n targetPort: \"ext-svc-4539\"\n - name: \"external-service-port-4540\"\ - \n port: 4540\n targetPort: \"ext-svc-4540\"\n - name: \"external-service-port-4541\"\ - \n port: 4541\n targetPort: \"ext-svc-4541\"\n - name: \"external-service-port-4542\"\ - \n port: 4542\n targetPort: \"ext-svc-4542\"\n - name: \"external-service-port-4543\"\ - \n port: 4543\n targetPort: \"ext-svc-4543\"\n - name: \"external-service-port-4544\"\ - \n port: 4544\n targetPort: \"ext-svc-4544\"\n - name: \"external-service-port-4545\"\ - \n port: 4545\n targetPort: \"ext-svc-4545\"\n - name: \"external-service-port-4546\"\ - \n port: 4546\n targetPort: \"ext-svc-4546\"\n - name: \"external-service-port-4547\"\ - \n port: 4547\n targetPort: \"ext-svc-4547\"\n - name: \"external-service-port-4548\"\ - \n port: 4548\n targetPort: \"ext-svc-4548\"\n - name: \"external-service-port-4549\"\ - \n port: 4549\n targetPort: \"ext-svc-4549\"\n - name: \"external-service-port-4550\"\ - \n port: 4550\n targetPort: \"ext-svc-4550\"\n - name: \"external-service-port-4551\"\ - \n port: 4551\n targetPort: \"ext-svc-4551\"\n - name: \"external-service-port-4552\"\ - \n port: 4552\n targetPort: \"ext-svc-4552\"\n - name: \"external-service-port-4553\"\ - \n port: 4553\n targetPort: \"ext-svc-4553\"\n - name: \"external-service-port-4554\"\ - \n port: 4554\n targetPort: \"ext-svc-4554\"\n - name: \"external-service-port-4555\"\ - \n port: 4555\n targetPort: \"ext-svc-4555\"\n - name: \"external-service-port-4556\"\ - \n port: 4556\n targetPort: \"ext-svc-4556\"\n - name: \"external-service-port-4557\"\ - \n port: 4557\n targetPort: \"ext-svc-4557\"\n - name: \"external-service-port-4558\"\ - \n port: 4558\n targetPort: \"ext-svc-4558\"\n - name: \"external-service-port-4559\"\ - \n port: 4559\n targetPort: \"ext-svc-4559\"\n selector:\n app.kubernetes.io/name: - localstack\n app.kubernetes.io/instance: localstack\n---\n# Source: localstack/templates/deployment.yaml\n - apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: localstack\n \n\ - spec:\n replicas: 1\n strategy:\n type: RollingUpdate\n selector:\n \ - \ matchLabels:\n app.kubernetes.io/name: localstack\n app.kubernetes.io/instance: - localstack\n template:\n metadata:\n labels:\n app.kubernetes.io/name: - localstack\n app.kubernetes.io/instance: localstack\n spec:\n \ - \ serviceAccountName: localstack\n securityContext:\n {}\n \ - \ containers:\n - name: localstack\n securityContext:\n \ - \ {}\n image: \"localstack/localstack:latest\"\n imagePullPolicy: - IfNotPresent\n ports:\n - name: edge\n containerPort: - 4566\n protocol: TCP\n - name: \"ext-svc-4510\"\n \ - \ containerPort: 4510\n protocol: TCP\n - - name: \"ext-svc-4511\"\n containerPort: 4511\n protocol: - TCP\n - name: \"ext-svc-4512\"\n containerPort: 4512\n\ - \ protocol: TCP\n - name: \"ext-svc-4513\"\n \ - \ containerPort: 4513\n protocol: TCP\n - name: - \"ext-svc-4514\"\n containerPort: 4514\n protocol: - TCP\n - name: \"ext-svc-4515\"\n containerPort: 4515\n\ - \ protocol: TCP\n - name: \"ext-svc-4516\"\n \ - \ containerPort: 4516\n protocol: TCP\n - name: - \"ext-svc-4517\"\n containerPort: 4517\n protocol: - TCP\n - name: \"ext-svc-4518\"\n containerPort: 4518\n\ - \ protocol: TCP\n - name: \"ext-svc-4519\"\n \ - \ containerPort: 4519\n protocol: TCP\n - name: - \"ext-svc-4520\"\n containerPort: 4520\n protocol: - TCP\n - name: \"ext-svc-4521\"\n containerPort: 4521\n\ - \ protocol: TCP\n - name: \"ext-svc-4522\"\n \ - \ containerPort: 4522\n protocol: TCP\n - name: - \"ext-svc-4523\"\n containerPort: 4523\n protocol: - TCP\n - name: \"ext-svc-4524\"\n containerPort: 4524\n\ - \ protocol: TCP\n - name: \"ext-svc-4525\"\n \ - \ containerPort: 4525\n protocol: TCP\n - name: - \"ext-svc-4526\"\n containerPort: 4526\n protocol: - TCP\n - name: \"ext-svc-4527\"\n containerPort: 4527\n\ - \ protocol: TCP\n - name: \"ext-svc-4528\"\n \ - \ containerPort: 4528\n protocol: TCP\n - name: - \"ext-svc-4529\"\n containerPort: 4529\n protocol: - TCP\n - name: \"ext-svc-4530\"\n containerPort: 4530\n\ - \ protocol: TCP\n - name: \"ext-svc-4531\"\n \ - \ containerPort: 4531\n protocol: TCP\n - name: - \"ext-svc-4532\"\n containerPort: 4532\n protocol: - TCP\n - name: \"ext-svc-4533\"\n containerPort: 4533\n\ - \ protocol: TCP\n - name: \"ext-svc-4534\"\n \ - \ containerPort: 4534\n protocol: TCP\n - name: - \"ext-svc-4535\"\n containerPort: 4535\n protocol: - TCP\n - name: \"ext-svc-4536\"\n containerPort: 4536\n\ - \ protocol: TCP\n - name: \"ext-svc-4537\"\n \ - \ containerPort: 4537\n protocol: TCP\n - name: - \"ext-svc-4538\"\n containerPort: 4538\n protocol: - TCP\n - name: \"ext-svc-4539\"\n containerPort: 4539\n\ - \ protocol: TCP\n - name: \"ext-svc-4540\"\n \ - \ containerPort: 4540\n protocol: TCP\n - name: - \"ext-svc-4541\"\n containerPort: 4541\n protocol: - TCP\n - name: \"ext-svc-4542\"\n containerPort: 4542\n\ - \ protocol: TCP\n - name: \"ext-svc-4543\"\n \ - \ containerPort: 4543\n protocol: TCP\n - name: - \"ext-svc-4544\"\n containerPort: 4544\n protocol: - TCP\n - name: \"ext-svc-4545\"\n containerPort: 4545\n\ - \ protocol: TCP\n - name: \"ext-svc-4546\"\n \ - \ containerPort: 4546\n protocol: TCP\n - name: - \"ext-svc-4547\"\n containerPort: 4547\n protocol: - TCP\n - name: \"ext-svc-4548\"\n containerPort: 4548\n\ - \ protocol: TCP\n - name: \"ext-svc-4549\"\n \ - \ containerPort: 4549\n protocol: TCP\n - name: - \"ext-svc-4550\"\n containerPort: 4550\n protocol: - TCP\n - name: \"ext-svc-4551\"\n containerPort: 4551\n\ - \ protocol: TCP\n - name: \"ext-svc-4552\"\n \ - \ containerPort: 4552\n protocol: TCP\n - name: - \"ext-svc-4553\"\n containerPort: 4553\n protocol: - TCP\n - name: \"ext-svc-4554\"\n containerPort: 4554\n\ - \ protocol: TCP\n - name: \"ext-svc-4555\"\n \ - \ containerPort: 4555\n protocol: TCP\n - name: - \"ext-svc-4556\"\n containerPort: 4556\n protocol: - TCP\n - name: \"ext-svc-4557\"\n containerPort: 4557\n\ - \ protocol: TCP\n - name: \"ext-svc-4558\"\n \ - \ containerPort: 4558\n protocol: TCP\n - name: - \"ext-svc-4559\"\n containerPort: 4559\n protocol: - TCP\n livenessProbe:\n failureThreshold: 3\n \ - \ initialDelaySeconds: 0\n periodSeconds: 10\n successThreshold: - 1\n timeoutSeconds: 1\n httpGet:\n path: - /_localstack/health\n port: edge\n readinessProbe:\n \ - \ failureThreshold: 3\n initialDelaySeconds: 0\n \ - \ periodSeconds: 10\n successThreshold: 1\n timeoutSeconds: - 1\n httpGet:\n path: /_localstack/health\n \ - \ port: edge\n resources: {}\n env:\n - name: - DEBUG\n value: \"0\"\n - name: EXTERNAL_SERVICE_PORTS_START\n\ - \ value: \"4510\"\n - name: EXTERNAL_SERVICE_PORTS_END\n\ - \ value: \"4560\"\n - name: LOCALSTACK_K8S_SERVICE_NAME\n\ - \ value: localstack\n - name: LOCALSTACK_K8S_NAMESPACE\n\ - \ valueFrom:\n fieldRef:\n fieldPath: - metadata.namespace\n - name: LAMBDA_RUNTIME_EXECUTOR\n \ - \ value: \"docker\"\n - name: LAMBDA_K8S_IMAGE_PREFIX\n \ - \ value: \"localstack/lambda-\"\n - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT\n\ - \ value: \"60\"\n - name: OVERRIDE_IN_DOCKER\n \ - \ value: \"1\"\n volumes: []\n" - key: manifests - name: manifests - persist: false - node_selector: - k8s.scaleway.com/pool-name: processing-node-pool - pod_env_vars: - CODE_SERVER_WS: /workspace/open-reproducible-app-package - CONDARC: /workspace/.condarc - CONDA_ENVS_PATH: /workspace/.envs - HOME: /workspace - XDG_CONFIG_HOME: /workspace/.local - XDG_DATA_HOME: /workspace/.local/share/ - XDG_RUNTIME_DIR: /workspace/.local - role_bindings: - - name: log-reader-role-binding - persist: false - role: - api_groups: - - '' - name: log-reader-role - resources: - - pods - - pods/log - verbs: - - get - - list - - watch - subjects: - - kind: ServiceAccount - name: default - - name: pod-reader-role-binding - persist: false - role: - api_groups: - - '' - name: pod-manager-role - resources: - - pods - verbs: - - create - - patch - - delete - - list - - watch - subjects: - - kind: ServiceAccount - name: default - - name: secret-patcher-role-binding - persist: false - role: - api_groups: - - '' - name: secret-patcher-role - resources: - - secrets - verbs: - - create - - delete - subjects: - - kind: ServiceAccount - name: default - volumes: - - access_modes: - - ReadWriteOnce - claim_name: workspace-claim - name: workspace-volume - persist: false - size: 10Gi - storage_class: scw-bssd - volume_mount: - mount_path: /workspace - name: workspace-volume -- config_maps: - - content: "set -x \n\ncd /workspace\n\ngit clone 'https://github.com/eoap/inference-eoap.git'\n - \ncode-server --install-extension ms-python.python \ncode-server --install-extension - redhat.vscode-yaml\ncode-server --install-extension sbg-rabix.benten-cwl\ncode-server - --install-extension ms-toolsai.jupyter\n\npip install -r /workspace/inference-eoap/requirements.txt\n - pip install stactools[validate]\n\nln -s /workspace/.local/share/code-server/extensions - /workspace/extensions\n\nmkdir -p /workspace/User/\n\necho '{\"workbench.colorTheme\"\ - : \"Visual Studio Dark\"}' > /workspace/User/settings.json\n\nexit 0" - default_mode: null - key: init - mount_path: /opt/init/.init.sh - name: init - persist: false - readonly: true - - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors - /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram - 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ - \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc - \n# >>> conda initialize >>>\n# !! Contents within this block are managed by - 'conda init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' - 2> /dev/null)\"\nif [ $? -eq 0 ]; then\n eval \"$__conda_setup\"\nelse\n\ - \ if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\ - \n else\n export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset - __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n \ - \ . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n\n - export PATH=\"/workspace/inference-eoap/bin:$PATH\"" - default_mode: null - key: bash-rc - mount_path: /workspace/.bashrc - name: bash-rc - persist: false - readonly: true - - content: source /workspace/.bashrc - default_mode: null - key: bash-login - mount_path: /workspace/.bash_login - name: bash-login - persist: false - readonly: true - default_url: null - definition: - default: false - description: This tutorial is designed for developers, scientists, and Earth observation - enthusiasts who want to get started with the EO Application Package for Machine - Learning inference. - display_name: Inference with Earth Observation Application Package - kubespawner_override: - cpu_guarantee: 3 - cpu_limit: 3 - extra_resource_guarantees: {} - extra_resource_limits: {} - image: - cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 - mem_guarantee: 14G - mem_limit: 14G - slug: ellip_studio_coder_slug_inference - groups: - - ai-inference - id: profile_studio_coder_inference - image_pull_secrets: - - data: - eyJhdXRocyI6eyJjci50ZXJyYWR1ZS5jb20iOnsidXNlcm5hbWUiOiJyb2JvdCRlbGxpcC1zdHVkaW8iLCJwYXNzd29yZCI6IjIzRlpXSm5PeEtad05RVGVYeWNzb1VnNmxVbTZIeXhTIiwiZW1haWwiOiJlbGxpcC1zdHVkaW9AdGVycmFkdWUuY29tIiwiYXV0aCI6ImNtOWliM1FrWld4c2FYQXRjM1IxWkdsdk9qSXpSbHBYU201UGVFdGFkMDVSVkdWWWVXTnpiMVZuTm14VmJUWkllWGhUIn19fQ== - name: cr-config - persist: false - init_containers: - - command: - - sh - - -c - - sh /opt/init/.init.sh - image: - cr.terradue.com/ellip-studio/studio-code-server@sha256:80f88e84cbdfec51fc4c005c933691d035f1180586abb229521320f7d2bea768 - name: init-file-on-volume - volume_mounts: - - mount_path: /workspace - name: workspace-volume - - mount_path: /opt/init/.init.sh - name: init - sub_path: init - manifests: - - content: "---\n# Source: localstack/templates/serviceaccount.yaml\napiVersion: - v1\nkind: ServiceAccount\nmetadata:\n name: localstack\n# ---\n# # Source: - localstack/templates/role.yaml\n# apiVersion: rbac.authorization.k8s.io/v1\n - # kind: Role\n# metadata:\n# name: localstack\n# rules:\n# - apiGroups: [\"\ - \"] # \"\" indicates the core API group\n# resources: [\"pods\"]\n# verbs: - [\"*\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/log\"]\n# verbs: [\"\ - get\"]\n# - apiGroups: [\"\"]\n# resources: [\"pods/exec\"]\n# verbs: [\"\ - get\", \"create\"]\n# - apiGroups: [\"\"]\n# resources: [\"services\"]\n#\ - \ verbs: [\"get\", \"list\"]\n# ---\n# # Source: localstack/templates/rolebinding.yaml\n - # apiVersion: rbac.authorization.k8s.io/v1\n# kind: RoleBinding\n# metadata:\n\ - # name: localstack\n# subjects:\n# # You can specify more than one \"subject\"\ - \n# - kind: ServiceAccount\n# name: localstack\n# roleRef:\n# kind: Role\n\ - # name: localstack\n# apiGroup: rbac.authorization.k8s.io\n---\n# Source: - localstack/templates/service.yaml\napiVersion: v1\nkind: Service\nmetadata:\n\ - \ name: localstack\n \n \nspec:\n type: ClusterIP\n ports:\n - name: - edge\n port: 4566\n targetPort: 4566\n - name: \"external-service-port-4510\"\ - \n port: 4510\n targetPort: \"ext-svc-4510\"\n - name: \"external-service-port-4511\"\ - \n port: 4511\n targetPort: \"ext-svc-4511\"\n - name: \"external-service-port-4512\"\ - \n port: 4512\n targetPort: \"ext-svc-4512\"\n - name: \"external-service-port-4513\"\ - \n port: 4513\n targetPort: \"ext-svc-4513\"\n - name: \"external-service-port-4514\"\ - \n port: 4514\n targetPort: \"ext-svc-4514\"\n - name: \"external-service-port-4515\"\ - \n port: 4515\n targetPort: \"ext-svc-4515\"\n - name: \"external-service-port-4516\"\ - \n port: 4516\n targetPort: \"ext-svc-4516\"\n - name: \"external-service-port-4517\"\ - \n port: 4517\n targetPort: \"ext-svc-4517\"\n - name: \"external-service-port-4518\"\ - \n port: 4518\n targetPort: \"ext-svc-4518\"\n - name: \"external-service-port-4519\"\ - \n port: 4519\n targetPort: \"ext-svc-4519\"\n - name: \"external-service-port-4520\"\ - \n port: 4520\n targetPort: \"ext-svc-4520\"\n - name: \"external-service-port-4521\"\ - \n port: 4521\n targetPort: \"ext-svc-4521\"\n - name: \"external-service-port-4522\"\ - \n port: 4522\n targetPort: \"ext-svc-4522\"\n - name: \"external-service-port-4523\"\ - \n port: 4523\n targetPort: \"ext-svc-4523\"\n - name: \"external-service-port-4524\"\ - \n port: 4524\n targetPort: \"ext-svc-4524\"\n - name: \"external-service-port-4525\"\ - \n port: 4525\n targetPort: \"ext-svc-4525\"\n - name: \"external-service-port-4526\"\ - \n port: 4526\n targetPort: \"ext-svc-4526\"\n - name: \"external-service-port-4527\"\ - \n port: 4527\n targetPort: \"ext-svc-4527\"\n - name: \"external-service-port-4528\"\ - \n port: 4528\n targetPort: \"ext-svc-4528\"\n - name: \"external-service-port-4529\"\ - \n port: 4529\n targetPort: \"ext-svc-4529\"\n - name: \"external-service-port-4530\"\ - \n port: 4530\n targetPort: \"ext-svc-4530\"\n - name: \"external-service-port-4531\"\ - \n port: 4531\n targetPort: \"ext-svc-4531\"\n - name: \"external-service-port-4532\"\ - \n port: 4532\n targetPort: \"ext-svc-4532\"\n - name: \"external-service-port-4533\"\ - \n port: 4533\n targetPort: \"ext-svc-4533\"\n - name: \"external-service-port-4534\"\ - \n port: 4534\n targetPort: \"ext-svc-4534\"\n - name: \"external-service-port-4535\"\ - \n port: 4535\n targetPort: \"ext-svc-4535\"\n - name: \"external-service-port-4536\"\ - \n port: 4536\n targetPort: \"ext-svc-4536\"\n - name: \"external-service-port-4537\"\ - \n port: 4537\n targetPort: \"ext-svc-4537\"\n - name: \"external-service-port-4538\"\ - \n port: 4538\n targetPort: \"ext-svc-4538\"\n - name: \"external-service-port-4539\"\ - \n port: 4539\n targetPort: \"ext-svc-4539\"\n - name: \"external-service-port-4540\"\ - \n port: 4540\n targetPort: \"ext-svc-4540\"\n - name: \"external-service-port-4541\"\ - \n port: 4541\n targetPort: \"ext-svc-4541\"\n - name: \"external-service-port-4542\"\ - \n port: 4542\n targetPort: \"ext-svc-4542\"\n - name: \"external-service-port-4543\"\ - \n port: 4543\n targetPort: \"ext-svc-4543\"\n - name: \"external-service-port-4544\"\ - \n port: 4544\n targetPort: \"ext-svc-4544\"\n - name: \"external-service-port-4545\"\ - \n port: 4545\n targetPort: \"ext-svc-4545\"\n - name: \"external-service-port-4546\"\ - \n port: 4546\n targetPort: \"ext-svc-4546\"\n - name: \"external-service-port-4547\"\ - \n port: 4547\n targetPort: \"ext-svc-4547\"\n - name: \"external-service-port-4548\"\ - \n port: 4548\n targetPort: \"ext-svc-4548\"\n - name: \"external-service-port-4549\"\ - \n port: 4549\n targetPort: \"ext-svc-4549\"\n - name: \"external-service-port-4550\"\ - \n port: 4550\n targetPort: \"ext-svc-4550\"\n - name: \"external-service-port-4551\"\ - \n port: 4551\n targetPort: \"ext-svc-4551\"\n - name: \"external-service-port-4552\"\ - \n port: 4552\n targetPort: \"ext-svc-4552\"\n - name: \"external-service-port-4553\"\ - \n port: 4553\n targetPort: \"ext-svc-4553\"\n - name: \"external-service-port-4554\"\ - \n port: 4554\n targetPort: \"ext-svc-4554\"\n - name: \"external-service-port-4555\"\ - \n port: 4555\n targetPort: \"ext-svc-4555\"\n - name: \"external-service-port-4556\"\ - \n port: 4556\n targetPort: \"ext-svc-4556\"\n - name: \"external-service-port-4557\"\ - \n port: 4557\n targetPort: \"ext-svc-4557\"\n - name: \"external-service-port-4558\"\ - \n port: 4558\n targetPort: \"ext-svc-4558\"\n - name: \"external-service-port-4559\"\ - \n port: 4559\n targetPort: \"ext-svc-4559\"\n selector:\n app.kubernetes.io/name: - localstack\n app.kubernetes.io/instance: localstack\n---\n# Source: localstack/templates/deployment.yaml\n - apiVersion: apps/v1\nkind: Deployment\nmetadata:\n name: localstack\n \n\ - spec:\n replicas: 1\n strategy:\n type: RollingUpdate\n selector:\n \ - \ matchLabels:\n app.kubernetes.io/name: localstack\n app.kubernetes.io/instance: - localstack\n template:\n metadata:\n labels:\n app.kubernetes.io/name: - localstack\n app.kubernetes.io/instance: localstack\n spec:\n \ - \ serviceAccountName: localstack\n securityContext:\n {}\n \ - \ containers:\n - name: localstack\n securityContext:\n \ - \ {}\n image: \"localstack/localstack:latest\"\n imagePullPolicy: - IfNotPresent\n ports:\n - name: edge\n containerPort: - 4566\n protocol: TCP\n - name: \"ext-svc-4510\"\n \ - \ containerPort: 4510\n protocol: TCP\n - - name: \"ext-svc-4511\"\n containerPort: 4511\n protocol: - TCP\n - name: \"ext-svc-4512\"\n containerPort: 4512\n\ - \ protocol: TCP\n - name: \"ext-svc-4513\"\n \ - \ containerPort: 4513\n protocol: TCP\n - name: - \"ext-svc-4514\"\n containerPort: 4514\n protocol: - TCP\n - name: \"ext-svc-4515\"\n containerPort: 4515\n\ - \ protocol: TCP\n - name: \"ext-svc-4516\"\n \ - \ containerPort: 4516\n protocol: TCP\n - name: - \"ext-svc-4517\"\n containerPort: 4517\n protocol: - TCP\n - name: \"ext-svc-4518\"\n containerPort: 4518\n\ - \ protocol: TCP\n - name: \"ext-svc-4519\"\n \ - \ containerPort: 4519\n protocol: TCP\n - name: - \"ext-svc-4520\"\n containerPort: 4520\n protocol: - TCP\n - name: \"ext-svc-4521\"\n containerPort: 4521\n\ - \ protocol: TCP\n - name: \"ext-svc-4522\"\n \ - \ containerPort: 4522\n protocol: TCP\n - name: - \"ext-svc-4523\"\n containerPort: 4523\n protocol: - TCP\n - name: \"ext-svc-4524\"\n containerPort: 4524\n\ - \ protocol: TCP\n - name: \"ext-svc-4525\"\n \ - \ containerPort: 4525\n protocol: TCP\n - name: - \"ext-svc-4526\"\n containerPort: 4526\n protocol: - TCP\n - name: \"ext-svc-4527\"\n containerPort: 4527\n\ - \ protocol: TCP\n - name: \"ext-svc-4528\"\n \ - \ containerPort: 4528\n protocol: TCP\n - name: - \"ext-svc-4529\"\n containerPort: 4529\n protocol: - TCP\n - name: \"ext-svc-4530\"\n containerPort: 4530\n\ - \ protocol: TCP\n - name: \"ext-svc-4531\"\n \ - \ containerPort: 4531\n protocol: TCP\n - name: - \"ext-svc-4532\"\n containerPort: 4532\n protocol: - TCP\n - name: \"ext-svc-4533\"\n containerPort: 4533\n\ - \ protocol: TCP\n - name: \"ext-svc-4534\"\n \ - \ containerPort: 4534\n protocol: TCP\n - name: - \"ext-svc-4535\"\n containerPort: 4535\n protocol: - TCP\n - name: \"ext-svc-4536\"\n containerPort: 4536\n\ - \ protocol: TCP\n - name: \"ext-svc-4537\"\n \ - \ containerPort: 4537\n protocol: TCP\n - name: - \"ext-svc-4538\"\n containerPort: 4538\n protocol: - TCP\n - name: \"ext-svc-4539\"\n containerPort: 4539\n\ - \ protocol: TCP\n - name: \"ext-svc-4540\"\n \ - \ containerPort: 4540\n protocol: TCP\n - name: - \"ext-svc-4541\"\n containerPort: 4541\n protocol: - TCP\n - name: \"ext-svc-4542\"\n containerPort: 4542\n\ - \ protocol: TCP\n - name: \"ext-svc-4543\"\n \ - \ containerPort: 4543\n protocol: TCP\n - name: - \"ext-svc-4544\"\n containerPort: 4544\n protocol: - TCP\n - name: \"ext-svc-4545\"\n containerPort: 4545\n\ - \ protocol: TCP\n - name: \"ext-svc-4546\"\n \ - \ containerPort: 4546\n protocol: TCP\n - name: - \"ext-svc-4547\"\n containerPort: 4547\n protocol: - TCP\n - name: \"ext-svc-4548\"\n containerPort: 4548\n\ - \ protocol: TCP\n - name: \"ext-svc-4549\"\n \ - \ containerPort: 4549\n protocol: TCP\n - name: - \"ext-svc-4550\"\n containerPort: 4550\n protocol: - TCP\n - name: \"ext-svc-4551\"\n containerPort: 4551\n\ - \ protocol: TCP\n - name: \"ext-svc-4552\"\n \ - \ containerPort: 4552\n protocol: TCP\n - name: - \"ext-svc-4553\"\n containerPort: 4553\n protocol: - TCP\n - name: \"ext-svc-4554\"\n containerPort: 4554\n\ - \ protocol: TCP\n - name: \"ext-svc-4555\"\n \ - \ containerPort: 4555\n protocol: TCP\n - name: - \"ext-svc-4556\"\n containerPort: 4556\n protocol: - TCP\n - name: \"ext-svc-4557\"\n containerPort: 4557\n\ - \ protocol: TCP\n - name: \"ext-svc-4558\"\n \ - \ containerPort: 4558\n protocol: TCP\n - name: - \"ext-svc-4559\"\n containerPort: 4559\n protocol: - TCP\n livenessProbe:\n failureThreshold: 3\n \ - \ initialDelaySeconds: 0\n periodSeconds: 10\n successThreshold: - 1\n timeoutSeconds: 1\n httpGet:\n path: - /_localstack/health\n port: edge\n readinessProbe:\n \ - \ failureThreshold: 3\n initialDelaySeconds: 0\n \ - \ periodSeconds: 10\n successThreshold: 1\n timeoutSeconds: - 1\n httpGet:\n path: /_localstack/health\n \ - \ port: edge\n resources: {}\n env:\n - name: - DEBUG\n value: \"0\"\n - name: EXTERNAL_SERVICE_PORTS_START\n\ - \ value: \"4510\"\n - name: EXTERNAL_SERVICE_PORTS_END\n\ - \ value: \"4560\"\n - name: LOCALSTACK_K8S_SERVICE_NAME\n\ - \ value: localstack\n - name: LOCALSTACK_K8S_NAMESPACE\n\ - \ valueFrom:\n fieldRef:\n fieldPath: - metadata.namespace\n - name: LAMBDA_RUNTIME_EXECUTOR\n \ - \ value: \"docker\"\n - name: LAMBDA_K8S_IMAGE_PREFIX\n \ - \ value: \"localstack/lambda-\"\n - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT\n\ - \ value: \"60\"\n - name: OVERRIDE_IN_DOCKER\n \ - \ value: \"1\"\n volumes: []\n" - key: manifests - name: manifests - persist: false - node_selector: - k8s.scaleway.com/pool-name: processing-node-pool-inference - pod_env_vars: - CODE_SERVER_WS: /workspace/inference-eoap - CONDARC: /workspace/.condarc - CONDA_ENVS_PATH: /workspace/.envs - CWLTOOL_OPTIONS: --podman - HOME: /workspace - XDG_CONFIG_HOME: /workspace/.local - XDG_DATA_HOME: /workspace/.local/share/ - XDG_RUNTIME_DIR: /workspace/.local - role_bindings: - - name: log-reader-role-binding - persist: false - role: - api_groups: - - '' - name: log-reader-role - resources: - - pods - - pods/log - verbs: - - get - - list - - watch - subjects: - - kind: ServiceAccount - name: default - - name: pod-reader-role-binding - persist: false - role: - api_groups: - - '' - name: pod-manager-role - resources: - - pods - verbs: - - create - - patch - - delete - - list - - watch - subjects: - - kind: ServiceAccount - name: default - volumes: - - access_modes: - - ReadWriteMany - claim_name: calrissian-claim - name: calrissian-volume - persist: false - size: 50Gi - storage_class: openebs-kernel-nfs-scw - volume_mount: - mount_path: /calrissian - name: calrissian-volume - - access_modes: - - ReadWriteOnce - claim_name: workspace-claim - name: workspace-volume - persist: false - size: 20Gi - storage_class: scw-bssd - volume_mount: - mount_path: /workspace - name: workspace-volume - diff --git a/jupyterhub/files/hub/jupyterhub_config.py b/jupyterhub/files/hub/jupyterhub_config.py deleted file mode 100644 index 10d0f0d..0000000 --- a/jupyterhub/files/hub/jupyterhub_config.py +++ /dev/null @@ -1,188 +0,0 @@ -import os -import sys - -from tornado.httpclient import AsyncHTTPClient - - -from application_hub_context.app_hub_context import DefaultApplicationHubContext - - -configuration_directory = os.path.dirname(os.path.realpath(__file__)) -sys.path.insert(0, configuration_directory) - -from z2jh import ( - get_config, - get_name, - get_name_env, -) - -config_path = "/usr/local/etc/jupyterhub/config.yml" - -def get_namespace_prefix(): - env = os.environ["JUPYTERHUB_ENV"].lower() # Retrieve the JUPYTERHUB_ENV environment variable - return f"jupyter-{env}" # Dynamically generate the namespace prefix - -def get_jupyterhub_hub_host(): - env = os.environ["JUPYTERHUB_HUB_HOST"].lower() - return f"hub.{env}" - -namespace_prefix = get_namespace_prefix() # Create dynamic namespace prefix - -def custom_options_form(spawner): - - spawner.log.info("Configure profile list") - - namespace = f"{namespace_prefix}-{spawner.user.name}" - - workspace = DefaultApplicationHubContext( - namespace=namespace, - spawner=spawner, - config_path=config_path, - ) - - spawner.profile_list = workspace.get_profile_list() - - return spawner._options_form_default() - - -def pre_spawn_hook(spawner): - - profile_slug = spawner.user_options.get("profile", None) - - env = os.environ["JUPYTERHUB_ENV"].lower() - - spawner.environment["CALRISSIAN_POD_NAME"] = f"jupyter-{env}-{spawner.user.name}" - - spawner.log.info(f"Using profile slug {profile_slug}") - - namespace = f"{namespace_prefix}-{spawner.user.name}" - - workspace = DefaultApplicationHubContext( - namespace=namespace, spawner=spawner, config_path=config_path, skip_namespace_check=True - ) - - workspace.initialise() - - profile_id = workspace.config_parser.get_profile_by_slug(slug=profile_slug).id - - default_url = workspace.config_parser.get_profile_default_url(profile_id=profile_id) - - if default_url: - spawner.log.info(f"Setting default url to {default_url}") - spawner.default_url = default_url - - -def post_stop_hook(spawner): - - namespace = f"{namespace_prefix}-{spawner.user.name}" - - workspace = DefaultApplicationHubContext( - namespace=namespace, spawner=spawner, config_path=config_path - ) - spawner.log.info("Dispose in post stop hook") - workspace.dispose() - - -c.JupyterHub.default_url = "spawn" - - -# Configure JupyterHub to use the curl backend for making HTTP requests, -# rather than the pure-python implementations. The default one starts -# being too slow to make a large number of requests to the proxy API -# at the rate required. -AsyncHTTPClient.configure("tornado.curl_httpclient.CurlAsyncHTTPClient") - -c.ConfigurableHTTPProxy.api_url = ( - f'http://{get_name("proxy-api")}:{get_name_env("proxy-api", "_SERVICE_PORT")}' -) -# c.ConfigurableHTTPProxy.should_start = False - -# Don't wait at all before redirecting a spawning user to the progress page -c.JupyterHub.tornado_settings = { - "slow_spawn_timeout": 0, -} - -jupyterhub_env = os.environ["JUPYTERHUB_ENV"].upper() -jupyterhub_hub_host = get_jupyterhub_hub_host() - -jupyterhub_single_user_image = os.environ["JUPYTERHUB_SINGLE_USER_IMAGE_NOTEBOOKS"] - -# Authentication -c.LocalAuthenticator.create_system_users = True -c.Authenticator.admin_users = {"jovyan"} -# Deprecated -c.Authenticator.allowed_users = {"jovyan"} -c.JupyterHub.authenticator_class = "dummy" - -# HTTP Proxy auth token -c.ConfigurableHTTPProxy.auth_token = get_config("proxy.secretToken") -c.JupyterHub.cookie_secret_file = "/srv/jupyterhub/cookie_secret" -# Proxy config -c.JupyterHub.cleanup_servers = False -# Network -c.JupyterHub.allow_named_servers = True -c.JupyterHub.ip = "0.0.0.0" -c.JupyterHub.hub_ip = "0.0.0.0" -c.JupyterHub.hub_connect_ip = jupyterhub_hub_host -# Misc -c.JupyterHub.cleanup_servers = False - -# Culling -c.JupyterHub.services = [ - { - "name": "idle-culler", - "admin": True, - "command": [sys.executable, "-m", "jupyterhub_idle_culler", "--timeout=3600"], - } -] - -# Logs -c.JupyterHub.log_level = "DEBUG" - -# Spawner -c.JupyterHub.spawner_class = "kubespawner.KubeSpawner" -c.KubeSpawner.environment = { - "JUPYTER_ENABLE_LAB": "true", -} - -c.KubeSpawner.uid = 1001 -c.KubeSpawner.fs_gid = 100 -c.KubeSpawner.hub_connect_ip = jupyterhub_hub_host - -# SecurityContext -c.KubeSpawner.privileged = True -c.KubeSpawner.allow_privilege_escalation = True - -# ServiceAccount -c.KubeSpawner.service_account = "default" -c.KubeSpawner.start_timeout = 60 * 15 -c.KubeSpawner.image = jupyterhub_single_user_image -c.KubernetesSpawner.verify_ssl = True -c.KubeSpawner.pod_name_template = ( - "jupyter-" + os.environ["JUPYTERHUB_ENV"].lower() + "-{username}" -) - -# Namespace -# Kubernetes namespace to spawn user pods in. -c.KubeSpawner.user_namespace_template = get_namespace_prefix() + "-{username}" - -# User namespace -c.KubeSpawner.enable_user_namespaces = True - -# Volumes -# volumes are managed by the pre_spawn_hook/post_stop_hook - -# TODO - move this value to the values.yaml file -c.KubeSpawner.image_pull_secrets = ["cr-config"] - -# custom options form -c.KubeSpawner.options_form = custom_options_form - -# hooks -c.KubeSpawner.pre_spawn_hook = pre_spawn_hook -c.KubeSpawner.post_stop_hook = post_stop_hook - -c.JupyterHub.template_paths = [ - "/opt/jupyterhub/template", - "/usr/local/share/jupyterhub/templates", -] diff --git a/jupyterhub/files/hub/z2jh.py b/jupyterhub/files/hub/z2jh.py deleted file mode 100644 index 57e463f..0000000 --- a/jupyterhub/files/hub/z2jh.py +++ /dev/null @@ -1,121 +0,0 @@ -""" -Utility methods for use in jupyterhub_config.py and dynamic subconfigs. - -Methods here can be imported by extraConfig in values.yaml -""" -# from collections import Mapping -from collections.abc import Mapping -from functools import lru_cache -import os - -import yaml - -# memoize so we only load config once -@lru_cache() -def _load_config(): - """Load the Helm chart configuration used to render the Helm templates of - the chart from a mounted k8s Secret, and merge in values from an optionally - mounted secret (hub.existingSecret).""" - - cfg = {} - for source in ("secret/values.yaml", "existing-secret/values.yaml"): - path = f"/usr/local/etc/jupyterhub/{source}" - if os.path.exists(path): - print(f"Loading {path}") - with open(path) as f: - values = yaml.safe_load(f) - cfg = _merge_dictionaries(cfg, values) - else: - print(f"No config at {path}") - return cfg - - -@lru_cache() -def _get_config_value(key): - """Load value from the k8s ConfigMap given a key.""" - - path = f"/usr/local/etc/jupyterhub/config/{key}" - if os.path.exists(path): - with open(path) as f: - return f.read() - else: - raise Exception(f"{path} not found!") - - -@lru_cache() -def get_secret_value(key, default="never-explicitly-set"): - """Load value from the user managed k8s Secret or the default k8s Secret - given a key.""" - - for source in ("existing-secret", "secret"): - path = f"/usr/local/etc/jupyterhub/{source}/{key}" - if os.path.exists(path): - with open(path) as f: - return f.read() - if default != "never-explicitly-set": - return default - raise Exception(f"{key} not found in either k8s Secret!") - - -def get_name(name): - """Returns the fullname of a resource given its short name""" - return _get_config_value(name) - - -def get_name_env(name, suffix=""): - """Returns the fullname of a resource given its short name along with a - suffix, converted to uppercase with dashes replaced with underscores. This - is useful to reference named services associated environment variables, such - as PROXY_PUBLIC_SERVICE_PORT.""" - env_key = _get_config_value(name) + suffix - env_key = env_key.upper().replace("-", "_") - return os.environ[env_key] - - -def _merge_dictionaries(a, b): - """Merge two dictionaries recursively. - - Simplified From https://stackoverflow.com/a/7205107 - """ - merged = a.copy() - for key in b: - if key in a: - if isinstance(a[key], Mapping) and isinstance(b[key], Mapping): - merged[key] = _merge_dictionaries(a[key], b[key]) - else: - merged[key] = b[key] - else: - merged[key] = b[key] - return merged - - -def get_config(key, default=None): - """ - Find a config item of a given name & return it - - Parses everything as YAML, so lists and dicts are available too - - get_config("a.b.c") returns config['a']['b']['c'] - """ - value = _load_config() - # resolve path in yaml - for level in key.split("."): - if not isinstance(value, dict): - # a parent is a scalar or null, - # can't resolve full path - return default - if level not in value: - return default - else: - value = value[level] - return value - - -def set_config_if_not_none(cparent, name, key): - """ - Find a config item of a given name, set the corresponding Jupyter - configuration item if not None - """ - data = get_config(key) - if data is not None: - setattr(cparent, name, data) diff --git a/jupyterhub/files/theme/page.html b/jupyterhub/files/theme/page.html deleted file mode 100644 index 6ee147a..0000000 --- a/jupyterhub/files/theme/page.html +++ /dev/null @@ -1,5 +0,0 @@ -{% extends "templates/page.html" %} - -{% set announcement = 'EOEPCA+ ApplicationHub demonstration instance' %} - -{% block title %}ApplicationHub{% endblock %} diff --git a/jupyterhub/files/theme/spawn.html b/jupyterhub/files/theme/spawn.html deleted file mode 100644 index 639636b..0000000 --- a/jupyterhub/files/theme/spawn.html +++ /dev/null @@ -1,7 +0,0 @@ -{% extends "templates/spawn.html" %} - -{% block heading %} -
-

Available demonstration applications

-
-{% endblock %} diff --git a/jupyterhub/files/theme/spawn_pending.html b/jupyterhub/files/theme/spawn_pending.html deleted file mode 100644 index f2b2e7c..0000000 --- a/jupyterhub/files/theme/spawn_pending.html +++ /dev/null @@ -1,94 +0,0 @@ -{% extends "page.html" %} - -{% block main %} - -
-
-
- {% block message %} -

Your pod is starting up.

-

You will be redirected automatically when it's ready for you.

- {% endblock %} -
-
- 0% Complete -
-
-

-
-
-
-
-
- Event log -
-
-
-
-
- -{% endblock %} - -{% block script %} -{{ super() }} - -{% endblock %} diff --git a/jupyterhub/requirements.txt b/jupyterhub/requirements.txt deleted file mode 100644 index e69de29..0000000 diff --git a/jupyterhub/templates/NOTES.txt b/jupyterhub/templates/NOTES.txt deleted file mode 100644 index e9a4edf..0000000 --- a/jupyterhub/templates/NOTES.txt +++ /dev/null @@ -1,153 +0,0 @@ -{{- $proxy_service := include "jupyterhub.proxy-public.fullname" . -}} - -{{- /* Generated with https://patorjk.com/software/taag/#p=display&h=0&f=Slant&t=JupyterHub */}} -. __ __ __ __ __ - / / __ __ ____ __ __ / /_ ___ _____ / / / / __ __ / /_ - __ / / / / / / / __ \ / / / / / __/ / _ \ / ___/ / /_/ / / / / / / __ \ -/ /_/ / / /_/ / / /_/ / / /_/ / / /_ / __/ / / / __ / / /_/ / / /_/ / -\____/ \__,_/ / .___/ \__, / \__/ \___/ /_/ /_/ /_/ \__,_/ /_.___/ - /_/ /____/ - - You have successfully installed the official JupyterHub Helm chart! - -### Installation info - - - Kubernetes namespace: {{ .Release.Namespace }} - - Helm release name: {{ .Release.Name }} - - Helm chart version: {{ .Chart.Version }} - - JupyterHub version: {{ .Chart.AppVersion }} - - Hub pod packages: See https://github.com/jupyterhub/zero-to-jupyterhub-k8s/blob/{{ include "jupyterhub.chart-version-to-git-ref" .Chart.Version }}/images/hub/requirements.txt - -### Followup links - - - Documentation: https://z2jh.jupyter.org - - Help forum: https://discourse.jupyter.org - - Social chat: https://gitter.im/jupyterhub/jupyterhub - - Issue tracking: https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues - -### Post-installation checklist - - - Verify that created Pods enter a Running state: - - kubectl --namespace={{ .Release.Namespace }} get pod - - If a pod is stuck with a Pending or ContainerCreating status, diagnose with: - - kubectl --namespace={{ .Release.Namespace }} describe pod - - If a pod keeps restarting, diagnose with: - - kubectl --namespace={{ .Release.Namespace }} logs --previous - {{- println }} - - {{- if eq .Values.proxy.service.type "LoadBalancer" }} - - Verify an external IP is provided for the k8s Service {{ $proxy_service }}. - - kubectl --namespace={{ .Release.Namespace }} get service {{ $proxy_service }} - - If the external ip remains , diagnose with: - - kubectl --namespace={{ .Release.Namespace }} describe service {{ $proxy_service }} - {{- end }} - - - Verify web based access: - {{- println }} - {{- if .Values.ingress.enabled }} - {{- range $host := .Values.ingress.hosts }} - Try insecure HTTP access: http://{{ $host }}{{ $.Values.hub.baseUrl | trimSuffix "/" }}/ - {{- end }} - - {{- range $tls := .Values.ingress.tls }} - {{- range $host := $tls.hosts }} - Try secure HTTPS access: https://{{ $host }}{{ $.Values.hub.baseUrl | trimSuffix "/" }}/ - {{- end }} - {{- end }} - {{- else }} - You have not configured a k8s Ingress resource so you need to access the k8s - Service {{ $proxy_service }} directly. - {{- println }} - - {{- if eq .Values.proxy.service.type "NodePort" }} - The k8s Service {{ $proxy_service }} is exposed via NodePorts. That means - that all the k8s cluster's nodes are exposing the k8s Service via those - ports. - - Try insecure HTTP access: http://:{{ .Values.proxy.service.nodePorts.http | default "no-http-nodeport-set"}} - Try secure HTTPS access: https://:{{ .Values.proxy.service.nodePorts.https | default "no-https-nodeport-set" }} - - {{- else }} - If your computer is outside the k8s cluster, you can port-forward traffic to - the k8s Service {{ $proxy_service }} with kubectl to access it from your - computer. - - kubectl --namespace={{ .Release.Namespace }} port-forward service/{{ $proxy_service }} 8080:http - - Try insecure HTTP access: http://localhost:8080 - {{- end }} - {{- end }} - {{- println }} - - - - - -{{- /* - Warnings for likely misconfiguration -*/}} - -{{- if and (not .Values.scheduling.podPriority.enabled) (and .Values.scheduling.userPlaceholder.enabled .Values.scheduling.userPlaceholder.replicas) }} -################################################################################# -###### WARNING: You are using user placeholders without pod priority ##### -###### enabled*, either enable pod priority or stop using the ##### -###### user placeholders** to avoid having placeholders that ##### -###### refuse to make room for a real user. ##### -###### ##### -###### *scheduling.podPriority.enabled ##### -###### **scheduling.userPlaceholder.enabled ##### -###### **scheduling.userPlaceholder.replicas ##### -################################################################################# -{{- println }} -{{- end }} - - - - - -{{- /* - Breaking changes. -*/}} - -{{- $breaking := "" }} -{{- $breaking_title := "\n" }} -{{- $breaking_title = print $breaking_title "\n#################################################################################" }} -{{- $breaking_title = print $breaking_title "\n###### BREAKING: The config values passed contained no longer accepted #####" }} -{{- $breaking_title = print $breaking_title "\n###### options. See the messages below for more details. #####" }} -{{- $breaking_title = print $breaking_title "\n###### #####" }} -{{- $breaking_title = print $breaking_title "\n###### To verify your updated config is accepted, you can use #####" }} -{{- $breaking_title = print $breaking_title "\n###### the `helm template` command. #####" }} -{{- $breaking_title = print $breaking_title "\n#################################################################################" }} - - -{{- /* - This is an example (in a helm template comment) on how to detect and - communicate with regards to a breaking chart config change. - - {{- if hasKey .Values.singleuser.cloudMetadata "enabled" }} - {{- $breaking = print $breaking "\n\nCHANGED: singleuser.cloudMetadata.enabled must as of 1.0.0 be configured using singleuser.cloudMetadata.blockWithIptables with the opposite value." }} - {{- end }} -*/}} - - -{{- if hasKey .Values.rbac "enabled" }} -{{- $breaking = print $breaking "\n\nCHANGED: rbac.enabled must as of version 2.0.0 be configured via rbac.create and .serviceAccount.create." }} -{{- end }} - - -{{- if hasKey .Values.hub "fsGid" }} -{{- $breaking = print $breaking "\n\nCHANGED: hub.fsGid must as of version 2.0.0 be configured via hub.podSecurityContext.fsGroup." }} -{{- end }} - - -{{- if $breaking }} -{{- fail (print $breaking_title $breaking "\n\n") }} -{{- end }} diff --git a/jupyterhub/templates/_helpers-names.tpl b/jupyterhub/templates/_helpers-names.tpl deleted file mode 100644 index bbb5864..0000000 --- a/jupyterhub/templates/_helpers-names.tpl +++ /dev/null @@ -1,306 +0,0 @@ -{{- /* - These helpers encapsulates logic on how we name resources. They also enable - parent charts to reference these dynamic resource names. - - To avoid duplicating documentation, for more information, please see the the - fullnameOverride entry in schema.yaml or the configuration reference that - schema.yaml renders to. - - https://z2jh.jupyter.org/en/latest/resources/reference.html#fullnameOverride -*/}} - - - -{{- /* - Utility templates -*/}} - -{{- /* - Renders to a prefix for the chart's resource names. This prefix is assumed to - make the resource name cluster unique. -*/}} -{{- define "jupyterhub.fullname" -}} - {{- /* - We have implemented a trick to allow a parent chart depending on this - chart to call these named templates. - - Caveats and notes: - - 1. While parent charts can reference these, grandparent charts can't. - 2. Parent charts must not use an alias for this chart. - 3. There is no failsafe workaround to above due to - https://github.com/helm/helm/issues/9214. - 4. .Chart is of its own type (*chart.Metadata) and needs to be casted - using "toYaml | fromYaml" in order to be able to use normal helm - template functions on it. - */}} - {{- $fullname_override := .Values.fullnameOverride }} - {{- $name_override := .Values.nameOverride }} - {{- if ne .Chart.Name "jupyterhub" }} - {{- if .Values.jupyterhub }} - {{- $fullname_override = .Values.jupyterhub.fullnameOverride }} - {{- $name_override = .Values.jupyterhub.nameOverride }} - {{- end }} - {{- end }} - - {{- if eq (typeOf $fullname_override) "string" }} - {{- $fullname_override }} - {{- else }} - {{- $name := $name_override | default .Chart.Name }} - {{- if contains $name .Release.Name }} - {{- .Release.Name }} - {{- else }} - {{- .Release.Name }}-{{ $name }} - {{- end }} - {{- end }} -{{- end }} - -{{- /* - Renders to a blank string or if the fullname template is truthy renders to it - with an appended dash. -*/}} -{{- define "jupyterhub.fullname.dash" -}} - {{- if (include "jupyterhub.fullname" .) }} - {{- include "jupyterhub.fullname" . }}- - {{- end }} -{{- end }} - - - -{{- /* - Namespaced resources -*/}} - -{{- /* hub Deployment */}} -{{- define "jupyterhub.hub.fullname" -}} - {{- include "jupyterhub.fullname.dash" . }}hub -{{- end }} - -{{- /* hub-serviceaccount ServiceAccount */}} -{{- define "jupyterhub.hub-serviceaccount.fullname" -}} - {{- if .Values.hub.serviceAccount.create }} - {{- .Values.hub.serviceAccount.name | default (include "jupyterhub.hub.fullname" .) }} - {{- else }} - {{- .Values.hub.serviceAccount.name | default "default" }} - {{- end }} -{{- end }} - -{{- /* hub-existing-secret Secret */}} -{{- define "jupyterhub.hub-existing-secret.fullname" -}} - {{- /* A hack to avoid issues from invoking this from a parent Helm chart. */}} - {{- $existing_secret := .Values.hub.existingSecret }} - {{- if ne .Chart.Name "jupyterhub" }} - {{- $existing_secret = .Values.jupyterhub.hub.existingSecret }} - {{- end }} - {{- if $existing_secret }} - {{- $existing_secret }} - {{- end }} -{{- end }} - -{{- /* hub-existing-secret-or-default Secret */}} -{{- define "jupyterhub.hub-existing-secret-or-default.fullname" -}} - {{- include "jupyterhub.hub-existing-secret.fullname" . | default (include "jupyterhub.hub.fullname" .) }} -{{- end }} - -{{- /* hub PVC */}} -{{- define "jupyterhub.hub-pvc.fullname" -}} - {{- include "jupyterhub.hub.fullname" . }}-db-dir -{{- end }} - -{{- /* proxy Deployment */}} -{{- define "jupyterhub.proxy.fullname" -}} - {{- include "jupyterhub.fullname.dash" . }}proxy -{{- end }} - -{{- /* proxy-api Service */}} -{{- define "jupyterhub.proxy-api.fullname" -}} - {{- include "jupyterhub.proxy.fullname" . }}-api -{{- end }} - -{{- /* proxy-http Service */}} -{{- define "jupyterhub.proxy-http.fullname" -}} - {{- include "jupyterhub.proxy.fullname" . }}-http -{{- end }} - -{{- /* proxy-public Service */}} -{{- define "jupyterhub.proxy-public.fullname" -}} - {{- include "jupyterhub.proxy.fullname" . }}-public -{{- end }} - -{{- /* proxy-public-tls Secret */}} -{{- define "jupyterhub.proxy-public-tls.fullname" -}} - {{- include "jupyterhub.proxy-public.fullname" . }}-tls-acme -{{- end }} - -{{- /* proxy-public-manual-tls Secret */}} -{{- define "jupyterhub.proxy-public-manual-tls.fullname" -}} - {{- include "jupyterhub.proxy-public.fullname" . }}-manual-tls -{{- end }} - -{{- /* autohttps Deployment */}} -{{- define "jupyterhub.autohttps.fullname" -}} - {{- include "jupyterhub.fullname.dash" . }}autohttps -{{- end }} - -{{- /* autohttps-serviceaccount ServiceAccount */}} -{{- define "jupyterhub.autohttps-serviceaccount.fullname" -}} - {{- if .Values.proxy.traefik.serviceAccount.create }} - {{- .Values.proxy.traefik.serviceAccount.name | default (include "jupyterhub.autohttps.fullname" .) }} - {{- else }} - {{- .Values.proxy.traefik.serviceAccount.name | default "default" }} - {{- end }} -{{- end }} - -{{- /* user-scheduler Deployment */}} -{{- define "jupyterhub.user-scheduler-deploy.fullname" -}} - {{- include "jupyterhub.fullname.dash" . }}user-scheduler -{{- end }} - -{{- /* user-scheduler-serviceaccount ServiceAccount */}} -{{- define "jupyterhub.user-scheduler-serviceaccount.fullname" -}} - {{- if .Values.scheduling.userScheduler.serviceAccount.create }} - {{- .Values.scheduling.userScheduler.serviceAccount.name | default (include "jupyterhub.user-scheduler-deploy.fullname" .) }} - {{- else }} - {{- .Values.scheduling.userScheduler.serviceAccount.name | default "default" }} - {{- end }} -{{- end }} - -{{- /* user-scheduler leader election lock resource */}} -{{- define "jupyterhub.user-scheduler-lock.fullname" -}} - {{- include "jupyterhub.user-scheduler-deploy.fullname" . }}-lock -{{- end }} - -{{- /* user-placeholder StatefulSet */}} -{{- define "jupyterhub.user-placeholder.fullname" -}} - {{- include "jupyterhub.fullname.dash" . }}user-placeholder -{{- end }} - -{{- /* image-awaiter Job */}} -{{- define "jupyterhub.hook-image-awaiter.fullname" -}} - {{- include "jupyterhub.fullname.dash" . }}hook-image-awaiter -{{- end }} - -{{- /* image-awaiter-serviceaccount ServiceAccount */}} -{{- define "jupyterhub.hook-image-awaiter-serviceaccount.fullname" -}} - {{- if .Values.prePuller.hook.serviceAccount.create }} - {{- .Values.prePuller.hook.serviceAccount.name | default (include "jupyterhub.hook-image-awaiter.fullname" .) }} - {{- else }} - {{- .Values.prePuller.hook.serviceAccount.name | default "default" }} - {{- end }} -{{- end }} - -{{- /* hook-image-puller DaemonSet */}} -{{- define "jupyterhub.hook-image-puller.fullname" -}} - {{- include "jupyterhub.fullname.dash" . }}hook-image-puller -{{- end }} - -{{- /* continuous-image-puller DaemonSet */}} -{{- define "jupyterhub.continuous-image-puller.fullname" -}} - {{- include "jupyterhub.fullname.dash" . }}continuous-image-puller -{{- end }} - -{{- /* singleuser NetworkPolicy */}} -{{- define "jupyterhub.singleuser.fullname" -}} - {{- include "jupyterhub.fullname.dash" . }}singleuser -{{- end }} - -{{- /* image-pull-secret Secret */}} -{{- define "jupyterhub.image-pull-secret.fullname" -}} - {{- include "jupyterhub.fullname.dash" . }}image-pull-secret -{{- end }} - -{{- /* Ingress */}} -{{- define "jupyterhub.ingress.fullname" -}} - {{- if (include "jupyterhub.fullname" .) }} - {{- include "jupyterhub.fullname" . }} - {{- else -}} - jupyterhub - {{- end }} -{{- end }} - - - -{{- /* - Cluster wide resources - - We enforce uniqueness of names for our cluster wide resources. We assume that - the prefix from setting fullnameOverride to null or a string will be cluster - unique. -*/}} - -{{- /* Priority */}} -{{- define "jupyterhub.priority.fullname" -}} - {{- if (include "jupyterhub.fullname" .) }} - {{- include "jupyterhub.fullname" . }} - {{- else }} - {{- .Release.Name }}-default-priority - {{- end }} -{{- end }} - -{{- /* user-placeholder Priority */}} -{{- define "jupyterhub.user-placeholder-priority.fullname" -}} - {{- if (include "jupyterhub.fullname" .) }} - {{- include "jupyterhub.user-placeholder.fullname" . }} - {{- else }} - {{- .Release.Name }}-user-placeholder-priority - {{- end }} -{{- end }} - -{{- /* image-puller Priority */}} -{{- define "jupyterhub.image-puller-priority.fullname" -}} - {{- if (include "jupyterhub.fullname" .) }} - {{- include "jupyterhub.fullname.dash" . }}image-puller - {{- else }} - {{- .Release.Name }}-image-puller-priority - {{- end }} -{{- end }} - -{{- /* user-scheduler's registered name */}} -{{- define "jupyterhub.user-scheduler.fullname" -}} - {{- if (include "jupyterhub.fullname" .) }} - {{- include "jupyterhub.user-scheduler-deploy.fullname" . }} - {{- else }} - {{- .Release.Name }}-user-scheduler - {{- end }} -{{- end }} - - - -{{- /* - A template to render all the named templates in this file for use in the - hub's ConfigMap. - - It is important we keep this in sync with the available templates. -*/}} -{{- define "jupyterhub.name-templates" -}} -fullname: {{ include "jupyterhub.fullname" . | quote }} -fullname-dash: {{ include "jupyterhub.fullname.dash" . | quote }} -hub: {{ include "jupyterhub.hub.fullname" . | quote }} -hub-serviceaccount: {{ include "jupyterhub.hub-serviceaccount.fullname" . | quote }} -hub-existing-secret: {{ include "jupyterhub.hub-existing-secret.fullname" . | quote }} -hub-existing-secret-or-default: {{ include "jupyterhub.hub-existing-secret-or-default.fullname" . | quote }} -hub-pvc: {{ include "jupyterhub.hub-pvc.fullname" . | quote }} -proxy: {{ include "jupyterhub.proxy.fullname" . | quote }} -proxy-api: {{ include "jupyterhub.proxy-api.fullname" . | quote }} -proxy-http: {{ include "jupyterhub.proxy-http.fullname" . | quote }} -proxy-public: {{ include "jupyterhub.proxy-public.fullname" . | quote }} -proxy-public-tls: {{ include "jupyterhub.proxy-public-tls.fullname" . | quote }} -proxy-public-manual-tls: {{ include "jupyterhub.proxy-public-manual-tls.fullname" . | quote }} -autohttps: {{ include "jupyterhub.autohttps.fullname" . | quote }} -autohttps-serviceaccount: {{ include "jupyterhub.autohttps-serviceaccount.fullname" . | quote }} -user-scheduler-deploy: {{ include "jupyterhub.user-scheduler-deploy.fullname" . | quote }} -user-scheduler-serviceaccount: {{ include "jupyterhub.user-scheduler-serviceaccount.fullname" . | quote }} -user-scheduler-lock: {{ include "jupyterhub.user-scheduler-lock.fullname" . | quote }} -user-placeholder: {{ include "jupyterhub.user-placeholder.fullname" . | quote }} -image-puller-priority: {{ include "jupyterhub.image-puller-priority.fullname" . | quote }} -hook-image-awaiter: {{ include "jupyterhub.hook-image-awaiter.fullname" . | quote }} -hook-image-awaiter-serviceaccount: {{ include "jupyterhub.hook-image-awaiter-serviceaccount.fullname" . | quote }} -hook-image-puller: {{ include "jupyterhub.hook-image-puller.fullname" . | quote }} -continuous-image-puller: {{ include "jupyterhub.continuous-image-puller.fullname" . | quote }} -singleuser: {{ include "jupyterhub.singleuser.fullname" . | quote }} -image-pull-secret: {{ include "jupyterhub.image-pull-secret.fullname" . | quote }} -ingress: {{ include "jupyterhub.ingress.fullname" . | quote }} -priority: {{ include "jupyterhub.priority.fullname" . | quote }} -user-placeholder-priority: {{ include "jupyterhub.user-placeholder-priority.fullname" . | quote }} -user-scheduler: {{ include "jupyterhub.user-scheduler.fullname" . | quote }} -{{- end }} diff --git a/jupyterhub/templates/_helpers-netpol.tpl b/jupyterhub/templates/_helpers-netpol.tpl deleted file mode 100644 index 5adbd3d..0000000 --- a/jupyterhub/templates/_helpers-netpol.tpl +++ /dev/null @@ -1,86 +0,0 @@ -{{- /* - This named template renders egress rules for NetworkPolicy resources based on - common configuration. - - It is rendering based on the `egressAllowRules` and `egress` keys of the - passed networkPolicy config object. Each flag set to true under - `egressAllowRules` is rendered to a egress rule that next to any custom user - defined rules from the `egress` config. - - This named template needs to render based on a specific networkPolicy - resource, but also needs access to the root context. Due to that, it - accepts a list as its scope, where the first element is supposed to be the - root context and the second element is supposed to be the networkPolicy - configuration object. - - As an example, this is how you would render this named template from a - NetworkPolicy resource under its egress: - - egress: - # other rules here... - - {{- with (include "jupyterhub.networkPolicy.renderEgressRules" (list . .Values.hub.networkPolicy)) }} - {{- . | nindent 4 }} - {{- end }} - - Note that the reference to privateIPs and nonPrivateIPs relate to - https://en.wikipedia.org/wiki/Private_network#Private_IPv4_addresses. -*/}} - -{{- define "jupyterhub.networkPolicy.renderEgressRules" -}} -{{- $root := index . 0 }} -{{- $netpol := index . 1 }} -{{- if $netpol.egressAllowRules.dnsPortsPrivateIPs }} -# Allow outbound connections to the DNS port in the private IP ranges -- ports: - - protocol: UDP - port: 53 - - protocol: TCP - port: 53 - to: - - ipBlock: - cidr: 10.0.0.0/8 - - ipBlock: - cidr: 172.16.0.0/12 - - ipBlock: - cidr: 192.168.0.0/16 -{{- end }} - -{{- if $netpol.egressAllowRules.nonPrivateIPs }} -# Allow outbound connections to non-private IP ranges -- to: - - ipBlock: - cidr: 0.0.0.0/0 - except: - # As part of this rule, don't: - # - allow outbound connections to private IP - - 10.0.0.0/8 - - 172.16.0.0/12 - - 192.168.0.0/16 - # - allow outbound connections to the cloud metadata server - - {{ $root.Values.singleuser.cloudMetadata.ip }}/32 -{{- end }} - -{{- if $netpol.egressAllowRules.privateIPs }} -# Allow outbound connections to private IP ranges -- to: - - ipBlock: - cidr: 10.0.0.0/8 - - ipBlock: - cidr: 172.16.0.0/12 - - ipBlock: - cidr: 192.168.0.0/16 -{{- end }} - -{{- if $netpol.egressAllowRules.cloudMetadataServer }} -# Allow outbound connections to the cloud metadata server -- to: - - ipBlock: - cidr: {{ $root.Values.singleuser.cloudMetadata.ip }}/32 -{{- end }} - -{{- with $netpol.egress }} -# Allow outbound connections based on user specified rules -{{ . | toYaml }} -{{- end }} -{{- end }} diff --git a/jupyterhub/templates/_helpers.tpl b/jupyterhub/templates/_helpers.tpl deleted file mode 100644 index 5cc5e6d..0000000 --- a/jupyterhub/templates/_helpers.tpl +++ /dev/null @@ -1,402 +0,0 @@ -{{- /* - ## About - This file contains helpers to systematically name, label and select Kubernetes - objects we define in the .yaml template files. - - - ## How helpers work - Helm helper functions is a good way to avoid repeating something. They will - generate some output based on one single dictionary of input that we call the - helpers scope. When you are in helm, you access your current scope with a - single a single punctuation (.). - - When you ask a helper to render its content, one often forward the current - scope to the helper in order to allow it to access .Release.Name, - .Values.rbac.create and similar values. - - #### Example - Passing the current scope - {{ include "jupyterhub.commonLabels" . }} - - It would be possible to pass something specific instead of the current scope - (.), but that would make .Release.Name etc. inaccessible by the helper which - is something we aim to avoid. - - #### Example - Passing a new scope - {{ include "demo.bananaPancakes" (dict "pancakes" 5 "bananas" 3) }} - - To let a helper access the current scope along with additional values we have - opted to create dictionary containing additional values that is then populated - with additional values from the current scope through a the merge function. - - #### Example - Passing a new scope augmented with the old - {{- $_ := merge (dict "appLabel" "kube-lego") . }} - {{- include "jupyterhub.matchLabels" $_ | nindent 6 }} - - In this way, the code within the definition of `jupyterhub.matchLabels` will - be able to access .Release.Name and .appLabel. - - NOTE: - The ordering of merge is crucial, the latter argument is merged into the - former. So if you would swap the order you would influence the current scope - risking unintentional behavior. Therefore, always put the fresh unreferenced - dictionary (dict "key1" "value1") first and the current scope (.) last. - - - ## Declared helpers - - appLabel | - - componentLabel | - - commonLabels | uses appLabel - - labels | uses commonLabels - - matchLabels | uses labels - - podCullerSelector | uses matchLabels - - - ## Example usage - ```yaml - # Excerpt from proxy/autohttps/deployment.yaml - apiVersion: apps/v1 - kind: Deployment - metadata: - name: {{ include "jupyterhub.autohttps.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} - spec: - selector: - matchLabels: - {{- include "jupyterhub.matchLabels" $_ | nindent 6 }} - template: - metadata: - labels: - {{- include "jupyterhub.labels" $_ | nindent 8 }} - hub.jupyter.org/network-access-proxy-http: "true" - ``` - - NOTE: - The "jupyterhub.matchLabels" and "jupyterhub.labels" is passed an augmented - scope that will influence the helpers' behavior. It get the current scope - "." but merged with a dictionary containing extra key/value pairs. In this - case the "." scope was merged with a small dictionary containing only one - key/value pair "appLabel: kube-lego". It is required for kube-lego to - function properly. It is a way to override the default app label's value. -*/}} - - -{{- /* - jupyterhub.appLabel: - Used by "jupyterhub.labels". -*/}} -{{- define "jupyterhub.appLabel" -}} -{{ .Values.nameOverride | default .Chart.Name | trunc 63 | trimSuffix "-" }} -{{- end }} - - -{{- /* - jupyterhub.componentLabel: - Used by "jupyterhub.labels". - - NOTE: The component label is determined by either... - - 1: The provided scope's .componentLabel - - 2: The template's filename if living in the root folder - - 3: The template parent folder's name - - : ...and is combined with .componentPrefix and .componentSuffix -*/}} -{{- define "jupyterhub.componentLabel" -}} -{{- $file := .Template.Name | base | trimSuffix ".yaml" -}} -{{- $parent := .Template.Name | dir | base | trimPrefix "templates" -}} -{{- $component := .componentLabel | default $parent | default $file -}} -{{- $component := print (.componentPrefix | default "") $component (.componentSuffix | default "") -}} -{{ $component }} -{{- end }} - - -{{- /* - jupyterhub.commonLabels: - Foundation for "jupyterhub.labels". - Provides labels: app, release, (chart and heritage). -*/}} -{{- define "jupyterhub.commonLabels" -}} -app: {{ .appLabel | default (include "jupyterhub.appLabel" .) }} -release: {{ .Release.Name }} -{{- if not .matchLabels }} -chart: {{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} -heritage: {{ .heritageLabel | default .Release.Service }} -{{- end }} -{{- end }} - - -{{- /* - jupyterhub.labels: - Provides labels: component, app, release, (chart and heritage). -*/}} -{{- define "jupyterhub.labels" -}} -component: {{ include "jupyterhub.componentLabel" . }} -{{ include "jupyterhub.commonLabels" . }} -{{- end }} - - -{{- /* - jupyterhub.matchLabels: - Used to provide pod selection labels: component, app, release. -*/}} -{{- define "jupyterhub.matchLabels" -}} -{{- $_ := merge (dict "matchLabels" true) . -}} -{{ include "jupyterhub.labels" $_ }} -{{- end }} - - -{{- /* - jupyterhub.dockerconfigjson: - Creates a base64 encoded docker registry json blob for use in a image pull - secret, just like the `kubectl create secret docker-registry` command does - for the generated secrets data.dockerconfigjson field. The output is - verified to be exactly the same even if you have a password spanning - multiple lines as you may need to use a private GCR registry. - - - https://kubernetes.io/docs/concepts/containers/images/#specifying-imagepullsecrets-on-a-pod -*/}} -{{- define "jupyterhub.dockerconfigjson" -}} -{{ include "jupyterhub.dockerconfigjson.yaml" . | b64enc }} -{{- end }} - -{{- define "jupyterhub.dockerconfigjson.yaml" -}} -{{- with .Values.imagePullSecret -}} -{ - "auths": { - {{ .registry | default "https://index.docker.io/v1/" | quote }}: { - "username": {{ .username | quote }}, - "password": {{ .password | quote }}, - {{- if .email }} - "email": {{ .email | quote }}, - {{- end }} - "auth": {{ (print .username ":" .password) | b64enc | quote }} - } - } -} -{{- end }} -{{- end }} - -{{- /* - jupyterhub.imagePullSecrets - Augments passed .pullSecrets with $.Values.imagePullSecrets -*/}} -{{- define "jupyterhub.imagePullSecrets" -}} - {{- /* - We have implemented a trick to allow a parent chart depending on this - chart to call this named templates. - - Caveats and notes: - - 1. While parent charts can reference these, grandparent charts can't. - 2. Parent charts must not use an alias for this chart. - 3. There is no failsafe workaround to above due to - https://github.com/helm/helm/issues/9214. - 4. .Chart is of its own type (*chart.Metadata) and needs to be casted - using "toYaml | fromYaml" in order to be able to use normal helm - template functions on it. - */}} - {{- $jupyterhub_values := .root.Values }} - {{- if ne .root.Chart.Name "jupyterhub" }} - {{- if .root.Values.jupyterhub }} - {{- $jupyterhub_values = .root.Values.jupyterhub }} - {{- end }} - {{- end }} - - {{- /* Populate $_.list with all relevant entries */}} - {{- $_ := dict "list" (concat .image.pullSecrets $jupyterhub_values.imagePullSecrets | uniq) }} - {{- if and $jupyterhub_values.imagePullSecret.create $jupyterhub_values.imagePullSecret.automaticReferenceInjection }} - {{- $__ := set $_ "list" (append $_.list (include "jupyterhub.image-pull-secret.fullname" .root) | uniq) }} - {{- end }} - - {{- /* Decide if something should be written */}} - {{- if not (eq ($_.list | toJson) "[]") }} - - {{- /* Process the $_.list where strings become dicts with a name key and the - strings become the name keys' values into $_.res */}} - {{- $_ := set $_ "res" list }} - {{- range $_.list }} - {{- if eq (typeOf .) "string" }} - {{- $__ := set $_ "res" (append $_.res (dict "name" .)) }} - {{- else }} - {{- $__ := set $_ "res" (append $_.res .) }} - {{- end }} - {{- end }} - - {{- /* Write the results */}} - {{- $_.res | toJson }} - - {{- end }} -{{- end }} - -{{- /* - jupyterhub.singleuser.resources: - The resource request of a singleuser. -*/}} -{{- define "jupyterhub.singleuser.resources" -}} -{{- $r1 := .Values.singleuser.cpu.guarantee -}} -{{- $r2 := .Values.singleuser.memory.guarantee -}} -{{- $r3 := .Values.singleuser.extraResource.guarantees -}} -{{- $r := or $r1 $r2 $r3 -}} -{{- $l1 := .Values.singleuser.cpu.limit -}} -{{- $l2 := .Values.singleuser.memory.limit -}} -{{- $l3 := .Values.singleuser.extraResource.limits -}} -{{- $l := or $l1 $l2 $l3 -}} -{{- if $r -}} -requests: - {{- if $r1 }} - cpu: {{ .Values.singleuser.cpu.guarantee }} - {{- end }} - {{- if $r2 }} - memory: {{ .Values.singleuser.memory.guarantee }} - {{- end }} - {{- if $r3 }} - {{- range $key, $value := .Values.singleuser.extraResource.guarantees }} - {{ $key | quote }}: {{ $value | quote }} - {{- end }} - {{- end }} -{{- end }} - -{{- if $l }} -limits: - {{- if $l1 }} - cpu: {{ .Values.singleuser.cpu.limit }} - {{- end }} - {{- if $l2 }} - memory: {{ .Values.singleuser.memory.limit }} - {{- end }} - {{- if $l3 }} - {{- range $key, $value := .Values.singleuser.extraResource.limits }} - {{ $key | quote }}: {{ $value | quote }} - {{- end }} - {{- end }} -{{- end }} -{{- end }} - -{{- /* - jupyterhub.extraEnv: - Output YAML formatted EnvVar entries for use in a containers env field. -*/}} -{{- define "jupyterhub.extraEnv" -}} -{{- include "jupyterhub.extraEnv.withTrailingNewLine" . | trimSuffix "\n" }} -{{- end }} - -{{- define "jupyterhub.extraEnv.withTrailingNewLine" -}} -{{- if . }} -{{- /* If extraEnv is a list, we inject it as it is. */}} -{{- if eq (typeOf .) "[]interface {}" }} -{{- . | toYaml }} - -{{- /* If extraEnv is a map, we differentiate two cases: */}} -{{- else if eq (typeOf .) "map[string]interface {}" }} -{{- range $key, $value := . }} -{{- /* - - If extraEnv.someKey has a map value, then we add the value as a YAML - parsed list element and use the key as the name value unless its - explicitly set. -*/}} -{{- if eq (typeOf $value) "map[string]interface {}" }} -{{- merge (dict) $value (dict "name" $key) | list | toYaml | println }} -{{- /* - - If extraEnv.someKey has a string value, then we use the key as the - environment variable name for the value. -*/}} -{{- else if eq (typeOf $value) "string" -}} -- name: {{ $key | quote }} - value: {{ $value | quote | println }} -{{- else }} -{{- printf "?.extraEnv.%s had an unexpected type (%s)" $key (typeOf $value) | fail }} -{{- end }} -{{- end }} {{- /* end of range */}} -{{- end }} -{{- end }} {{- /* end of: if . */}} -{{- end }} {{- /* end of definition */}} - -{{- /* - jupyterhub.extraFiles.data: - Renders content for a k8s Secret's data field, coming from extraFiles with - binaryData entries. -*/}} -{{- define "jupyterhub.extraFiles.data.withNewLineSuffix" -}} - {{- range $file_key, $file_details := . }} - {{- include "jupyterhub.extraFiles.validate-file" (list $file_key $file_details) }} - {{- if $file_details.binaryData }} - {{- $file_key | quote }}: {{ $file_details.binaryData | nospace | quote }}{{ println }} - {{- end }} - {{- end }} -{{- end }} -{{- define "jupyterhub.extraFiles.data" -}} - {{- include "jupyterhub.extraFiles.data.withNewLineSuffix" . | trimSuffix "\n" }} -{{- end }} - -{{- /* - jupyterhub.extraFiles.stringData: - Renders content for a k8s Secret's stringData field, coming from extraFiles - with either data or stringData entries. -*/}} -{{- define "jupyterhub.extraFiles.stringData.withNewLineSuffix" -}} - {{- range $file_key, $file_details := . }} - {{- include "jupyterhub.extraFiles.validate-file" (list $file_key $file_details) }} - {{- $file_name := $file_details.mountPath | base }} - {{- if $file_details.stringData }} - {{- $file_key | quote }}: | - {{- $file_details.stringData | trimSuffix "\n" | nindent 2 }}{{ println }} - {{- end }} - {{- if $file_details.data }} - {{- $file_key | quote }}: | - {{- if or (eq (ext $file_name) ".yaml") (eq (ext $file_name) ".yml") }} - {{- $file_details.data | toYaml | nindent 2 }}{{ println }} - {{- else if eq (ext $file_name) ".json" }} - {{- $file_details.data | toJson | nindent 2 }}{{ println }} - {{- else if eq (ext $file_name) ".toml" }} - {{- $file_details.data | toToml | trimSuffix "\n" | nindent 2 }}{{ println }} - {{- else }} - {{- print "\n\nextraFiles entries with 'data' (" $file_key " > " $file_details.mountPath ") needs to have a filename extension of .yaml, .yml, .json, or .toml!" | fail }} - {{- end }} - {{- end }} - {{- end }} -{{- end }} -{{- define "jupyterhub.extraFiles.stringData" -}} - {{- include "jupyterhub.extraFiles.stringData.withNewLineSuffix" . | trimSuffix "\n" }} -{{- end }} - -{{- define "jupyterhub.extraFiles.validate-file" -}} - {{- $file_key := index . 0 }} - {{- $file_details := index . 1 }} - - {{- /* Use of mountPath. */}} - {{- if not ($file_details.mountPath) }} - {{- print "\n\nextraFiles entries (" $file_key ") must contain the field 'mountPath'." | fail }} - {{- end }} - - {{- /* Use one of stringData, binaryData, data. */}} - {{- $field_count := 0 }} - {{- if $file_details.data }} - {{- $field_count = add1 $field_count }} - {{- end }} - {{- if $file_details.stringData }} - {{- $field_count = add1 $field_count }} - {{- end }} - {{- if $file_details.binaryData }} - {{- $field_count = add1 $field_count }} - {{- end }} - {{- if ne $field_count 1 }} - {{- print "\n\nextraFiles entries (" $file_key ") must only contain one of the fields: 'data', 'stringData', and 'binaryData'." | fail }} - {{- end }} -{{- end }} - -{{- /* - jupyterhub.chart-version-to-git-ref: - Renders a valid git reference from a chartpress generated version string. - In practice, either a git tag or a git commit hash will be returned. - - - The version string will follow a chartpress pattern, see - https://github.com/jupyterhub/chartpress#examples-chart-versions-and-image-tags. - - - The regexReplaceAll function is a sprig library function, see - https://masterminds.github.io/sprig/strings.html. - - - The regular expression is in golang syntax, but \d had to become \\d for - example. -*/}} -{{- define "jupyterhub.chart-version-to-git-ref" -}} -{{- regexReplaceAll ".*[.-]n\\d+[.]h(.*)" . "${1}" }} -{{- end }} diff --git a/jupyterhub/templates/hub/_helpers-passwords.tpl b/jupyterhub/templates/hub/_helpers-passwords.tpl deleted file mode 100644 index 83edf70..0000000 --- a/jupyterhub/templates/hub/_helpers-passwords.tpl +++ /dev/null @@ -1,92 +0,0 @@ -{{- /* - This file contains logic to lookup already - generated passwords or generate a new. - - proxy.secretToken / hub.config.ConfigurableHTTPProxy.auth_token - hub.cookieSecret / hub.config.JupyterHub.cookie_secret - auth.state.cryptoKey* / hub.config.CryptKeeper.keys - - *Note that the entire auth section is deprecated and users - are forced through "fail" in NOTES.txt to migrate to hub.config. - - Note that lookup logic returns falsy value when run with - `helm diff upgrade`, so it is a bit troublesome to test. -*/}} - -{{- /* - Returns given number of random Hex characters. - - - randNumeric 4 | atoi generates a random number in [0, 10^4) - This is a range range evenly divisble by 16, but even if off by one, - that last partial interval offsetting randomness is only 1 part in 625. - - mod N 16 maps to the range 0-15 - - printf "%x" represents a single number 0-15 as a single hex character -*/}} -{{- define "jupyterhub.randHex" -}} - {{- $result := "" }} - {{- range $i := until . }} - {{- $rand_hex_char := mod (randNumeric 4 | atoi) 16 | printf "%x" }} - {{- $result = print $result $rand_hex_char }} - {{- end }} - {{- $result }} -{{- end }} - -{{- define "jupyterhub.hub.config.ConfigurableHTTPProxy.auth_token" -}} - {{- if (.Values.hub.config | dig "ConfigurableHTTPProxy" "auth_token" "") }} - {{- .Values.hub.config.ConfigurableHTTPProxy.auth_token }} - {{- else if .Values.proxy.secretToken }} - {{- .Values.proxy.secretToken }} - {{- else }} - {{- $k8s_state := lookup "v1" "Secret" .Release.Namespace (include "jupyterhub.hub.fullname" .) | default (dict "data" (dict)) }} - {{- if hasKey $k8s_state.data "hub.config.ConfigurableHTTPProxy.auth_token" }} - {{- index $k8s_state.data "hub.config.ConfigurableHTTPProxy.auth_token" | b64dec }} - {{- else }} - {{- randAlphaNum 64 }} - {{- end }} - {{- end }} -{{- end }} - -{{- define "jupyterhub.hub.config.JupyterHub.cookie_secret" -}} - {{- if (.Values.hub.config | dig "JupyterHub" "cookie_secret" "") }} - {{- .Values.hub.config.JupyterHub.cookie_secret }} - {{- else if .Values.hub.cookieSecret }} - {{- .Values.hub.cookieSecret }} - {{- else }} - {{- $k8s_state := lookup "v1" "Secret" .Release.Namespace (include "jupyterhub.hub.fullname" .) | default (dict "data" (dict)) }} - {{- if hasKey $k8s_state.data "hub.config.JupyterHub.cookie_secret" }} - {{- index $k8s_state.data "hub.config.JupyterHub.cookie_secret" | b64dec }} - {{- else }} - {{- include "jupyterhub.randHex" 64 }} - {{- end }} - {{- end }} -{{- end }} - -{{- define "jupyterhub.hub.config.CryptKeeper.keys" -}} - {{- if (.Values.hub.config | dig "CryptKeeper" "keys" "") }} - {{- .Values.hub.config.CryptKeeper.keys | join ";" }} - {{- else }} - {{- $k8s_state := lookup "v1" "Secret" .Release.Namespace (include "jupyterhub.hub.fullname" .) | default (dict "data" (dict)) }} - {{- if hasKey $k8s_state.data "hub.config.CryptKeeper.keys" }} - {{- index $k8s_state.data "hub.config.CryptKeeper.keys" | b64dec }} - {{- else }} - {{- include "jupyterhub.randHex" 64 }} - {{- end }} - {{- end }} -{{- end }} - -{{- define "jupyterhub.hub.services.get_api_token" -}} - {{- $_ := index . 0 }} - {{- $service_key := index . 1 }} - {{- $explicitly_set_api_token := or ($_.Values.hub.services | dig $service_key "api_token" "") ($_.Values.hub.services | dig $service_key "apiToken" "") }} - {{- if $explicitly_set_api_token }} - {{- $explicitly_set_api_token }} - {{- else }} - {{- $k8s_state := lookup "v1" "Secret" $_.Release.Namespace (include "jupyterhub.hub.fullname" $_) | default (dict "data" (dict)) }} - {{- $k8s_secret_key := print "hub.services." $service_key ".apiToken" }} - {{- if hasKey $k8s_state.data $k8s_secret_key }} - {{- index $k8s_state.data $k8s_secret_key | b64dec }} - {{- else }} - {{- include "jupyterhub.randHex" 64 }} - {{- end }} - {{- end }} -{{- end }} diff --git a/jupyterhub/templates/hub/configmap-theme.yaml b/jupyterhub/templates/hub/configmap-theme.yaml deleted file mode 100644 index cfbae26..0000000 --- a/jupyterhub/templates/hub/configmap-theme.yaml +++ /dev/null @@ -1,25 +0,0 @@ -kind: ConfigMap -apiVersion: v1 -metadata: - name: {{ include "jupyterhub.hub.fullname" . }}-theme - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -data: - {{- /* - Glob files to allow them to be mounted by the hub pod - - jupyterhub_config: | - multi line string content... - z2jh.py: | - multi line string content... - */}} - # templates for the hub - page.html: {{ required "A valid .Values.pageTheme entry required!" (tpl (.Values.pageTheme | default (.Files.Get "files/theme/page.html")) . | quote ) }} - spawn_pending.html: {{ required "A valid .Values.spawnPendingTheme entry required!" (tpl (.Values.spawnPendingTheme | default (.Files.Get "files/theme/spawn_pending.html")) . | quote ) }} - spawn.html: {{ required "A valid .Values.spawnTheme entry required!" (tpl (.Values.spawnTheme | default (.Files.Get "files/theme/spawn.html")) . | quote) }} - - {{- /* - Store away a checksum of the hook-image-puller daemonset so future upgrades - can compare and decide if it should run or not using the `lookup` function. - */}} - checksum_hook-image-puller: {{ include "jupyterhub.imagePuller.daemonset.hook.checksum" . | quote }} diff --git a/jupyterhub/templates/hub/configmap.yaml b/jupyterhub/templates/hub/configmap.yaml deleted file mode 100644 index 08debe6..0000000 --- a/jupyterhub/templates/hub/configmap.yaml +++ /dev/null @@ -1,34 +0,0 @@ -kind: ConfigMap -apiVersion: v1 -metadata: - name: {{ include "jupyterhub.hub.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -data: - {{- /* - Resource names exposed to reliably reference them. - - user-scheduler: "my-helm-release-user-scheduler" - ... - */}} - {{- include "jupyterhub.name-templates" . | nindent 2 }} - - {{- /* - Glob files to allow them to be mounted by the hub pod - - jupyterhub_config: | - multi line string content... - z2jh.py: | - multi line string content... - */}} - config.yml: {{ required "A valid .Values.appHubConfig entry required!" (tpl (.Values.appHubConfig | default (.Files.Get "files/hub/config.yml")) . | quote) }} - jupyterhub_config.py: {{ required "A valid .Values.jupHubConfig entry required!" (tpl (.Values.jupHubConfig | default (.Files.Get "files/hub/jupyterhub_config.py")) . | quote) }} - z2jh.py: {{ required "A valid .Values.z2jhConfig entry required!" (tpl (.Values.z2jhConfig | default (.Files.Get "files/hub/z2jh.py")) . | quote) }} - - - - {{- /* - Store away a checksum of the hook-image-puller daemonset so future upgrades - can compare and decide if it should run or not using the `lookup` function. - */}} - checksum_hook-image-puller: {{ include "jupyterhub.imagePuller.daemonset.hook.checksum" . | quote }} diff --git a/jupyterhub/templates/hub/deployment.yaml b/jupyterhub/templates/hub/deployment.yaml deleted file mode 100644 index dd46d2f..0000000 --- a/jupyterhub/templates/hub/deployment.yaml +++ /dev/null @@ -1,252 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "jupyterhub.hub.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -spec: - {{- if typeIs "int" .Values.hub.revisionHistoryLimit }} - revisionHistoryLimit: {{ .Values.hub.revisionHistoryLimit }} - {{- end }} - replicas: 1 - selector: - matchLabels: - {{- include "jupyterhub.matchLabels" . | nindent 6 }} - strategy: - {{- .Values.hub.deploymentStrategy | toYaml | nindent 4 }} - template: - metadata: - labels: - {{- /* Changes here will cause the Deployment to restart the pods. */}} - {{- include "jupyterhub.matchLabels" . | nindent 8 }} - hub.jupyter.org/network-access-proxy-api: "true" - hub.jupyter.org/network-access-proxy-http: "true" - hub.jupyter.org/network-access-singleuser: "true" - {{- with .Values.hub.labels }} - {{- . | toYaml | nindent 8 }} - {{- end }} - annotations: - {{- /* This lets us autorestart when the secret changes! */}} - checksum/configmap: {{ include (print .Template.BasePath "/hub/configmap.yaml") . | sha256sum }} - checksum/theme: {{ include (print .Template.BasePath "/hub/configmap-theme.yaml") . | sha256sum }} - checksum/secret: {{ include (print .Template.BasePath "/hub/secret.yaml") . | sha256sum }} - {{- with .Values.hub.annotations }} - {{- . | toYaml | nindent 8 }} - {{- end }} - spec: - {{- if .Values.scheduling.podPriority.enabled }} - priorityClassName: {{ include "jupyterhub.priority.fullname" . }} - {{- end }} - {{- with .Values.hub.nodeSelector }} - nodeSelector: - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- with concat .Values.scheduling.corePods.tolerations .Values.hub.tolerations }} - tolerations: - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- include "jupyterhub.coreAffinity" . | nindent 6 }} - volumes: - - name: theme - configMap: - name: {{ include "jupyterhub.hub.fullname" . }}-theme - - name: config - configMap: - name: {{ include "jupyterhub.hub.fullname" . }} - - name: secret - secret: - secretName: {{ include "jupyterhub.hub.fullname" . }} - {{- with (include "jupyterhub.hub-existing-secret.fullname" .) }} - - name: existing-secret - secret: - secretName: {{ . }} - {{- end }} - {{- if .Values.hub.extraFiles }} - - name: files - secret: - secretName: {{ include "jupyterhub.hub.fullname" . }} - items: - {{- range $file_key, $file_details := .Values.hub.extraFiles }} - - key: {{ $file_key | quote }} - path: {{ $file_key | quote }} - {{- with $file_details.mode }} - mode: {{ . }} - {{- end }} - {{- end }} - {{- end }} - {{- with .Values.hub.extraVolumes }} - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- if eq .Values.hub.db.type "sqlite-pvc" }} - - name: pvc - persistentVolumeClaim: - claimName: {{ include "jupyterhub.hub-pvc.fullname" . }} - {{- end }} - {{- with include "jupyterhub.hub-serviceaccount.fullname" . }} - serviceAccountName: {{ . }} - {{- end }} - {{- with .Values.hub.podSecurityContext }} - securityContext: - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- with include "jupyterhub.imagePullSecrets" (dict "root" . "image" .Values.hub.image) }} - imagePullSecrets: {{ . }} - {{- end }} - {{- with .Values.hub.initContainers }} - initContainers: - {{- . | toYaml | nindent 8 }} - {{- end }} - containers: - {{- with .Values.hub.extraContainers }} - {{- . | toYaml | nindent 8 }} - {{- end }} - - name: hub - image: {{ .Values.hub.image.name }}:{{ .Values.hub.image.tag }} - {{- with .Values.hub.command }} - command: - {{- range . }} - - {{ tpl . $ }} - {{- end }} - {{- end }} - args: - {{- /* .Values.hub.args overrides everything the Helm chart otherside would set */}} - {{- if .Values.hub.args }} - {{- range .Values.hub.args }} - - {{ tpl . $ }} - {{- end }} - - {{- /* .Values.hub.args didn't replace the default logic */}} - {{- else }} - - jupyterhub - - --config - - /usr/local/etc/jupyterhub/jupyterhub_config.py - {{- if .Values.debug.enabled }} - - --debug - {{- end }} - {{- /* NOTE: - We want to do automatic upgrades for sqlite-pvc by default, but - allow users to opt out of that if they want. Users using their own - db need to 'opt in' Go Templates treat nil and "" and false as - 'false', making this code complex. We can probably make this a - one-liner, but doing combinations of boolean vars in go templates - is very inelegant & hard to reason about. - */}} - {{- $upgradeType := typeOf .Values.hub.db.upgrade }} - {{- if eq $upgradeType "bool" }} - {{- /* .Values.hub.db.upgrade has been explicitly set to true or false */}} - {{- if .Values.hub.db.upgrade }} - - --upgrade-db - {{- end }} - {{- else if eq $upgradeType "" }} - {{- /* .Values.hub.db.upgrade is nil */}} - {{- if eq .Values.hub.db.type "sqlite-pvc" }} - - --upgrade-db - {{- end }} - {{- end }} - {{- end }} - volumeMounts: - - mountPath: /usr/local/etc/jupyterhub/config.yml - subPath: config.yml - name: config - - mountPath: /usr/local/etc/jupyterhub/jupyterhub_config.py - subPath: jupyterhub_config.py - name: config - - mountPath: /usr/local/etc/jupyterhub/z2jh.py - subPath: z2jh.py - name: config - - mountPath: /usr/local/etc/jupyterhub/config/ - name: config - - mountPath: /opt/jupyterhub/template/ - name: theme - - mountPath: /usr/local/etc/jupyterhub/secret/ - name: secret - {{- if (include "jupyterhub.hub-existing-secret.fullname" .) }} - - mountPath: /usr/local/etc/jupyterhub/existing-secret/ - name: existing-secret - {{- end }} - {{- range $file_key, $file_details := .Values.hub.extraFiles }} - - mountPath: {{ $file_details.mountPath }} - subPath: {{ $file_key | quote }} - name: files - {{- end }} - {{- with .Values.hub.extraVolumeMounts }} - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- if eq .Values.hub.db.type "sqlite-pvc" }} - - mountPath: /srv/jupyterhub - name: pvc - {{- with .Values.hub.db.pvc.subPath }} - subPath: {{ . | quote }} - {{- end }} - {{- end }} - {{- with .Values.hub.resources }} - resources: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- with .Values.hub.image.pullPolicy }} - imagePullPolicy: {{ . }} - {{- end }} - {{- with .Values.hub.containerSecurityContext }} - securityContext: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- with .Values.hub.lifecycle }} - lifecycle: - {{- . | toYaml | nindent 12 }} - {{- end }} - env: - - name: PYTHONUNBUFFERED - value: "1" - - name: HELM_RELEASE_NAME - value: {{ .Release.Name | quote }} - - name: POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: CONFIGPROXY_AUTH_TOKEN - valueFrom: - secretKeyRef: - {{- /* NOTE: - References the chart managed k8s Secret even if - hub.existingSecret is specified to avoid using the lookup - function on the user managed k8s Secret which is assumed to - not be possible. - */}} - name: {{ include "jupyterhub.hub.fullname" . }} - key: hub.config.ConfigurableHTTPProxy.auth_token - {{- with .Values.hub.extraEnv }} - {{- include "jupyterhub.extraEnv" . | nindent 12 }} - {{- end }} - ports: - - name: http - containerPort: 8081 - {{- if .Values.hub.livenessProbe.enabled }} - {{- /* NOTE: - We don't know how long hub database upgrades could take so having a - liveness probe could be a bit risky unless we put a - initialDelaySeconds value with long enough margin for that to not be - an issue. If it is too short, we could end up aborting database - upgrades midway or ending up in an infinite restart loop. - */}} - livenessProbe: - initialDelaySeconds: {{ .Values.hub.livenessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.hub.livenessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.hub.livenessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.hub.livenessProbe.failureThreshold }} - httpGet: - path: {{ .Values.hub.baseUrl | trimSuffix "/" }}/hub/health - port: http - {{- end }} - {{- if .Values.hub.readinessProbe.enabled }} - readinessProbe: - initialDelaySeconds: {{ .Values.hub.readinessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.hub.readinessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.hub.readinessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.hub.readinessProbe.failureThreshold }} - httpGet: - path: {{ .Values.hub.baseUrl | trimSuffix "/" }}/hub/health - port: http - {{- end }} - {{- with .Values.hub.extraPodSpec }} - {{- . | toYaml | nindent 6 }} - {{- end }} diff --git a/jupyterhub/templates/hub/netpol.yaml b/jupyterhub/templates/hub/netpol.yaml deleted file mode 100644 index 904b2c3..0000000 --- a/jupyterhub/templates/hub/netpol.yaml +++ /dev/null @@ -1,84 +0,0 @@ -{{- if .Values.hub.networkPolicy.enabled -}} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: {{ include "jupyterhub.hub.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -spec: - podSelector: - matchLabels: - {{- include "jupyterhub.matchLabels" . | nindent 6 }} - policyTypes: - - Ingress - - Egress - - # IMPORTANT: - # NetworkPolicy's ingress "from" and egress "to" rule specifications require - # great attention to detail. A quick summary is: - # - # 1. You can provide "from"/"to" rules that provide access either ports or a - # subset of ports. - # 2. You can for each "from"/"to" rule provide any number of - # "sources"/"destinations" of four different kinds. - # - podSelector - targets pods with a certain label in the same namespace as the NetworkPolicy - # - namespaceSelector - targets all pods running in namespaces with a certain label - # - namespaceSelector and podSelector - targets pods with a certain label running in namespaces with a certain label - # - ipBlock - targets network traffic from/to a set of IP address ranges - # - # Read more at: https://kubernetes.io/docs/concepts/services-networking/network-policies/#behavior-of-to-and-from-selectors - # - ingress: - {{- with .Values.hub.networkPolicy.allowedIngressPorts }} - # allow incoming traffic to these ports independent of source - - ports: - {{- range $port := . }} - - port: {{ $port }} - {{- end }} - {{- end }} - - # allowed pods (hub.jupyter.org/network-access-hub) --> hub - - ports: - - port: http - from: - # source 1 - labeled pods - - podSelector: - matchLabels: - hub.jupyter.org/network-access-hub: "true" - {{- if eq .Values.hub.networkPolicy.interNamespaceAccessLabels "accept" }} - namespaceSelector: - matchLabels: {} # without this, the podSelector would only consider pods in the local namespace - # source 2 - pods in labeled namespaces - - namespaceSelector: - matchLabels: - hub.jupyter.org/network-access-hub: "true" - {{- end }} - - {{- with .Values.hub.networkPolicy.ingress }} - # depends, but default is nothing --> hub - {{- . | toYaml | nindent 4 }} - {{- end }} - - egress: - # hub --> proxy - - to: - - podSelector: - matchLabels: - {{- $_ := merge (dict "componentLabel" "proxy") . }} - {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} - ports: - - port: 8001 - - # hub --> singleuser-server - - to: - - podSelector: - matchLabels: - {{- $_ := merge (dict "componentLabel" "singleuser-server") . }} - {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} - ports: - - port: 8888 - - {{- with (include "jupyterhub.networkPolicy.renderEgressRules" (list . .Values.hub.networkPolicy)) }} - {{- . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/jupyterhub/templates/hub/pdb.yaml b/jupyterhub/templates/hub/pdb.yaml deleted file mode 100644 index 3a22e39..0000000 --- a/jupyterhub/templates/hub/pdb.yaml +++ /dev/null @@ -1,23 +0,0 @@ -{{- if .Values.hub.pdb.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} -{{- /* k8s 1.21+ required */ -}} -apiVersion: policy/v1 -{{- else }} -apiVersion: policy/v1beta1 -{{- end }} -kind: PodDisruptionBudget -metadata: - name: {{ include "jupyterhub.hub.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -spec: - {{- if not (typeIs "" .Values.hub.pdb.maxUnavailable) }} - maxUnavailable: {{ .Values.hub.pdb.maxUnavailable }} - {{- end }} - {{- if not (typeIs "" .Values.hub.pdb.minAvailable) }} - minAvailable: {{ .Values.hub.pdb.minAvailable }} - {{- end }} - selector: - matchLabels: - {{- include "jupyterhub.matchLabels" . | nindent 6 }} -{{- end }} diff --git a/jupyterhub/templates/hub/pvc.yaml b/jupyterhub/templates/hub/pvc.yaml deleted file mode 100644 index a433a97..0000000 --- a/jupyterhub/templates/hub/pvc.yaml +++ /dev/null @@ -1,25 +0,0 @@ -{{- if eq .Values.hub.db.type "sqlite-pvc" -}} -kind: PersistentVolumeClaim -apiVersion: v1 -metadata: - name: {{ include "jupyterhub.hub-pvc.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} - {{- with .Values.hub.db.pvc.annotations }} - annotations: - {{- . | toYaml | nindent 4 }} - {{- end }} -spec: - {{- with .Values.hub.db.pvc.selector }} - selector: - {{- . | toYaml | nindent 4 }} - {{- end }} - {{- if typeIs "string" .Values.hub.db.pvc.storageClassName }} - storageClassName: {{ .Values.hub.db.pvc.storageClassName | quote }} - {{- end }} - accessModes: - {{- .Values.hub.db.pvc.accessModes | toYaml | nindent 4 }} - resources: - requests: - storage: {{ .Values.hub.db.pvc.storage | quote }} -{{- end }} diff --git a/jupyterhub/templates/hub/rbac.yaml b/jupyterhub/templates/hub/rbac.yaml deleted file mode 100644 index 3abf00e..0000000 --- a/jupyterhub/templates/hub/rbac.yaml +++ /dev/null @@ -1,30 +0,0 @@ -{{- if .Values.rbac.create -}} -kind: Role -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: {{ include "jupyterhub.hub.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -rules: - - apiGroups: [""] # "" indicates the core API group - resources: ["pods", "persistentvolumeclaims", "secrets", "services"] - verbs: ["get", "watch", "list", "create", "delete"] - - apiGroups: [""] # "" indicates the core API group - resources: ["events"] - verbs: ["get", "watch", "list"] ---- -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: {{ include "jupyterhub.hub.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -subjects: - - kind: ServiceAccount - name: {{ include "jupyterhub.hub-serviceaccount.fullname" . }} - namespace: "{{ .Release.Namespace }}" -roleRef: - kind: Role - name: {{ include "jupyterhub.hub.fullname" . }} - apiGroup: rbac.authorization.k8s.io -{{- end }} diff --git a/jupyterhub/templates/hub/secret.yaml b/jupyterhub/templates/hub/secret.yaml deleted file mode 100644 index 851bda0..0000000 --- a/jupyterhub/templates/hub/secret.yaml +++ /dev/null @@ -1,50 +0,0 @@ -kind: Secret -apiVersion: v1 -metadata: - name: {{ include "jupyterhub.hub.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -type: Opaque -data: - {{- $values := merge dict .Values }} - {{- /* also passthrough subset of Chart / Release */}} - {{- $_ := set $values "Chart" (dict "Name" .Chart.Name "Version" .Chart.Version) }} - {{- $_ := set $values "Release" (pick .Release "Name" "Namespace" "Service") }} - values.yaml: {{ $values | toYaml | b64enc | quote }} - - {{- with .Values.hub.db.password }} - # Used to mount MYSQL_PWD or PGPASSWORD on hub pod, unless hub.existingSecret - # is set as then that k8s Secret's value must be specified instead. - hub.db.password: {{ . | b64enc | quote }} - {{- end }} - - # Any JupyterHub Services api_tokens are exposed in this k8s Secret as a - # convinience for external services running in the k8s cluster that could - # mount them directly from this k8s Secret. - {{- range $key, $service := .Values.hub.services }} - hub.services.{{ $key }}.apiToken: {{ include "jupyterhub.hub.services.get_api_token" (list $ $key) | b64enc | quote }} - {{- end }} - - # During Helm template rendering, these values that can be autogenerated for - # users are set using the following logic: - # - # 1. Use chart configuration's value - # 2. Use k8s Secret's value - # 3. Use a new autogenerated value - # - # hub.config.ConfigurableHTTPProxy.auth_token: for hub to proxy-api authorization (JupyterHub.proxy_auth_token is deprecated) - # hub.config.JupyterHub.cookie_secret: for cookie encryption - # hub.config.CryptKeeper.keys: for auth state encryption - # - hub.config.ConfigurableHTTPProxy.auth_token: {{ include "jupyterhub.hub.config.ConfigurableHTTPProxy.auth_token" . | required "This should not happen: blank output from 'jupyterhub.hub.config.ConfigurableHTTPProxy.auth_token' template" | b64enc | quote }} - hub.config.JupyterHub.cookie_secret: {{ include "jupyterhub.hub.config.JupyterHub.cookie_secret" . | required "This should not happen: blank output from 'jupyterhub.hub.config.JupyterHub.cookie_secret' template" | b64enc | quote }} - hub.config.CryptKeeper.keys: {{ include "jupyterhub.hub.config.CryptKeeper.keys" . | required "This should not happen: blank output from 'jupyterhub.hub.config.CryptKeeper.keys' template" | b64enc | quote }} - - {{- with include "jupyterhub.extraFiles.data" .Values.hub.extraFiles }} - {{- . | nindent 2 }} - {{- end }} - -{{- with include "jupyterhub.extraFiles.stringData" .Values.hub.extraFiles }} -stringData: - {{- . | nindent 2 }} -{{- end }} diff --git a/jupyterhub/templates/hub/service.yaml b/jupyterhub/templates/hub/service.yaml deleted file mode 100644 index 13f80b5..0000000 --- a/jupyterhub/templates/hub/service.yaml +++ /dev/null @@ -1,37 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - name: {{ include "jupyterhub.hub.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} - annotations: - {{- if not (index .Values.hub.service.annotations "prometheus.io/scrape") }} - prometheus.io/scrape: "true" - {{- end }} - {{- if not (index .Values.hub.service.annotations "prometheus.io/path") }} - prometheus.io/path: {{ .Values.hub.baseUrl | trimSuffix "/" }}/hub/metrics - {{- end }} - {{- if not (index .Values.hub.service.annotations "prometheus.io/port") }} - prometheus.io/port: "8081" - {{- end }} - {{- with .Values.hub.service.annotations }} - {{- . | toYaml | nindent 4 }} - {{- end }} -spec: - type: {{ .Values.hub.service.type }} - {{- with .Values.hub.service.loadBalancerIP }} - loadBalancerIP: {{ . }} - {{- end }} - selector: - {{- include "jupyterhub.matchLabels" . | nindent 4 }} - ports: - - name: hub - port: 8081 - targetPort: http - {{- with .Values.hub.service.ports.nodePort }} - nodePort: {{ . }} - {{- end }} - - {{- with .Values.hub.service.extraPorts }} - {{- . | toYaml | nindent 4 }} - {{- end }} diff --git a/jupyterhub/templates/hub/serviceaccount.yaml b/jupyterhub/templates/hub/serviceaccount.yaml deleted file mode 100644 index 06a5069..0000000 --- a/jupyterhub/templates/hub/serviceaccount.yaml +++ /dev/null @@ -1,12 +0,0 @@ -{{- if .Values.hub.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "jupyterhub.hub-serviceaccount.fullname" . }} - {{- with .Values.hub.serviceAccount.annotations }} - annotations: - {{- . | toYaml | nindent 4 }} - {{- end }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -{{- end }} diff --git a/jupyterhub/templates/image-pull-secret.yaml b/jupyterhub/templates/image-pull-secret.yaml deleted file mode 100644 index e033ec6..0000000 --- a/jupyterhub/templates/image-pull-secret.yaml +++ /dev/null @@ -1,15 +0,0 @@ -{{- if .Values.imagePullSecret.create }} -kind: Secret -apiVersion: v1 -metadata: - name: {{ include "jupyterhub.image-pull-secret.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} - annotations: - "helm.sh/hook": pre-install,pre-upgrade - "helm.sh/hook-delete-policy": before-hook-creation - "helm.sh/hook-weight": "-20" -type: kubernetes.io/dockerconfigjson -data: - .dockerconfigjson: {{ include "jupyterhub.dockerconfigjson" . }} -{{- end }} diff --git a/jupyterhub/templates/image-puller/_helpers-daemonset.tpl b/jupyterhub/templates/image-puller/_helpers-daemonset.tpl deleted file mode 100644 index 1fe8276..0000000 --- a/jupyterhub/templates/image-puller/_helpers-daemonset.tpl +++ /dev/null @@ -1,251 +0,0 @@ -{{- /* -Returns an image-puller daemonset. Two daemonsets will be created like this. -- hook-image-puller: for pre helm upgrade image pulling (lives temporarily) -- continuous-image-puller: for newly added nodes image pulling -*/}} -{{- define "jupyterhub.imagePuller.daemonset" -}} -apiVersion: apps/v1 -kind: DaemonSet -metadata: - {{- if .hook }} - name: {{ include "jupyterhub.hook-image-puller.fullname" . }} - {{- else }} - name: {{ include "jupyterhub.continuous-image-puller.fullname" . }} - {{- end }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} - {{- if .hook }} - hub.jupyter.org/deletable: "true" - {{- end }} - {{- if .hook }} - annotations: - {{- /* - Allows the daemonset to be deleted when the image-awaiter job is completed. - */}} - "helm.sh/hook": pre-install,pre-upgrade - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook-weight": "-10" - {{- end }} -spec: - selector: - matchLabels: - {{- include "jupyterhub.matchLabels" . | nindent 6 }} - updateStrategy: - type: RollingUpdate - rollingUpdate: - maxUnavailable: 100% - {{- if typeIs "int" .Values.prePuller.revisionHistoryLimit }} - revisionHistoryLimit: {{ .Values.prePuller.revisionHistoryLimit }} - {{- end }} - template: - metadata: - labels: - {{- include "jupyterhub.matchLabels" . | nindent 8 }} - {{- with .Values.prePuller.annotations }} - annotations: - {{- . | toYaml | nindent 8 }} - {{- end }} - spec: - {{- /* - image-puller pods are made evictable to save on the k8s pods - per node limit all k8s clusters have and have a higher priority - than user-placeholder pods that could block an entire node. - */}} - {{- if .Values.scheduling.podPriority.enabled }} - priorityClassName: {{ include "jupyterhub.image-puller-priority.fullname" . }} - {{- end }} - {{- with .Values.singleuser.nodeSelector }} - nodeSelector: - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- with concat .Values.scheduling.userPods.tolerations .Values.singleuser.extraTolerations .Values.prePuller.extraTolerations }} - tolerations: - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- if include "jupyterhub.userNodeAffinityRequired" . }} - affinity: - nodeAffinity: - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - {{- include "jupyterhub.userNodeAffinityRequired" . | nindent 14 }} - {{- end }} - terminationGracePeriodSeconds: 0 - automountServiceAccountToken: false - {{- with include "jupyterhub.imagePullSecrets" (dict "root" . "image" .Values.singleuser.image) }} - imagePullSecrets: {{ . }} - {{- end }} - initContainers: - {{- /* --- Conditionally pull an image all user pods will use in an initContainer --- */}} - {{- $blockWithIptables := hasKey .Values.singleuser.cloudMetadata "enabled" | ternary (not .Values.singleuser.cloudMetadata.enabled) .Values.singleuser.cloudMetadata.blockWithIptables }} - {{- if $blockWithIptables }} - - name: image-pull-metadata-block - image: {{ .Values.singleuser.networkTools.image.name }}:{{ .Values.singleuser.networkTools.image.tag }} - {{- with .Values.singleuser.networkTools.image.pullPolicy }} - imagePullPolicy: {{ . }} - {{- end }} - command: - - /bin/sh - - -c - - echo "Pulling complete" - {{- with .Values.prePuller.resources }} - resources: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- with .Values.prePuller.containerSecurityContext }} - securityContext: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- end }} - - {{- /* --- Pull default image --- */}} - - name: image-pull-singleuser - image: {{ .Values.singleuser.image.name }}:{{ .Values.singleuser.image.tag }} - command: - - /bin/sh - - -c - - echo "Pulling complete" - {{- with .Values.prePuller.resources }} - resources: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- with .Values.prePuller.containerSecurityContext }} - securityContext: - {{- . | toYaml | nindent 12 }} - {{- end }} - - {{- /* --- Pull extra containers' images --- */}} - {{- range $k, $container := concat .Values.singleuser.initContainers .Values.singleuser.extraContainers }} - - name: image-pull-singleuser-init-and-extra-containers-{{ $k }} - image: {{ $container.image }} - command: - - /bin/sh - - -c - - echo "Pulling complete" - {{- with $.Values.prePuller.resources }} - resources: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- with $.Values.prePuller.containerSecurityContext }} - securityContext: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- end }} - - {{- /* --- Conditionally pull profileList images --- */}} - {{- if .Values.prePuller.pullProfileListImages }} - {{- range $k, $container := .Values.singleuser.profileList }} - {{- if $container.kubespawner_override }} - {{- if $container.kubespawner_override.image }} - - name: image-pull-singleuser-profilelist-{{ $k }} - image: {{ $container.kubespawner_override.image }} - command: - - /bin/sh - - -c - - echo "Pulling complete" - {{- with $.Values.prePuller.resources }} - resources: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- with $.Values.prePuller.containerSecurityContext }} - securityContext: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- end }} - {{- end }} - {{- end }} - {{- end }} - - {{- /* --- Pull extra images --- */}} - {{- range $k, $v := .Values.prePuller.extraImages }} - - name: image-pull-{{ $k }} - image: {{ $v.name }}:{{ $v.tag }} - command: - - /bin/sh - - -c - - echo "Pulling complete" - {{- with $.Values.prePuller.resources }} - resources: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- with $.Values.prePuller.containerSecurityContext }} - securityContext: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- end }} - containers: - - name: pause - image: {{ .Values.prePuller.pause.image.name }}:{{ .Values.prePuller.pause.image.tag }} - {{- with .Values.prePuller.resources }} - resources: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- with .Values.prePuller.pause.containerSecurityContext }} - securityContext: - {{- . | toYaml | nindent 12 }} - {{- end }} -{{- end }} - - -{{- /* - Returns a rendered k8s DaemonSet resource: continuous-image-puller -*/}} -{{- define "jupyterhub.imagePuller.daemonset.continuous" -}} - {{- $_ := merge (dict "hook" false "componentPrefix" "continuous-") . }} - {{- include "jupyterhub.imagePuller.daemonset" $_ }} -{{- end }} - - -{{- /* - Returns a rendered k8s DaemonSet resource: hook-image-puller -*/}} -{{- define "jupyterhub.imagePuller.daemonset.hook" -}} - {{- $_ := merge (dict "hook" true "componentPrefix" "hook-") . }} - {{- include "jupyterhub.imagePuller.daemonset" $_ }} -{{- end }} - - -{{- /* - Returns a checksum of the rendered k8s DaemonSet resource: hook-image-puller - - This checksum is used when prePuller.hook.pullOnlyOnChanges=true to decide if - it is worth creating the hook-image-puller associated resources. -*/}} -{{- define "jupyterhub.imagePuller.daemonset.hook.checksum" -}} - {{- /* - We pin componentLabel and Chart.Version as doing so can pin labels - of no importance if they would change. Chart.Name is also pinned as - a harmless technical workaround when we compute the checksum. - */}} - {{- $_ := merge (dict "componentLabel" "pinned" "Chart" (dict "Name" "jupyterhub" "Version" "pinned")) . -}} - {{- $yaml := include "jupyterhub.imagePuller.daemonset.hook" $_ }} - {{- $yaml | sha256sum }} -{{- end }} - - -{{- /* - Returns a truthy string or a blank string depending on if the - hook-image-puller should be installed. The truthy strings are comments - that summarize the state that led to returning a truthy string. - - - prePuller.hook.enabled must be true - - if prePuller.hook.pullOnlyOnChanges is true, the checksum of the - hook-image-puller daemonset must differ since last upgrade -*/}} -{{- define "jupyterhub.imagePuller.daemonset.hook.install" -}} - {{- if .Values.prePuller.hook.enabled }} - {{- if .Values.prePuller.hook.pullOnlyOnChanges }} - {{- $new_checksum := include "jupyterhub.imagePuller.daemonset.hook.checksum" . }} - {{- $k8s_state := lookup "v1" "ConfigMap" .Release.Namespace (include "jupyterhub.hub.fullname" .) | default (dict "data" (dict)) }} - {{- $old_checksum := index $k8s_state.data "checksum_hook-image-puller" | default "" }} - {{- if ne $new_checksum $old_checksum -}} -# prePuller.hook.enabled={{ .Values.prePuller.hook.enabled }} -# prePuller.hook.pullOnlyOnChanges={{ .Values.prePuller.hook.pullOnlyOnChanges }} -# post-upgrade checksum != pre-upgrade checksum (of the hook-image-puller DaemonSet) -# "{{ $new_checksum }}" != "{{ $old_checksum}}" - {{- end }} - {{- else -}} -# prePuller.hook.enabled={{ .Values.prePuller.hook.enabled }} -# prePuller.hook.pullOnlyOnChanges={{ .Values.prePuller.hook.pullOnlyOnChanges }} - {{- end }} - {{- end }} -{{- end }} diff --git a/jupyterhub/templates/image-puller/daemonset-continuous.yaml b/jupyterhub/templates/image-puller/daemonset-continuous.yaml deleted file mode 100644 index 85a572f..0000000 --- a/jupyterhub/templates/image-puller/daemonset-continuous.yaml +++ /dev/null @@ -1,8 +0,0 @@ -{{- /* -The continuous-image-puller daemonset task is to pull required images to nodes -that are added in between helm upgrades, for example by manually adding a node -or by the cluster autoscaler. -*/}} -{{- if .Values.prePuller.continuous.enabled }} -{{- include "jupyterhub.imagePuller.daemonset.continuous" . }} -{{- end }} diff --git a/jupyterhub/templates/image-puller/daemonset-hook.yaml b/jupyterhub/templates/image-puller/daemonset-hook.yaml deleted file mode 100644 index 7e9c2d0..0000000 --- a/jupyterhub/templates/image-puller/daemonset-hook.yaml +++ /dev/null @@ -1,9 +0,0 @@ -{{- /* -The hook-image-puller daemonset will be created with the highest priority during -helm upgrades. It's task is to pull the required images on all nodes. When the -image-awaiter job confirms the required images to be pulled, the daemonset is -deleted. Only then will the actual helm upgrade start. -*/}} -{{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) -}} -{{- include "jupyterhub.imagePuller.daemonset.hook" . }} -{{- end }} diff --git a/jupyterhub/templates/image-puller/job.yaml b/jupyterhub/templates/image-puller/job.yaml deleted file mode 100644 index 5509f13..0000000 --- a/jupyterhub/templates/image-puller/job.yaml +++ /dev/null @@ -1,76 +0,0 @@ -{{- /* -This job has a part to play in a helm upgrade process. It simply waits for the -hook-image-puller daemonset which is started slightly before this job to get -its' pods running. If all those pods are running they must have pulled all the -required images on all nodes as they are used as init containers with a dummy -command. -*/}} -{{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) -}} -apiVersion: batch/v1 -kind: Job -metadata: - name: {{ include "jupyterhub.hook-image-awaiter.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} - hub.jupyter.org/deletable: "true" - annotations: - "helm.sh/hook": pre-install,pre-upgrade - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook-weight": "10" -spec: - template: - # The hook-image-awaiter Job and hook-image-puller DaemonSet was - # conditionally created based on this state: - # - {{- include "jupyterhub.imagePuller.daemonset.hook.install" . | nindent 4 }} - # - metadata: - labels: - {{- /* Changes here will cause the Job to restart the pods. */}} - {{- include "jupyterhub.matchLabels" . | nindent 8 }} - {{- with .Values.prePuller.labels }} - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- with .Values.prePuller.annotations }} - annotations: - {{- . | toYaml | nindent 8 }} - {{- end }} - spec: - restartPolicy: Never - {{- with include "jupyterhub.hook-image-awaiter-serviceaccount.fullname" . }} - serviceAccountName: {{ . }} - {{- end }} - {{- with .Values.prePuller.hook.nodeSelector }} - nodeSelector: - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- with concat .Values.scheduling.corePods.tolerations .Values.prePuller.hook.tolerations }} - tolerations: - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- with include "jupyterhub.imagePullSecrets" (dict "root" . "image" .Values.prePuller.hook.image) }} - imagePullSecrets: {{ . }} - {{- end }} - containers: - - image: {{ .Values.prePuller.hook.image.name }}:{{ .Values.prePuller.hook.image.tag }} - name: {{ include "jupyterhub.hook-image-awaiter.fullname" . }} - {{- with .Values.prePuller.hook.image.pullPolicy }} - imagePullPolicy: {{ . }} - {{- end }} - command: - - /image-awaiter - - -ca-path=/var/run/secrets/kubernetes.io/serviceaccount/ca.crt - - -auth-token-path=/var/run/secrets/kubernetes.io/serviceaccount/token - - -api-server-address=https://kubernetes.default.svc:$(KUBERNETES_SERVICE_PORT) - - -namespace={{ .Release.Namespace }} - - -daemonset={{ include "jupyterhub.hook-image-puller.fullname" . }} - - -pod-scheduling-wait-duration={{ .Values.prePuller.hook.podSchedulingWaitDuration }} - {{- with .Values.prePuller.hook.containerSecurityContext }} - securityContext: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- with .Values.prePuller.hook.resources }} - resources: - {{- . | toYaml | nindent 12 }} - {{- end }} -{{- end }} diff --git a/jupyterhub/templates/image-puller/priorityclass.yaml b/jupyterhub/templates/image-puller/priorityclass.yaml deleted file mode 100644 index b2dbae0..0000000 --- a/jupyterhub/templates/image-puller/priorityclass.yaml +++ /dev/null @@ -1,18 +0,0 @@ -{{- if .Values.scheduling.podPriority.enabled }} -{{- if or .Values.prePuller.hook.enabled .Values.prePuller.continuous.enabled -}} -apiVersion: scheduling.k8s.io/v1 -kind: PriorityClass -metadata: - name: {{ include "jupyterhub.image-puller-priority.fullname" . }} - annotations: - meta.helm.sh/release-name: "{{ .Release.Name }}" - meta.helm.sh/release-namespace: "{{ .Release.Namespace }}" - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -value: {{ .Values.scheduling.podPriority.imagePullerPriority }} -globalDefault: false -description: >- - Enables [hook|continuous]-image-puller pods to fit on nodes even though they - are clogged by user-placeholder pods, while not evicting normal user pods. -{{- end }} -{{- end }} diff --git a/jupyterhub/templates/image-puller/rbac.yaml b/jupyterhub/templates/image-puller/rbac.yaml deleted file mode 100644 index 996a59a..0000000 --- a/jupyterhub/templates/image-puller/rbac.yaml +++ /dev/null @@ -1,45 +0,0 @@ -{{- /* -Permissions to be used by the hook-image-awaiter job -*/}} -{{- if .Values.rbac.create -}} -{{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) -}} -kind: Role -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: {{ include "jupyterhub.hook-image-awaiter.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} - hub.jupyter.org/deletable: "true" - annotations: - "helm.sh/hook": pre-install,pre-upgrade - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook-weight": "0" -rules: - - apiGroups: ["apps"] # "" indicates the core API group - resources: ["daemonsets"] - verbs: ["get"] ---- -{{- /* -... as declared by this binding. -*/}} -kind: RoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: {{ include "jupyterhub.hook-image-awaiter.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} - hub.jupyter.org/deletable: "true" - annotations: - "helm.sh/hook": pre-install,pre-upgrade - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook-weight": "0" -subjects: - - kind: ServiceAccount - name: {{ include "jupyterhub.hook-image-awaiter-serviceaccount.fullname" . }} - namespace: "{{ .Release.Namespace }}" -roleRef: - kind: Role - name: {{ include "jupyterhub.hook-image-awaiter.fullname" . }} - apiGroup: rbac.authorization.k8s.io -{{- end }} -{{- end }} diff --git a/jupyterhub/templates/image-puller/serviceaccount.yaml b/jupyterhub/templates/image-puller/serviceaccount.yaml deleted file mode 100644 index 8161101..0000000 --- a/jupyterhub/templates/image-puller/serviceaccount.yaml +++ /dev/null @@ -1,21 +0,0 @@ -{{- /* -ServiceAccount for the pre-puller hook's image-awaiter-job -*/}} -{{- if .Values.prePuller.hook.serviceAccount.create -}} -{{- if (include "jupyterhub.imagePuller.daemonset.hook.install" .) -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "jupyterhub.hook-image-awaiter-serviceaccount.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} - hub.jupyter.org/deletable: "true" - annotations: - "helm.sh/hook": pre-install,pre-upgrade - "helm.sh/hook-delete-policy": before-hook-creation,hook-succeeded - "helm.sh/hook-weight": "0" - {{- with .Values.prePuller.hook.serviceAccount.annotations }} - {{- . | toYaml | nindent 4 }} - {{- end }} -{{- end }} -{{- end }} diff --git a/jupyterhub/templates/ingress.yaml b/jupyterhub/templates/ingress.yaml deleted file mode 100644 index 91f96f4..0000000 --- a/jupyterhub/templates/ingress.yaml +++ /dev/null @@ -1,35 +0,0 @@ -{{- if .Values.ingress.enabled -}} -apiVersion: networking.k8s.io/v1 -kind: Ingress -metadata: - name: {{ include "jupyterhub.ingress.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} - {{- with .Values.ingress.annotations }} - annotations: - {{- . | toYaml | nindent 4 }} - {{- end }} -spec: - {{- with .Values.ingress.ingressClassName }} - ingressClassName: "{{ . }}" - {{- end }} - rules: - {{- range $host := .Values.ingress.hosts | default (list "") }} - - http: - paths: - - path: {{ $.Values.hub.baseUrl | trimSuffix "/" }}/{{ $.Values.ingress.pathSuffix }} - pathType: {{ $.Values.ingress.pathType }} - backend: - service: - name: {{ include "jupyterhub.proxy-public.fullname" $ }} - port: - name: http - {{- if $host }} - host: {{ $host | quote }} - {{- end }} - {{- end }} - {{- with .Values.ingress.tls }} - tls: - {{- . | toYaml | nindent 4 }} - {{- end }} -{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/_README.txt b/jupyterhub/templates/proxy/autohttps/_README.txt deleted file mode 100644 index eaf1c5c..0000000 --- a/jupyterhub/templates/proxy/autohttps/_README.txt +++ /dev/null @@ -1,9 +0,0 @@ -# Automatic HTTPS Terminator - -This directory has Kubernetes objects for automatic Let's Encrypt Support. -When enabled, we create a new deployment object that has an nginx-ingress -and kube-lego container in it. This is responsible for requesting, -storing and renewing certificates as needed from Let's Encrypt. - -The only change required outside of this directory is in the `proxy-public` -service, which targets different hubs based on automatic HTTPS status. diff --git a/jupyterhub/templates/proxy/autohttps/_configmap-dynamic.yaml b/jupyterhub/templates/proxy/autohttps/_configmap-dynamic.yaml deleted file mode 100644 index 0e2a8f4..0000000 --- a/jupyterhub/templates/proxy/autohttps/_configmap-dynamic.yaml +++ /dev/null @@ -1,109 +0,0 @@ -{{- define "jupyterhub.dynamic.yaml" -}} -# Content of dynamic.yaml to be merged merged with -# proxy.traefik.extraDynamicConfig. -# ---------------------------------------------------------------------------- -http: - # Middlewares tweaks requests. We define them here and reference them in - # our routers. We use them to redirect http traffic and headers to proxied - # web requests. - # - # ref: https://docs.traefik.io/middlewares/overview/ - middlewares: - hsts: - # A middleware to add a HTTP Strict-Transport-Security (HSTS) response - # header, they function as a request for browsers to enforce HTTPS on - # their end in for a given time into the future, and optionally - # subdomains for requests to subdomains as well. - # - # ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security - headers: - stsIncludeSubdomains: {{ .Values.proxy.traefik.hsts.includeSubdomains }} - stsPreload: {{ .Values.proxy.traefik.hsts.preload }} - stsSeconds: {{ .Values.proxy.traefik.hsts.maxAge }} - # A middleware to redirect to https - redirect: - redirectScheme: - permanent: true - scheme: https - # A middleware to add a X-Scheme (X-Forwarded-Proto) header that - # JupyterHub's Tornado web-server needs if expecting to serve https - # traffic. Without it we would run into issues like: - # https://github.com/jupyterhub/jupyterhub/issues/2284 - scheme: - headers: - customRequestHeaders: - # DISCUSS ME: Can we use the X-Forwarded-Proto header instead? It - # seems more recognized. Mozilla calls it the de-facto standard - # header for this purpose, and Tornado recognizes both. - # - # ref: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Forwarded-Proto - # ref: https://www.tornadoweb.org/en/stable/httpserver.html#http-server - X-Scheme: https - - # Routers routes web requests to a service and optionally tweaks them with - # middleware. - # - # ref: https://docs.traefik.io/routing/routers/ - routers: - # Route secure https traffic to the configurable-http-proxy managed by - # JupyterHub. - default: - entrypoints: - - "https" - middlewares: - - "hsts" - - "scheme" - rule: PathPrefix(`/`) - service: default - # Use our predefined TLS options and certificate resolver, enabling - # this route to act as a TLS termination proxy with high security - # standards. - tls: - certResolver: default - domains: - {{- range $host := .Values.proxy.https.hosts }} - - main: {{ $host }} - {{- end }} - options: default - - # Route insecure http traffic to https - insecure: - entrypoints: - - "http" - middlewares: - - "redirect" - rule: PathPrefix(`/`) - service: default - - # Services represents the destinations we route traffic to. - # - # ref: https://docs.traefik.io/routing/services/ - services: - # Represents the configurable-http-proxy (chp) server that is managed by - # JupyterHub to route traffic both to itself and to user pods. - default: - loadBalancer: - servers: - - url: 'http://proxy-http:8000/' - -# Configure TLS to give us an A+ in the ssllabs.com test -# -# ref: https://www.ssllabs.com/ssltest/ -tls: - options: - default: - # Allowed ciphers adapted from Mozillas SSL Configuration Generator - # configured for Intermediate support which doesn't support very old - # systems but doesn't require very modern either. - # - # ref: https://ssl-config.mozilla.org/#server=traefik&version=2.1.2&config=intermediate&guideline=5.4 - cipherSuites: - - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 - - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 - - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 - - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 - - TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 - - TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 - minVersion: VersionTLS12 - sniStrict: true -{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/_configmap-traefik.yaml b/jupyterhub/templates/proxy/autohttps/_configmap-traefik.yaml deleted file mode 100644 index 7287a70..0000000 --- a/jupyterhub/templates/proxy/autohttps/_configmap-traefik.yaml +++ /dev/null @@ -1,68 +0,0 @@ -{{- define "jupyterhub.traefik.yaml" -}} -# Content of traefik.yaml to be merged merged with -# proxy.traefik.extraStaticConfig. -# ---------------------------------------------------------------------------- - -# Config of logs about web requests -# -# ref: https://docs.traefik.io/observability/access-logs/ -accessLog: - # Redact commonly sensitive headers - fields: - headers: - names: - Authorization: redacted - Cookie: redacted - Set-Cookie: redacted - X-Xsrftoken: redacted - # Only log errors - filters: - statusCodes: - - 500-599 - -# Automatically acquire certificates certificates form a Certificate -# Authority (CA) like Let's Encrypt using the ACME protocol's HTTP-01 -# challenge. -# -# ref: https://docs.traefik.io/https/acme/#certificate-resolvers -certificatesResolvers: - default: - acme: - caServer: {{ .Values.proxy.https.letsencrypt.acmeServer }} - email: {{ .Values.proxy.https.letsencrypt.contactEmail }} - httpChallenge: - entryPoint: http - storage: /etc/acme/acme.json - -# Let Traefik listen to port 80 and port 443 -# -# ref: https://docs.traefik.io/routing/entrypoints/ -entryPoints: - # Port 80, used for: - # - ACME HTTP-01 challenges - # - Redirects to HTTPS - http: - address: ':8080' - # Port 443, used for: - # - TLS Termination Proxy, where HTTPS transitions to HTTP. - https: - address: ':8443' - # Configure a high idle timeout for our websockets connections - transport: - respondingTimeouts: - idleTimeout: 10m0s - -# Config of logs about what happens to Traefik itself (startup, -# configuration, events, shutdown, and so on). -# -# ref: https://docs.traefik.io/observability/logs -log: - level: {{ if .Values.debug.enabled -}} DEBUG {{- else -}} WARN {{- end }} - -# Let Traefik monitor another file we mount for dynamic configuration. As we -# mount this file through this configmap, we can make a `kubectl edit` on the -# configmap and have Traefik update on changes to dynamic.yaml. -providers: - file: - filename: /etc/traefik/dynamic.yaml -{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/configmap.yaml b/jupyterhub/templates/proxy/autohttps/configmap.yaml deleted file mode 100644 index 4804bf7..0000000 --- a/jupyterhub/templates/proxy/autohttps/configmap.yaml +++ /dev/null @@ -1,28 +0,0 @@ -{{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) }} -{{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) }} -{{- if $autoHTTPS -}} -{{- $_ := .Values.proxy.https.letsencrypt.contactEmail | required "proxy.https.letsencrypt.contactEmail is a required field" -}} - -# This configmap contains Traefik configuration files to be mounted. -# - traefik.yaml will only be read during startup (static configuration) -# - dynamic.yaml will be read on change (dynamic configuration) -# -# ref: https://docs.traefik.io/getting-started/configuration-overview/ -# -# The configuration files are first rendered with Helm templating to large YAML -# strings. Then we use the fromYAML function on these strings to get an object, -# that we in turn merge with user provided extra configuration. -# -kind: ConfigMap -apiVersion: v1 -metadata: - name: {{ include "jupyterhub.autohttps.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -data: - traefik.yaml: | - {{- include "jupyterhub.traefik.yaml" . | fromYaml | merge .Values.proxy.traefik.extraStaticConfig | toYaml | nindent 4 }} - dynamic.yaml: | - {{- include "jupyterhub.dynamic.yaml" . | fromYaml | merge .Values.proxy.traefik.extraDynamicConfig | toYaml | nindent 4 }} - -{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/deployment.yaml b/jupyterhub/templates/proxy/autohttps/deployment.yaml deleted file mode 100644 index f76f3ef..0000000 --- a/jupyterhub/templates/proxy/autohttps/deployment.yaml +++ /dev/null @@ -1,154 +0,0 @@ -{{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) }} -{{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) }} -{{- if $autoHTTPS -}} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "jupyterhub.autohttps.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -spec: - {{- if typeIs "int" .Values.proxy.traefik.revisionHistoryLimit }} - revisionHistoryLimit: {{ .Values.proxy.traefik.revisionHistoryLimit }} - {{- end }} - replicas: 1 - selector: - matchLabels: - {{- include "jupyterhub.matchLabels" . | nindent 6 }} - template: - metadata: - labels: - {{- include "jupyterhub.matchLabels" . | nindent 8 }} - hub.jupyter.org/network-access-proxy-http: "true" - {{- with .Values.proxy.traefik.labels }} - {{- . | toYaml | nindent 8 }} - {{- end }} - annotations: - # Only force a restart through a change to this checksum when the static - # configuration is changed, as the dynamic can be updated after start. - # Any disruptions to this deployment impacts everything, it is the - # entrypoint of all network traffic. - checksum/static-config: {{ include "jupyterhub.traefik.yaml" . | fromYaml | merge .Values.proxy.traefik.extraStaticConfig | toYaml | sha256sum }} - spec: - {{- with include "jupyterhub.autohttps-serviceaccount.fullname" . }} - serviceAccountName: {{ . }} - {{- end }} - {{- if .Values.scheduling.podPriority.enabled }} - priorityClassName: {{ include "jupyterhub.priority.fullname" . }} - {{- end }} - {{- with .Values.proxy.traefik.nodeSelector }} - nodeSelector: - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- with concat .Values.scheduling.corePods.tolerations .Values.proxy.traefik.tolerations }} - tolerations: - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- include "jupyterhub.coreAffinity" . | nindent 6 }} - volumes: - - name: certificates - emptyDir: {} - - name: traefik-config - configMap: - name: {{ include "jupyterhub.autohttps.fullname" . }} - {{- with .Values.proxy.traefik.extraVolumes }} - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- with include "jupyterhub.imagePullSecrets" (dict "root" . "image" .Values.proxy.traefik.image) }} - imagePullSecrets: {{ . }} - {{- end }} - initContainers: - - name: load-acme - image: "{{ .Values.proxy.secretSync.image.name }}:{{ .Values.proxy.secretSync.image.tag }}" - {{- with .Values.proxy.secretSync.image.pullPolicy }} - imagePullPolicy: {{ . }} - {{- end }} - args: - - load - - {{ include "jupyterhub.proxy-public-tls.fullname" . }} - - acme.json - - /etc/acme/acme.json - env: - # We need this to get logs immediately - - name: PYTHONUNBUFFERED - value: "True" - {{- with .Values.proxy.traefik.extraEnv }} - {{- include "jupyterhub.extraEnv" . | nindent 12 }} - {{- end }} - volumeMounts: - - name: certificates - mountPath: /etc/acme - {{- with .Values.proxy.secretSync.containerSecurityContext }} - securityContext: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- with .Values.proxy.traefik.extraInitContainers }} - {{- . | toYaml | nindent 8 }} - {{- end }} - containers: - - name: traefik - image: "{{ .Values.proxy.traefik.image.name }}:{{ .Values.proxy.traefik.image.tag }}" - {{- with .Values.proxy.traefik.image.pullPolicy }} - imagePullPolicy: {{ . }} - {{- end }} - {{- with .Values.proxy.traefik.resources }} - resources: - {{- . | toYaml | nindent 12 }} - {{- end }} - ports: - - name: http - containerPort: 8080 - - name: https - containerPort: 8443 - {{- with .Values.proxy.traefik.extraPorts }} - {{- . | toYaml | nindent 12 }} - {{- end }} - volumeMounts: - - name: traefik-config - mountPath: /etc/traefik - - name: certificates - mountPath: /etc/acme - {{- with .Values.proxy.traefik.extraVolumeMounts }} - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- with .Values.proxy.traefik.extraEnv }} - env: - {{- include "jupyterhub.extraEnv" . | nindent 12 }} - {{- end }} - {{- with .Values.proxy.traefik.containerSecurityContext }} - securityContext: - {{- . | toYaml | nindent 12 }} - {{- end }} - - name: secret-sync - image: "{{ .Values.proxy.secretSync.image.name }}:{{ .Values.proxy.secretSync.image.tag }}" - {{- with .Values.proxy.secretSync.image.pullPolicy }} - imagePullPolicy: {{ . }} - {{- end }} - {{- with .Values.proxy.secretSync.resources }} - resources: - {{- . | toYaml | nindent 12 }} - {{- end }} - args: - - watch-save - - --label=app={{ include "jupyterhub.appLabel" . }} - - --label=release={{ .Release.Name }} - - --label=chart={{ .Chart.Name }}-{{ .Chart.Version | replace "+" "_" }} - - --label=heritage=secret-sync - - {{ include "jupyterhub.proxy-public-tls.fullname" . }} - - acme.json - - /etc/acme/acme.json - env: - # We need this to get logs immediately - - name: PYTHONUNBUFFERED - value: "True" - volumeMounts: - - name: certificates - mountPath: /etc/acme - {{- with .Values.proxy.secretSync.containerSecurityContext }} - securityContext: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- with .Values.proxy.traefik.extraPodSpec }} - {{- . | toYaml | nindent 6 }} - {{- end }} -{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/netpol.yaml b/jupyterhub/templates/proxy/autohttps/netpol.yaml deleted file mode 100644 index 78270b6..0000000 --- a/jupyterhub/templates/proxy/autohttps/netpol.yaml +++ /dev/null @@ -1,78 +0,0 @@ -{{- $HTTPS := .Values.proxy.https.enabled -}} -{{- $autoHTTPS := and $HTTPS (and (eq .Values.proxy.https.type "letsencrypt") .Values.proxy.https.hosts) -}} -{{- if and $autoHTTPS .Values.proxy.traefik.networkPolicy.enabled -}} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: {{ include "jupyterhub.autohttps.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -spec: - podSelector: - matchLabels: - {{- include "jupyterhub.matchLabels" . | nindent 6 }} - policyTypes: - - Ingress - - Egress - - # IMPORTANT: - # NetworkPolicy's ingress "from" and egress "to" rule specifications require - # great attention to detail. A quick summary is: - # - # 1. You can provide "from"/"to" rules that provide access either ports or a - # subset of ports. - # 2. You can for each "from"/"to" rule provide any number of - # "sources"/"destinations" of four different kinds. - # - podSelector - targets pods with a certain label in the same namespace as the NetworkPolicy - # - namespaceSelector - targets all pods running in namespaces with a certain label - # - namespaceSelector and podSelector - targets pods with a certain label running in namespaces with a certain label - # - ipBlock - targets network traffic from/to a set of IP address ranges - # - # Read more at: https://kubernetes.io/docs/concepts/services-networking/network-policies/#behavior-of-to-and-from-selectors - # - ingress: - {{- with .Values.proxy.traefik.networkPolicy.allowedIngressPorts }} - # allow incoming traffic to these ports independent of source - - ports: - {{- range $port := . }} - - port: {{ $port }} - {{- end }} - {{- end }} - - # allowed pods (hub.jupyter.org/network-access-proxy-http) --> proxy (http/https port) - - ports: - - port: http - - port: https - from: - # source 1 - labeled pods - - podSelector: - matchLabels: - hub.jupyter.org/network-access-proxy-http: "true" - {{- if eq .Values.proxy.traefik.networkPolicy.interNamespaceAccessLabels "accept" }} - namespaceSelector: - matchLabels: {} # without this, the podSelector would only consider pods in the local namespace - # source 2 - pods in labeled namespaces - - namespaceSelector: - matchLabels: - hub.jupyter.org/network-access-proxy-http: "true" - {{- end }} - - {{- with .Values.proxy.traefik.networkPolicy.ingress}} - # depends, but default is nothing --> proxy - {{- . | toYaml | nindent 4 }} - {{- end }} - - egress: - # autohttps --> proxy (http port) - - to: - - podSelector: - matchLabels: - {{- $_ := merge (dict "componentLabel" "proxy") . }} - {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} - ports: - - port: 8000 - - {{- with (include "jupyterhub.networkPolicy.renderEgressRules" (list . .Values.proxy.traefik.networkPolicy)) }} - {{- . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/pdb.yaml b/jupyterhub/templates/proxy/autohttps/pdb.yaml deleted file mode 100644 index 074d0ac..0000000 --- a/jupyterhub/templates/proxy/autohttps/pdb.yaml +++ /dev/null @@ -1,23 +0,0 @@ -{{- if .Values.proxy.traefik.pdb.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} -{{- /* k8s 1.21+ required */ -}} -apiVersion: policy/v1 -{{- else }} -apiVersion: policy/v1beta1 -{{- end }} -kind: PodDisruptionBudget -metadata: - name: proxy - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -spec: - {{- if not (typeIs "" .Values.proxy.traefik.pdb.maxUnavailable) }} - maxUnavailable: {{ .Values.proxy.traefik.pdb.maxUnavailable }} - {{- end }} - {{- if not (typeIs "" .Values.proxy.traefik.pdb.minAvailable) }} - minAvailable: {{ .Values.proxy.traefik.pdb.minAvailable }} - {{- end }} - selector: - matchLabels: - {{- include "jupyterhub.matchLabels" . | nindent 6 }} -{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/rbac.yaml b/jupyterhub/templates/proxy/autohttps/rbac.yaml deleted file mode 100644 index a0fd41a..0000000 --- a/jupyterhub/templates/proxy/autohttps/rbac.yaml +++ /dev/null @@ -1,35 +0,0 @@ -{{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) -}} -{{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) -}} -{{- if $autoHTTPS -}} -{{- if .Values.rbac.create -}} -apiVersion: rbac.authorization.k8s.io/v1 -kind: Role -metadata: - name: {{ include "jupyterhub.autohttps.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} - {{- with .Values.proxy.traefik.serviceAccount.annotations }} - annotations: - {{- . | toYaml | nindent 4 }} - {{- end }} -rules: -- apiGroups: [""] - resources: ["secrets"] - verbs: ["get", "patch", "list", "create"] ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: RoleBinding -metadata: - name: {{ include "jupyterhub.autohttps.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -subjects: -- kind: ServiceAccount - name: {{ include "jupyterhub.autohttps-serviceaccount.fullname" . }} - apiGroup: -roleRef: - kind: Role - name: {{ include "jupyterhub.autohttps.fullname" . }} - apiGroup: rbac.authorization.k8s.io -{{- end }} -{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/service.yaml b/jupyterhub/templates/proxy/autohttps/service.yaml deleted file mode 100644 index 615e36d..0000000 --- a/jupyterhub/templates/proxy/autohttps/service.yaml +++ /dev/null @@ -1,25 +0,0 @@ -{{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) }} -{{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) }} -{{- if $autoHTTPS -}} -apiVersion: v1 -kind: Service -metadata: - name: {{ include "jupyterhub.proxy-http.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} - {{- with .Values.proxy.service.labels }} - {{- . | toYaml | nindent 4 }} - {{- end }} - {{- with .Values.proxy.service.annotations }} - annotations: - {{- . | toYaml | nindent 4 }} - {{- end }} -spec: - type: ClusterIP - selector: - {{- $_ := merge (dict "componentLabel" "proxy") . }} - {{- include "jupyterhub.matchLabels" $_ | nindent 4 }} - ports: - - port: 8000 - targetPort: http -{{- end }} diff --git a/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml b/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml deleted file mode 100644 index 5b340bb..0000000 --- a/jupyterhub/templates/proxy/autohttps/serviceaccount.yaml +++ /dev/null @@ -1,12 +0,0 @@ -{{- $HTTPS := (and .Values.proxy.https.hosts .Values.proxy.https.enabled) -}} -{{- $autoHTTPS := (and $HTTPS (eq .Values.proxy.https.type "letsencrypt")) -}} -{{- if $autoHTTPS -}} -{{- if .Values.proxy.traefik.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "jupyterhub.autohttps-serviceaccount.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -{{- end }} -{{- end }} diff --git a/jupyterhub/templates/proxy/deployment.yaml b/jupyterhub/templates/proxy/deployment.yaml deleted file mode 100644 index 2b35382..0000000 --- a/jupyterhub/templates/proxy/deployment.yaml +++ /dev/null @@ -1,178 +0,0 @@ -{{- $manualHTTPS := and .Values.proxy.https.enabled (eq .Values.proxy.https.type "manual") -}} -{{- $manualHTTPSwithsecret := and .Values.proxy.https.enabled (eq .Values.proxy.https.type "secret") -}} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "jupyterhub.proxy.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -spec: - {{- if typeIs "int" .Values.proxy.chp.revisionHistoryLimit }} - revisionHistoryLimit: {{ .Values.proxy.chp.revisionHistoryLimit }} - {{- end }} - replicas: 1 - selector: - matchLabels: - {{- include "jupyterhub.matchLabels" . | nindent 6 }} - strategy: - {{- .Values.proxy.deploymentStrategy | toYaml | nindent 4 }} - template: - metadata: - labels: - {{- /* Changes here will cause the Deployment to restart the pods. */}} - {{- include "jupyterhub.matchLabels" . | nindent 8 }} - hub.jupyter.org/network-access-hub: "true" - hub.jupyter.org/network-access-singleuser: "true" - {{- with .Values.proxy.labels }} - {{- . | toYaml | nindent 8 }} - {{- end }} - annotations: - # We want to restart proxy only if the auth token changes - # Other changes to the hub config should not restart. - # We truncate to 4 chars to avoid leaking auth token info, - # since someone could brute force the hash to obtain the token - # - # Note that if auth_token has to be generated at random, it will be - # generated at random here separately from being generated at random in - # the k8s Secret template. This will cause this annotation to change to - # match the k8s Secret during the first upgrade following an auth_token - # was generated. - checksum/auth-token: {{ include "jupyterhub.hub.config.ConfigurableHTTPProxy.auth_token" . | sha256sum | trunc 4 | quote }} - checksum/proxy-secret: {{ include (print $.Template.BasePath "/proxy/secret.yaml") . | sha256sum | quote }} - {{- with .Values.proxy.annotations }} - {{- . | toYaml | nindent 8 }} - {{- end }} - spec: - terminationGracePeriodSeconds: 60 - {{- if .Values.scheduling.podPriority.enabled }} - priorityClassName: {{ include "jupyterhub.priority.fullname" . }} - {{- end }} - {{- with .Values.proxy.chp.nodeSelector }} - nodeSelector: - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- with concat .Values.scheduling.corePods.tolerations .Values.proxy.chp.tolerations }} - tolerations: - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- include "jupyterhub.coreAffinity" . | nindent 6 }} - {{- if $manualHTTPS }} - volumes: - - name: tls-secret - secret: - secretName: {{ include "jupyterhub.proxy-public-manual-tls.fullname" . }} - {{- else if $manualHTTPSwithsecret }} - volumes: - - name: tls-secret - secret: - secretName: {{ .Values.proxy.https.secret.name }} - {{- end }} - {{- with include "jupyterhub.imagePullSecrets" (dict "root" . "image" .Values.proxy.chp.image) }} - imagePullSecrets: {{ . }} - {{- end }} - containers: - - name: chp - image: {{ .Values.proxy.chp.image.name }}:{{ .Values.proxy.chp.image.tag }} - {{- $hubNameAsEnv := include "jupyterhub.hub.fullname" . | upper | replace "-" "_" }} - {{- $hubHost := printf "http://%s:$(%s_SERVICE_PORT)" (include "jupyterhub.hub.fullname" .) $hubNameAsEnv }} - command: - - configurable-http-proxy - - "--ip=" - - "--api-ip=" - - --api-port=8001 - - --default-target={{ .Values.proxy.chp.defaultTarget | default $hubHost }} - - --error-target={{ .Values.proxy.chp.errorTarget | default (printf "%s/hub/error" $hubHost) }} - {{- if $manualHTTPS }} - - --port=8443 - - --redirect-port=8000 - - --redirect-to=443 - - --ssl-key=/etc/chp/tls/tls.key - - --ssl-cert=/etc/chp/tls/tls.crt - {{- else if $manualHTTPSwithsecret }} - - --port=8443 - - --redirect-port=8000 - - --redirect-to=443 - - --ssl-key=/etc/chp/tls/{{ .Values.proxy.https.secret.key }} - - --ssl-cert=/etc/chp/tls/{{ .Values.proxy.https.secret.crt }} - {{- else }} - - --port=8000 - {{- end }} - {{- if .Values.debug.enabled }} - - --log-level=debug - {{- end }} - {{- range .Values.proxy.chp.extraCommandLineFlags }} - - {{ tpl . $ }} - {{- end }} - {{- if or $manualHTTPS $manualHTTPSwithsecret }} - volumeMounts: - - name: tls-secret - mountPath: /etc/chp/tls - readOnly: true - {{- end }} - {{- with .Values.proxy.chp.resources }} - resources: - {{- . | toYaml | nindent 12 }} - {{- end }} - env: - - name: CONFIGPROXY_AUTH_TOKEN - valueFrom: - secretKeyRef: - # NOTE: References the chart managed k8s Secret even if - # hub.existingSecret is specified to avoid using the - # lookup function on the user managed k8s Secret. - name: {{ include "jupyterhub.hub.fullname" . }} - key: hub.config.ConfigurableHTTPProxy.auth_token - {{- with .Values.proxy.chp.extraEnv }} - {{- include "jupyterhub.extraEnv" . | nindent 12 }} - {{- end }} - {{- with .Values.proxy.chp.image.pullPolicy }} - imagePullPolicy: {{ . }} - {{- end }} - ports: - {{- if or $manualHTTPS $manualHTTPSwithsecret }} - - name: https - containerPort: 8443 - {{- end }} - - name: http - containerPort: 8000 - - name: api - containerPort: 8001 - {{- if .Values.proxy.chp.livenessProbe.enabled }} - livenessProbe: - initialDelaySeconds: {{ .Values.proxy.chp.livenessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.proxy.chp.livenessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.proxy.chp.livenessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.proxy.chp.livenessProbe.failureThreshold }} - httpGet: - path: /_chp_healthz - {{- if or $manualHTTPS $manualHTTPSwithsecret }} - port: https - scheme: HTTPS - {{- else }} - port: http - scheme: HTTP - {{- end }} - {{- end }} - {{- if .Values.proxy.chp.readinessProbe.enabled }} - readinessProbe: - initialDelaySeconds: {{ .Values.proxy.chp.readinessProbe.initialDelaySeconds }} - periodSeconds: {{ .Values.proxy.chp.readinessProbe.periodSeconds }} - timeoutSeconds: {{ .Values.proxy.chp.readinessProbe.timeoutSeconds }} - failureThreshold: {{ .Values.proxy.chp.readinessProbe.failureThreshold }} - httpGet: - path: /_chp_healthz - {{- if or $manualHTTPS $manualHTTPSwithsecret }} - port: https - scheme: HTTPS - {{- else }} - port: http - scheme: HTTP - {{- end }} - {{- end }} - {{- with .Values.proxy.chp.containerSecurityContext }} - securityContext: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- with .Values.proxy.chp.extraPodSpec }} - {{- . | toYaml | nindent 6 }} - {{- end }} diff --git a/jupyterhub/templates/proxy/netpol.yaml b/jupyterhub/templates/proxy/netpol.yaml deleted file mode 100644 index 0af853a..0000000 --- a/jupyterhub/templates/proxy/netpol.yaml +++ /dev/null @@ -1,108 +0,0 @@ -{{- $HTTPS := .Values.proxy.https.enabled -}} -{{- $autoHTTPS := and $HTTPS (and (eq .Values.proxy.https.type "letsencrypt") .Values.proxy.https.hosts) -}} -{{- $manualHTTPS := and $HTTPS (eq .Values.proxy.https.type "manual") -}} -{{- $manualHTTPSwithsecret := and $HTTPS (eq .Values.proxy.https.type "secret") -}} -{{- if .Values.proxy.chp.networkPolicy.enabled -}} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: {{ include "jupyterhub.proxy.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -spec: - podSelector: - matchLabels: - {{- include "jupyterhub.matchLabels" . | nindent 6 }} - policyTypes: - - Ingress - - Egress - - # IMPORTANT: - # NetworkPolicy's ingress "from" and egress "to" rule specifications require - # great attention to detail. A quick summary is: - # - # 1. You can provide "from"/"to" rules that provide access either ports or a - # subset of ports. - # 2. You can for each "from"/"to" rule provide any number of - # "sources"/"destinations" of four different kinds. - # - podSelector - targets pods with a certain label in the same namespace as the NetworkPolicy - # - namespaceSelector - targets all pods running in namespaces with a certain label - # - namespaceSelector and podSelector - targets pods with a certain label running in namespaces with a certain label - # - ipBlock - targets network traffic from/to a set of IP address ranges - # - # Read more at: https://kubernetes.io/docs/concepts/services-networking/network-policies/#behavior-of-to-and-from-selectors - # - ingress: - {{- with .Values.proxy.chp.networkPolicy.allowedIngressPorts }} - # allow incoming traffic to these ports independent of source - - ports: - {{- range $port := . }} - - port: {{ $port }} - {{- end }} - {{- end }} - - # allowed pods (hub.jupyter.org/network-access-proxy-http) --> proxy (http/https port) - - ports: - - port: http - {{- if or $manualHTTPS $manualHTTPSwithsecret }} - - port: https - {{- end }} - from: - # source 1 - labeled pods - - podSelector: - matchLabels: - hub.jupyter.org/network-access-proxy-http: "true" - {{- if eq .Values.proxy.chp.networkPolicy.interNamespaceAccessLabels "accept" }} - namespaceSelector: - matchLabels: {} # without this, the podSelector would only consider pods in the local namespace - # source 2 - pods in labeled namespaces - - namespaceSelector: - matchLabels: - hub.jupyter.org/network-access-proxy-http: "true" - {{- end }} - - # allowed pods (hub.jupyter.org/network-access-proxy-api) --> proxy (api port) - - ports: - - port: api - from: - # source 1 - labeled pods - - podSelector: - matchLabels: - hub.jupyter.org/network-access-proxy-api: "true" - {{- if eq .Values.proxy.chp.networkPolicy.interNamespaceAccessLabels "accept" }} - namespaceSelector: - matchLabels: {} # without this, the podSelector would only consider pods in the local namespace - # source 2 - pods in labeled namespaces - - namespaceSelector: - matchLabels: - hub.jupyter.org/network-access-proxy-api: "true" - {{- end }} - - {{- with .Values.proxy.chp.networkPolicy.ingress}} - # depends, but default is nothing --> proxy - {{- . | toYaml | nindent 4 }} - {{- end }} - - egress: - # proxy --> hub - - to: - - podSelector: - matchLabels: - {{- $_ := merge (dict "componentLabel" "hub") . }} - {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} - ports: - - port: 8081 - - # proxy --> singleuser-server - - to: - - podSelector: - matchLabels: - {{- $_ := merge (dict "componentLabel" "singleuser-server") . }} - {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} - ports: - - port: 8888 - - {{- with (include "jupyterhub.networkPolicy.renderEgressRules" (list . .Values.proxy.chp.networkPolicy)) }} - {{- . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/jupyterhub/templates/proxy/pdb.yaml b/jupyterhub/templates/proxy/pdb.yaml deleted file mode 100644 index d8651f5..0000000 --- a/jupyterhub/templates/proxy/pdb.yaml +++ /dev/null @@ -1,23 +0,0 @@ -{{- if .Values.proxy.chp.pdb.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} -{{- /* k8s 1.21+ required */ -}} -apiVersion: policy/v1 -{{- else }} -apiVersion: policy/v1beta1 -{{- end }} -kind: PodDisruptionBudget -metadata: - name: {{ include "jupyterhub.proxy.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -spec: - {{- if not (typeIs "" .Values.proxy.chp.pdb.maxUnavailable) }} - maxUnavailable: {{ .Values.proxy.chp.pdb.maxUnavailable }} - {{- end }} - {{- if not (typeIs "" .Values.proxy.chp.pdb.minAvailable) }} - minAvailable: {{ .Values.proxy.chp.pdb.minAvailable }} - {{- end }} - selector: - matchLabels: - {{- include "jupyterhub.matchLabels" . | nindent 6 }} -{{- end }} diff --git a/jupyterhub/templates/proxy/secret.yaml b/jupyterhub/templates/proxy/secret.yaml deleted file mode 100644 index d9ff8ad..0000000 --- a/jupyterhub/templates/proxy/secret.yaml +++ /dev/null @@ -1,13 +0,0 @@ -{{- $manualHTTPS := and .Values.proxy.https.enabled (eq .Values.proxy.https.type "manual") -}} -{{- if $manualHTTPS -}} -apiVersion: v1 -kind: Secret -metadata: - name: {{ include "jupyterhub.proxy-public-manual-tls.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -type: kubernetes.io/tls -data: - tls.crt: {{ .Values.proxy.https.manual.cert | required "Required configuration missing: proxy.https.manual.cert" | b64enc }} - tls.key: {{ .Values.proxy.https.manual.key | required "Required configuration missing: proxy.https.manual.key" | b64enc }} -{{- end }} diff --git a/jupyterhub/templates/proxy/service.yaml b/jupyterhub/templates/proxy/service.yaml deleted file mode 100644 index 8a96eb1..0000000 --- a/jupyterhub/templates/proxy/service.yaml +++ /dev/null @@ -1,80 +0,0 @@ -{{- $enabled := .Values.proxy.https.enabled -}} -{{- $autoHTTPS := and $enabled (and (eq .Values.proxy.https.type "letsencrypt") .Values.proxy.https.hosts) -}} -{{- $manualHTTPS := and $enabled (eq .Values.proxy.https.type "manual") -}} -{{- $manualHTTPSwithsecret := and $enabled (eq .Values.proxy.https.type "secret") -}} -{{- $offloadHTTPS := and $enabled (eq .Values.proxy.https.type "offload") -}} -{{- $valid := or $autoHTTPS (or $manualHTTPS (or $manualHTTPSwithsecret $offloadHTTPS)) -}} -{{- $HTTPS := and $enabled $valid -}} -apiVersion: v1 -kind: Service -metadata: - name: {{ include "jupyterhub.proxy-api.fullname" . }} - labels: - {{- $_ := merge (dict "componentSuffix" "-api") . }} - {{- include "jupyterhub.labels" $_ | nindent 4 }} -spec: - selector: - {{- include "jupyterhub.matchLabels" . | nindent 4 }} - ports: - - port: 8001 - targetPort: api ---- -apiVersion: v1 -kind: Service -metadata: - name: {{ include "jupyterhub.proxy-public.fullname" . }} - labels: - {{- $_ := merge (dict "componentSuffix" "-public") . }} - {{- include "jupyterhub.labels" $_ | nindent 4 }} - {{- with .Values.proxy.service.labels }} - {{- . | toYaml | nindent 4 }} - {{- end }} - {{- with .Values.proxy.service.annotations }} - annotations: - {{- . | toYaml | nindent 4 }} - {{- end }} -spec: - selector: - {{- if $autoHTTPS }} - component: autohttps - {{- else }} - component: proxy - {{- end }} - release: {{ .Release.Name }} - ports: - {{- if $HTTPS }} - - name: https - port: 443 - # When HTTPS termination is handled outside our helm chart, pass traffic - # coming in via this Service's port 443 to targeted pod's port meant for - # HTTP traffic. - {{- if $offloadHTTPS }} - targetPort: http - {{- else }} - targetPort: https - {{- end }} - {{- with .Values.proxy.service.nodePorts.https }} - nodePort: {{ . }} - {{- end }} - {{- end }} - {{- if ne .Values.proxy.service.disableHttpPort true }} - - name: http - port: 80 - targetPort: http - {{- with .Values.proxy.service.nodePorts.http }} - nodePort: {{ . }} - {{- end }} - {{- end }} - {{- with .Values.proxy.service.extraPorts }} - {{- . | toYaml | nindent 4 }} - {{- end }} - type: {{ .Values.proxy.service.type }} - {{- with .Values.proxy.service.loadBalancerIP }} - loadBalancerIP: {{ . }} - {{- end }} - {{- if eq .Values.proxy.service.type "LoadBalancer" }} - {{- with .Values.proxy.service.loadBalancerSourceRanges }} - loadBalancerSourceRanges: - {{- . | toYaml | nindent 4 }} - {{- end }} - {{- end }} diff --git a/jupyterhub/templates/scheduling/_scheduling-helpers.tpl b/jupyterhub/templates/scheduling/_scheduling-helpers.tpl deleted file mode 100644 index 0a1a741..0000000 --- a/jupyterhub/templates/scheduling/_scheduling-helpers.tpl +++ /dev/null @@ -1,138 +0,0 @@ -{{- define "jupyterhub.userNodeAffinityRequired" -}} -{{- if eq .Values.scheduling.userPods.nodeAffinity.matchNodePurpose "require" -}} -- matchExpressions: - - key: hub.jupyter.org/node-purpose - operator: In - values: [user] -{{- end }} -{{- with .Values.singleuser.extraNodeAffinity.required }} -{{- . | toYaml | nindent 0 }} -{{- end }} -{{- end }} - -{{- define "jupyterhub.userNodeAffinityPreferred" -}} -{{- if eq .Values.scheduling.userPods.nodeAffinity.matchNodePurpose "prefer" -}} -- weight: 100 - preference: - matchExpressions: - - key: hub.jupyter.org/node-purpose - operator: In - values: [user] -{{- end }} -{{- with .Values.singleuser.extraNodeAffinity.preferred }} -{{- . | toYaml | nindent 0 }} -{{- end }} -{{- end }} - -{{- define "jupyterhub.userPodAffinityRequired" -}} -{{- with .Values.singleuser.extraPodAffinity.required -}} -{{ . | toYaml }} -{{- end }} -{{- end }} - -{{- define "jupyterhub.userPodAffinityPreferred" -}} -{{- with .Values.singleuser.extraPodAffinity.preferred -}} -{{ . | toYaml }} -{{- end }} -{{- end }} - -{{- define "jupyterhub.userPodAntiAffinityRequired" -}} -{{- with .Values.singleuser.extraPodAntiAffinity.required -}} -{{ . | toYaml }} -{{- end }} -{{- end }} - -{{- define "jupyterhub.userPodAntiAffinityPreferred" -}} -{{- with .Values.singleuser.extraPodAntiAffinity.preferred -}} -{{ . | toYaml }} -{{- end }} -{{- end }} - - - -{{- /* - jupyterhub.userAffinity: - It is used by user-placeholder to set the same affinity on them as the - spawned user pods spawned by kubespawner. -*/}} -{{- define "jupyterhub.userAffinity" -}} - -{{- $dummy := set . "nodeAffinityRequired" (include "jupyterhub.userNodeAffinityRequired" .) -}} -{{- $dummy := set . "podAffinityRequired" (include "jupyterhub.userPodAffinityRequired" .) -}} -{{- $dummy := set . "podAntiAffinityRequired" (include "jupyterhub.userPodAntiAffinityRequired" .) -}} -{{- $dummy := set . "nodeAffinityPreferred" (include "jupyterhub.userNodeAffinityPreferred" .) -}} -{{- $dummy := set . "podAffinityPreferred" (include "jupyterhub.userPodAffinityPreferred" .) -}} -{{- $dummy := set . "podAntiAffinityPreferred" (include "jupyterhub.userPodAntiAffinityPreferred" .) -}} -{{- $dummy := set . "hasNodeAffinity" (or .nodeAffinityRequired .nodeAffinityPreferred) -}} -{{- $dummy := set . "hasPodAffinity" (or .podAffinityRequired .podAffinityPreferred) -}} -{{- $dummy := set . "hasPodAntiAffinity" (or .podAntiAffinityRequired .podAntiAffinityPreferred) -}} - -{{- if .hasNodeAffinity -}} -nodeAffinity: - {{- if .nodeAffinityRequired }} - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - {{- .nodeAffinityRequired | nindent 6 }} - {{- end }} - - {{- if .nodeAffinityPreferred }} - preferredDuringSchedulingIgnoredDuringExecution: - {{- .nodeAffinityPreferred | nindent 4 }} - {{- end }} -{{- end }} - -{{- if .hasPodAffinity }} -podAffinity: - {{- if .podAffinityRequired }} - requiredDuringSchedulingIgnoredDuringExecution: - {{- .podAffinityRequired | nindent 4 }} - {{- end }} - - {{- if .podAffinityPreferred }} - preferredDuringSchedulingIgnoredDuringExecution: - {{- .podAffinityPreferred | nindent 4 }} - {{- end }} -{{- end }} - -{{- if .hasPodAntiAffinity }} -podAntiAffinity: - {{- if .podAntiAffinityRequired }} - requiredDuringSchedulingIgnoredDuringExecution: - {{- .podAntiAffinityRequired | nindent 4 }} - {{- end }} - - {{- if .podAntiAffinityPreferred }} - preferredDuringSchedulingIgnoredDuringExecution: - {{- .podAntiAffinityPreferred | nindent 4 }} - {{- end }} -{{- end }} - -{{- end }} - - - -{{- define "jupyterhub.coreAffinity" -}} -{{- $require := eq .Values.scheduling.corePods.nodeAffinity.matchNodePurpose "require" -}} -{{- $prefer := eq .Values.scheduling.corePods.nodeAffinity.matchNodePurpose "prefer" -}} -{{- if or $require $prefer -}} -affinity: - nodeAffinity: - {{- if $require }} - requiredDuringSchedulingIgnoredDuringExecution: - nodeSelectorTerms: - - matchExpressions: - - key: hub.jupyter.org/node-purpose - operator: In - values: [core] - {{- end }} - {{- if $prefer }} - preferredDuringSchedulingIgnoredDuringExecution: - - weight: 100 - preference: - matchExpressions: - - key: hub.jupyter.org/node-purpose - operator: In - values: [core] - {{- end }} -{{- end }} -{{- end }} diff --git a/jupyterhub/templates/scheduling/priorityclass.yaml b/jupyterhub/templates/scheduling/priorityclass.yaml deleted file mode 100644 index 77c84cb..0000000 --- a/jupyterhub/templates/scheduling/priorityclass.yaml +++ /dev/null @@ -1,15 +0,0 @@ -{{- if .Values.scheduling.podPriority.enabled }} -apiVersion: scheduling.k8s.io/v1 -kind: PriorityClass -metadata: - name: {{ include "jupyterhub.priority.fullname" . }} - annotations: - meta.helm.sh/release-name: "{{ .Release.Name }}" - meta.helm.sh/release-namespace: "{{ .Release.Namespace }}" - labels: - {{- $_ := merge (dict "componentLabel" "default-priority") . }} - {{- include "jupyterhub.labels" $_ | nindent 4 }} -value: {{ .Values.scheduling.podPriority.defaultPriority }} -globalDefault: {{ .Values.scheduling.podPriority.globalDefault }} -description: "A default priority higher than user placeholders priority." -{{- end }} diff --git a/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml b/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml deleted file mode 100644 index ec84fb5..0000000 --- a/jupyterhub/templates/scheduling/user-placeholder/pdb.yaml +++ /dev/null @@ -1,22 +0,0 @@ -{{- /* -The cluster autoscaler should be allowed to evict and reschedule these pods if -it would help in order to scale down a node. -*/}} -{{- if .Values.scheduling.userPlaceholder.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} -{{- /* k8s 1.21+ required */ -}} -apiVersion: policy/v1 -{{- else }} -apiVersion: policy/v1beta1 -{{- end }} -kind: PodDisruptionBudget -metadata: - name: {{ include "jupyterhub.user-placeholder.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -spec: - minAvailable: 0 - selector: - matchLabels: - {{- include "jupyterhub.matchLabels" . | nindent 6 }} -{{- end }} diff --git a/jupyterhub/templates/scheduling/user-placeholder/priorityclass.yaml b/jupyterhub/templates/scheduling/user-placeholder/priorityclass.yaml deleted file mode 100644 index bdedbdd..0000000 --- a/jupyterhub/templates/scheduling/user-placeholder/priorityclass.yaml +++ /dev/null @@ -1,16 +0,0 @@ -{{- if .Values.scheduling.podPriority.enabled }} -{{- if .Values.scheduling.userPlaceholder.enabled -}} -apiVersion: scheduling.k8s.io/v1 -kind: PriorityClass -metadata: - name: {{ include "jupyterhub.user-placeholder-priority.fullname" . }} - annotations: - meta.helm.sh/release-name: "{{ .Release.Name }}" - meta.helm.sh/release-namespace: "{{ .Release.Namespace }}" - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -value: {{ .Values.scheduling.podPriority.userPlaceholderPriority }} -globalDefault: false -description: "With a priority higher or eqaul to a cluster autoscalers priority cutoff, a pod can trigger a cluster scale up. At the same time, placeholder pods priority should be lower than other pods to make them evictable." -{{- end }} -{{- end }} diff --git a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml b/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml deleted file mode 100644 index e0f6f59..0000000 --- a/jupyterhub/templates/scheduling/user-placeholder/statefulset.yaml +++ /dev/null @@ -1,80 +0,0 @@ - -{{- /* -These user-placeholder pods can be used to test cluster autoscaling in a -controlled fashion. - -Example: -$ echo 'Simulating four users...' -$ kubectl scale sts/user-placeholder --replicas 4 -*/}} -{{- if .Values.scheduling.userPlaceholder.enabled -}} -apiVersion: apps/v1 -kind: StatefulSet -metadata: - name: {{ include "jupyterhub.user-placeholder.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -spec: - podManagementPolicy: Parallel - {{- if typeIs "int" .Values.scheduling.userPlaceholder.revisionHistoryLimit }} - revisionHistoryLimit: {{ .Values.scheduling.userPlaceholder.revisionHistoryLimit }} - {{- end }} - replicas: {{ .Values.scheduling.userPlaceholder.replicas }} - selector: - matchLabels: - {{- include "jupyterhub.matchLabels" . | nindent 6 }} - serviceName: {{ include "jupyterhub.user-placeholder.fullname" . }} - template: - metadata: - {{- with .Values.scheduling.userPlaceholder.annotations }} - annotations: - {{- . | toYaml | nindent 8 }} - {{- end }} - labels: - {{- /* Changes here will cause the Deployment to restart the pods. */}} - {{- include "jupyterhub.matchLabels" . | nindent 8 }} - {{- with .Values.scheduling.userPlaceholder.labels }} - {{- . | toYaml | nindent 8 }} - {{- end }} - spec: - {{- if .Values.scheduling.podPriority.enabled }} - priorityClassName: {{ include "jupyterhub.user-placeholder-priority.fullname" . }} - {{- end }} - {{- if .Values.scheduling.userScheduler.enabled }} - schedulerName: {{ include "jupyterhub.user-scheduler.fullname" . }} - {{- end }} - {{- with .Values.singleuser.nodeSelector }} - nodeSelector: - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- with concat .Values.scheduling.userPods.tolerations .Values.singleuser.extraTolerations }} - tolerations: - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- if include "jupyterhub.userAffinity" . }} - affinity: - {{- include "jupyterhub.userAffinity" . | nindent 8 }} - {{- end }} - terminationGracePeriodSeconds: 0 - automountServiceAccountToken: false - {{- with include "jupyterhub.imagePullSecrets" (dict "root" . "image" .Values.scheduling.userPlaceholder.image) }} - imagePullSecrets: {{ . }} - {{- end }} - containers: - - name: pause - image: {{ .Values.scheduling.userPlaceholder.image.name }}:{{ .Values.scheduling.userPlaceholder.image.tag }} - {{- if .Values.scheduling.userPlaceholder.resources }} - resources: - {{- .Values.scheduling.userPlaceholder.resources | toYaml | nindent 12 }} - {{- else if (include "jupyterhub.singleuser.resources" .) }} - resources: - {{- include "jupyterhub.singleuser.resources" . | nindent 12 }} - {{- end }} - {{- with .Values.scheduling.userPlaceholder.image.pullPolicy }} - imagePullPolicy: {{ . }} - {{- end }} - {{- with .Values.scheduling.userPlaceholder.containerSecurityContext }} - securityContext: - {{- . | toYaml | nindent 12 }} - {{- end }} -{{- end }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml b/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml deleted file mode 100644 index 22ea9a8..0000000 --- a/jupyterhub/templates/scheduling/user-scheduler/configmap.yaml +++ /dev/null @@ -1,95 +0,0 @@ -{{- if .Values.scheduling.userScheduler.enabled -}} -kind: ConfigMap -apiVersion: v1 -metadata: - name: {{ include "jupyterhub.user-scheduler-deploy.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -data: - {{- /* - This is configuration of a k8s official kube-scheduler binary running in the - user-scheduler pod. - - ref: https://kubernetes.io/docs/reference/scheduling/config/ - ref: https://kubernetes.io/docs/reference/config-api/kube-scheduler-config.v1beta2/ - ref: https://kubernetes.io/docs/reference/config-api/kube-scheduler-config.v1beta3/ - - v1beta1 can be used with kube-scheduler binary version <=1.21 - v1beta2 requires kube-scheduler binary version >=1.22 - v1beta3 requires kube-scheduler binary version >=1.23 - - kube-scheduler binaries versioned >=1.21 will error in k8s clusters - versioned <=1.20. To support a modern version of kube-scheduler and k8s - versions <=1.20 upwards, we provide two scenarios: - - 1. For k8s >= 1.21 we use a modern version of kube-scheduler and Helm chart - configuration works as expected. - 2. For k8s <= 1.20 we use a hardcoded version of kube-scheduler (v1.20.15) - and configuration (v1beta1) of kube-scheduler. - */}} - config.yaml: | - {{- /* - FIXME: We have added a workaround for EKS where - .Capabilities.KubeVersion.Minor can return a - string like "22+" instead of just "22". - - See https://github.com/aws/eks-distro/issues/1128. - */}} - {{- if ge (atoi (.Capabilities.KubeVersion.Minor | replace "+" "")) 21 }} - apiVersion: kubescheduler.config.k8s.io/v1beta3 - kind: KubeSchedulerConfiguration - leaderElection: - resourceLock: endpoints - resourceName: {{ include "jupyterhub.user-scheduler-lock.fullname" . }} - resourceNamespace: "{{ .Release.Namespace }}" - profiles: - - schedulerName: {{ include "jupyterhub.user-scheduler.fullname" . }} - {{- with .Values.scheduling.userScheduler.plugins }} - plugins: - {{- . | toYaml | nindent 10 }} - {{- end }} - {{- with .Values.scheduling.userScheduler.pluginConfig }} - pluginConfig: - {{- . | toYaml | nindent 10 }} - {{- end }} - {{- else }} - # WARNING: The tag of this image is hardcoded, and the - # "scheduling.userScheduler.plugins" configuration of the Helm - # chart that generated this resource manifest wasn't respected. If - # you install the Helm chart in a k8s cluster versioned 1.21 or - # higher, your configuration will be respected. - apiVersion: kubescheduler.config.k8s.io/v1beta1 - kind: KubeSchedulerConfiguration - leaderElection: - resourceLock: endpoints - resourceName: {{ include "jupyterhub.user-scheduler-lock.fullname" . }} - resourceNamespace: "{{ .Release.Namespace }}" - profiles: - - schedulerName: {{ include "jupyterhub.user-scheduler.fullname" . }} - plugins: - score: - disabled: - - name: SelectorSpread - - name: TaintToleration - - name: PodTopologySpread - - name: NodeResourcesBalancedAllocation - - name: NodeResourcesLeastAllocated - # Disable plugins to be allowed to enable them again with a - # different weight and avoid an error. - - name: NodePreferAvoidPods - - name: NodeAffinity - - name: InterPodAffinity - - name: ImageLocality - enabled: - - name: NodePreferAvoidPods - weight: 161051 - - name: NodeAffinity - weight: 14631 - - name: InterPodAffinity - weight: 1331 - - name: NodeResourcesMostAllocated - weight: 121 - - name: ImageLocality - weight: 11 - {{- end }} -{{- end }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml b/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml deleted file mode 100644 index 58bb23a..0000000 --- a/jupyterhub/templates/scheduling/user-scheduler/deployment.yaml +++ /dev/null @@ -1,109 +0,0 @@ -{{- if .Values.scheduling.userScheduler.enabled -}} -apiVersion: apps/v1 -kind: Deployment -metadata: - name: {{ include "jupyterhub.user-scheduler-deploy.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -spec: - {{- if typeIs "int" .Values.scheduling.userScheduler.revisionHistoryLimit }} - revisionHistoryLimit: {{ .Values.scheduling.userScheduler.revisionHistoryLimit }} - {{- end }} - replicas: {{ .Values.scheduling.userScheduler.replicas }} - selector: - matchLabels: - {{- include "jupyterhub.matchLabels" . | nindent 6 }} - template: - metadata: - labels: - {{- include "jupyterhub.matchLabels" . | nindent 8 }} - {{- with .Values.scheduling.userScheduler.labels }} - {{- . | toYaml | nindent 8 }} - {{- end }} - annotations: - checksum/config-map: {{ include (print $.Template.BasePath "/scheduling/user-scheduler/configmap.yaml") . | sha256sum }} - {{- with .Values.scheduling.userScheduler.annotations }} - {{- . | toYaml | nindent 8 }} - {{- end }} - spec: - {{ with include "jupyterhub.user-scheduler-serviceaccount.fullname" . }} - serviceAccountName: {{ . }} - {{- end }} - {{- if .Values.scheduling.podPriority.enabled }} - priorityClassName: {{ include "jupyterhub.priority.fullname" . }} - {{- end }} - {{- with .Values.scheduling.userScheduler.nodeSelector }} - nodeSelector: - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- with concat .Values.scheduling.corePods.tolerations .Values.scheduling.userScheduler.tolerations }} - tolerations: - {{- . | toYaml | nindent 8 }} - {{- end }} - {{- include "jupyterhub.coreAffinity" . | nindent 6 }} - volumes: - - name: config - configMap: - name: {{ include "jupyterhub.user-scheduler-deploy.fullname" . }} - {{- with include "jupyterhub.imagePullSecrets" (dict "root" . "image" .Values.scheduling.userScheduler.image) }} - imagePullSecrets: {{ . }} - {{- end }} - containers: - - name: kube-scheduler - {{- /* - FIXME: We have added a workaround for EKS where - .Capabilities.KubeVersion.Minor can return a - string like "22+" instead of just "22". - - See https://github.com/aws/eks-distro/issues/1128. - */}} - {{- if ge (atoi (.Capabilities.KubeVersion.Minor | replace "+" "")) 21 }} - image: {{ .Values.scheduling.userScheduler.image.name }}:{{ .Values.scheduling.userScheduler.image.tag }} - {{- else }} - # WARNING: The tag of this image is hardcoded, and the - # "scheduling.userScheduler.image.tag" configuration of the - # Helm chart that generated this resource manifest isn't - # respected. If you install the Helm chart in a k8s cluster - # versioned 1.21 or higher, your configuration will be - # respected. - image: {{ .Values.scheduling.userScheduler.image.name }}:v1.20.15 - {{- end }} - {{- with .Values.scheduling.userScheduler.image.pullPolicy }} - imagePullPolicy: {{ . }} - {{- end }} - command: - - /usr/local/bin/kube-scheduler - # NOTE: --authentication-skip-lookup=true is used to avoid a - # seemingly harmless error, if we need to not skip - # "authentication lookup" in the future, see the linked issue. - # - # ref: https://github.com/jupyterhub/zero-to-jupyterhub-k8s/issues/1894 - - --config=/etc/user-scheduler/config.yaml - - --authentication-skip-lookup=true - - --v={{ .Values.scheduling.userScheduler.logLevel }} - volumeMounts: - - mountPath: /etc/user-scheduler - name: config - livenessProbe: - httpGet: - path: /healthz - scheme: HTTPS - port: 10259 - initialDelaySeconds: 15 - readinessProbe: - httpGet: - path: /healthz - scheme: HTTPS - port: 10259 - {{- with .Values.scheduling.userScheduler.resources }} - resources: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- with .Values.scheduling.userScheduler.containerSecurityContext }} - securityContext: - {{- . | toYaml | nindent 12 }} - {{- end }} - {{- with .Values.scheduling.userScheduler.extraPodSpec }} - {{- . | toYaml | nindent 6 }} - {{- end }} -{{- end }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml b/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml deleted file mode 100644 index 3a9544e..0000000 --- a/jupyterhub/templates/scheduling/user-scheduler/pdb.yaml +++ /dev/null @@ -1,23 +0,0 @@ -{{- if and .Values.scheduling.userScheduler.enabled .Values.scheduling.userScheduler.pdb.enabled -}} -{{- if .Capabilities.APIVersions.Has "policy/v1/PodDisruptionBudget" -}} -{{- /* k8s 1.21+ required */ -}} -apiVersion: policy/v1 -{{- else }} -apiVersion: policy/v1beta1 -{{- end }} -kind: PodDisruptionBudget -metadata: - name: {{ include "jupyterhub.user-scheduler-deploy.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -spec: - {{- if not (typeIs "" .Values.scheduling.userScheduler.pdb.maxUnavailable) }} - maxUnavailable: {{ .Values.scheduling.userScheduler.pdb.maxUnavailable }} - {{- end }} - {{- if not (typeIs "" .Values.scheduling.userScheduler.pdb.minAvailable) }} - minAvailable: {{ .Values.scheduling.userScheduler.pdb.minAvailable }} - {{- end }} - selector: - matchLabels: - {{- include "jupyterhub.matchLabels" . | nindent 6 }} -{{- end }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml b/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml deleted file mode 100644 index f77640b..0000000 --- a/jupyterhub/templates/scheduling/user-scheduler/rbac.yaml +++ /dev/null @@ -1,233 +0,0 @@ -{{- if .Values.scheduling.userScheduler.enabled -}} -{{- if .Values.rbac.create -}} -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: {{ include "jupyterhub.user-scheduler.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -rules: - # Copied from the system:kube-scheduler ClusterRole of the k8s version - # matching the kube-scheduler binary we use. A modification has been made to - # resourceName fields to remain relevant for how we have named our resources - # in this Helm chart. - # - # NOTE: These rules have been: - # - unchanged between 1.12 and 1.15 - # - changed in 1.16 - # - changed in 1.17 - # - unchanged between 1.18 and 1.20 - # - changed in 1.21: get/list/watch permission for namespace, - # csidrivers, csistoragecapacities was added. - # - unchanged between 1.22 and 1.23 - # - # ref: https://github.com/kubernetes/kubernetes/blob/v1.23.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L705-L861 - - apiGroups: - - "" - - events.k8s.io - resources: - - events - verbs: - - create - - patch - - update - - apiGroups: - - coordination.k8s.io - resources: - - leases - verbs: - - create - - apiGroups: - - coordination.k8s.io - resourceNames: - - {{ include "jupyterhub.user-scheduler-lock.fullname" . }} - resources: - - leases - verbs: - - get - - update - - apiGroups: - - "" - resources: - - endpoints - verbs: - - create - - apiGroups: - - "" - resourceNames: - - {{ include "jupyterhub.user-scheduler-lock.fullname" . }} - resources: - - endpoints - verbs: - - get - - update - - apiGroups: - - "" - resources: - - nodes - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - pods - verbs: - - delete - - get - - list - - watch - - apiGroups: - - "" - resources: - - bindings - - pods/binding - verbs: - - create - - apiGroups: - - "" - resources: - - pods/status - verbs: - - patch - - update - - apiGroups: - - "" - resources: - - replicationcontrollers - - services - verbs: - - get - - list - - watch - - apiGroups: - - apps - - extensions - resources: - - replicasets - verbs: - - get - - list - - watch - - apiGroups: - - apps - resources: - - statefulsets - verbs: - - get - - list - - watch - - apiGroups: - - policy - resources: - - poddisruptionbudgets - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - persistentvolumeclaims - - persistentvolumes - verbs: - - get - - list - - watch - - apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create - - apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create - - apiGroups: - - storage.k8s.io - resources: - - csinodes - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - namespaces - verbs: - - get - - list - - watch - - apiGroups: - - storage.k8s.io - resources: - - csidrivers - verbs: - - get - - list - - watch - - apiGroups: - - storage.k8s.io - resources: - - csistoragecapacities - verbs: - - get - - list - - watch - - # Copied from the system:volume-scheduler ClusterRole of the k8s version - # matching the kube-scheduler binary we use. - # - # NOTE: These rules have not changed between 1.12 and 1.23. - # - # ref: https://github.com/kubernetes/kubernetes/blob/v1.23.0/plugin/pkg/auth/authorizer/rbac/bootstrappolicy/testdata/cluster-roles.yaml#L1280-L1307 - - apiGroups: - - "" - resources: - - persistentvolumes - verbs: - - get - - list - - patch - - update - - watch - - apiGroups: - - storage.k8s.io - resources: - - storageclasses - verbs: - - get - - list - - watch - - apiGroups: - - "" - resources: - - persistentvolumeclaims - verbs: - - get - - list - - patch - - update - - watch ---- -kind: ClusterRoleBinding -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: {{ include "jupyterhub.user-scheduler.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -subjects: - - kind: ServiceAccount - name: {{ include "jupyterhub.user-scheduler-serviceaccount.fullname" . }} - namespace: "{{ .Release.Namespace }}" -roleRef: - kind: ClusterRole - name: {{ include "jupyterhub.user-scheduler.fullname" . }} - apiGroup: rbac.authorization.k8s.io -{{- end }} -{{- end }} diff --git a/jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml b/jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml deleted file mode 100644 index f84ffc1..0000000 --- a/jupyterhub/templates/scheduling/user-scheduler/serviceaccount.yaml +++ /dev/null @@ -1,14 +0,0 @@ -{{- if .Values.scheduling.userScheduler.enabled -}} -{{- if .Values.scheduling.userScheduler.serviceAccount.create -}} -apiVersion: v1 -kind: ServiceAccount -metadata: - name: {{ include "jupyterhub.user-scheduler-serviceaccount.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} - {{- with .Values.scheduling.userScheduler.serviceAccount.annotations }} - annotations: - {{- . | toYaml | nindent 4 }} - {{- end }} -{{- end }} -{{- end }} diff --git a/jupyterhub/templates/singleuser/netpol.yaml b/jupyterhub/templates/singleuser/netpol.yaml deleted file mode 100644 index f388b81..0000000 --- a/jupyterhub/templates/singleuser/netpol.yaml +++ /dev/null @@ -1,99 +0,0 @@ -{{- if and .Values.singleuser.networkPolicy.enabled -}} -apiVersion: networking.k8s.io/v1 -kind: NetworkPolicy -metadata: - name: {{ include "jupyterhub.singleuser.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -spec: - podSelector: - matchLabels: - {{- $_ := merge (dict "componentLabel" "singleuser-server") . }} - {{- include "jupyterhub.matchLabels" $_ | nindent 6 }} - policyTypes: - - Ingress - - Egress - - # IMPORTANT: - # NetworkPolicy's ingress "from" and egress "to" rule specifications require - # great attention to detail. A quick summary is: - # - # 1. You can provide "from"/"to" rules that provide access either ports or a - # subset of ports. - # 2. You can for each "from"/"to" rule provide any number of - # "sources"/"destinations" of four different kinds. - # - podSelector - targets pods with a certain label in the same namespace as the NetworkPolicy - # - namespaceSelector - targets all pods running in namespaces with a certain label - # - namespaceSelector and podSelector - targets pods with a certain label running in namespaces with a certain label - # - ipBlock - targets network traffic from/to a set of IP address ranges - # - # Read more at: https://kubernetes.io/docs/concepts/services-networking/network-policies/#behavior-of-to-and-from-selectors - # - ingress: - {{- with .Values.singleuser.networkPolicy.allowedIngressPorts }} - # allow incoming traffic to these ports independent of source - - ports: - {{- range $port := . }} - - port: {{ $port }} - {{- end }} - {{- end }} - - # allowed pods (hub.jupyter.org/network-access-singleuser) --> singleuser-server - - ports: - - port: notebook-port - from: - # source 1 - labeled pods - - podSelector: - matchLabels: - hub.jupyter.org/network-access-singleuser: "true" - {{- if eq .Values.singleuser.networkPolicy.interNamespaceAccessLabels "accept" }} - namespaceSelector: - matchLabels: {} # without this, the podSelector would only consider pods in the local namespace - # source 2 - pods in labeled namespaces - - namespaceSelector: - matchLabels: - hub.jupyter.org/network-access-singleuser: "true" - {{- end }} - - {{- with .Values.singleuser.networkPolicy.ingress }} - # depends, but default is nothing --> singleuser-server - {{- . | toYaml | nindent 4 }} - {{- end }} - - egress: - # singleuser-server --> hub - - to: - - podSelector: - matchLabels: - {{- $_ := merge (dict "componentLabel" "hub") . }} - {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} - ports: - - port: 8081 - - # singleuser-server --> proxy - # singleuser-server --> autohttps - # - # While not critical for core functionality, a user or library code may rely - # on communicating with the proxy or autohttps pods via a k8s Service it can - # detected from well known environment variables. - # - - to: - - podSelector: - matchLabels: - {{- $_ := merge (dict "componentLabel" "proxy") . }} - {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} - ports: - - port: 8000 - - to: - - podSelector: - matchLabels: - {{- $_ := merge (dict "componentLabel" "autohttps") . }} - {{- include "jupyterhub.matchLabels" $_ | nindent 14 }} - ports: - - port: 8080 - - port: 8443 - - {{- with (include "jupyterhub.networkPolicy.renderEgressRules" (list . .Values.singleuser.networkPolicy)) }} - {{- . | nindent 4 }} - {{- end }} -{{- end }} diff --git a/jupyterhub/templates/singleuser/secret.yaml b/jupyterhub/templates/singleuser/secret.yaml deleted file mode 100644 index f4a5fe2..0000000 --- a/jupyterhub/templates/singleuser/secret.yaml +++ /dev/null @@ -1,17 +0,0 @@ -{{- if .Values.singleuser.extraFiles }} -kind: Secret -apiVersion: v1 -metadata: - name: {{ include "jupyterhub.singleuser.fullname" . }} - labels: - {{- include "jupyterhub.labels" . | nindent 4 }} -type: Opaque -{{- with include "jupyterhub.extraFiles.data" .Values.singleuser.extraFiles }} -data: - {{- . | nindent 2 }} -{{- end }} -{{- with include "jupyterhub.extraFiles.stringData" .Values.singleuser.extraFiles }} -stringData: - {{- . | nindent 2 }} -{{- end }} -{{- end }} diff --git a/requirements.in b/requirements.in deleted file mode 100644 index cfea6af..0000000 --- a/requirements.in +++ /dev/null @@ -1,24 +0,0 @@ -# The top-level dependencies are listed here. -# Using pip-tools you can automatically generate requirements.txt. -# This helps you segegate the top-level dependencies and makes the -# upgrade process easier. - -# Here, the dependencies aren't pinned to keep them updated. -# However, you should pin your top-level dependencies. - -# https://github.com/jazzband/pip-tools - -pyjwt==2.9.0 -jupyterhub==5.1.0 -pytest==8.3.3 -pytest-asyncio==0.24.0 -pytest-cov==5.0.0 -requests-mock==1.12.1 -jupyterhub-kubespawner==6.2.0 -httplib2==0.22.0 -oauthenticator==17.0.0 -jupyterhub-idle-culler==1.4.0 -kubernetes==31.0.0 -loguru==0.7.2 -addict==2.4.0 -pydantic==2.9.2 From ccd38b402633e235783366dcd654e6cc6d971ea1 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Fri, 27 Dec 2024 17:32:44 +0100 Subject: [PATCH 50/65] fixes empty envs --- application_hub_context/app_hub_context.py | 38 ++++++++++++---------- application_hub_context/models.py | 2 +- config-generator/models.py | 2 ++ 3 files changed, 23 insertions(+), 19 deletions(-) diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index 672e20b..6e12fc2 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -982,31 +982,32 @@ def initialise(self): if env_from_config_maps: self.spawner.extra_container_config["env_from"] = [] - for env_from_config_map in env_from_config_maps: - self.spawner.log.info(f"env_from_config_map {env_from_config_map}") - self.spawner.extra_container_config["env_from"].append( - { - "configMapRef": { - "name": self.render(env_from_config_map), - }}) - self.spawner.log.info(f"extra_container_config {self.spawner.extra_container_config}") + for env_from_config_map in env_from_config_maps: + self.spawner.log.info(f"env_from_config_map {env_from_config_map}") + self.spawner.extra_container_config["env_from"].append( + { + "configMapRef": { + "name": self.render(env_from_config_map), + }}) + self.spawner.log.info(f"extra_container_config {self.spawner.extra_container_config}") # process the pod env vars from secrets env_from_secrets = self.config_parser.get_profile_env_from_secrets( profile_id=profile_id ) self.spawner.log.info(f"env_from_secrets {env_from_secrets}") - if env_from_secrets and self.spawner.extra_container_config["env_from"] is None: - self.spawner.extra_container_config["env_from"] = [] + if env_from_secrets: + if self.spawner.extra_container_config["env_from"] is None: + self.spawner.extra_container_config["env_from"] = [] - for env_from_secret in env_from_secrets: - self.spawner.log.info(f"env_from_secret {env_from_secret}") - self.spawner.extra_container_config["env_from"].append( - { - "secretRef": { - "name": self.render(env_from_secret), - }}) - self.spawner.log.info(f"extra_container_config {self.spawner.extra_container_config}") + for env_from_secret in env_from_secrets: + self.spawner.log.info(f"env_from_secret {env_from_secret}") + self.spawner.extra_container_config["env_from"].append( + { + "secretRef": { + "name": self.render(env_from_secret), + }}) + self.spawner.log.info(f"extra_container_config {self.spawner.extra_container_config}") secret_mounts = self.config_parser.get_profile_secret_mounts( @@ -1021,6 +1022,7 @@ def initialise(self): { "name": self.render(secret_mount.name), "mountPath": secret_mount.mount_path, + "subPath": secret_mount.sub_path, }, ] ) diff --git a/application_hub_context/models.py b/application_hub_context/models.py index c049c3a..86b52c8 100644 --- a/application_hub_context/models.py +++ b/application_hub_context/models.py @@ -166,7 +166,7 @@ class ImagePullSecret(BaseModel): class SecretMount(BaseModel): name: str mount_path: str - + sub_path: Optional[str] = None class Profile(BaseModel): id: str diff --git a/config-generator/models.py b/config-generator/models.py index 03b91c3..4715396 100644 --- a/config-generator/models.py +++ b/config-generator/models.py @@ -124,9 +124,11 @@ class ImagePullSecret(BaseModel): persist: bool = True data: Optional[str] = None + class SecretMount(BaseModel): name: str mount_path: str + sub_path: Optional[str] = None class Profile(BaseModel): From d8ad8b70240788f7bbfda13a2710b00edaa86d76 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Fri, 27 Dec 2024 17:47:52 +0100 Subject: [PATCH 51/65] fixes manifest persistance --- application_hub_context/app_hub_context.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index 6e12fc2..b57b721 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -1084,8 +1084,9 @@ def dispose(self): self.spawner.log.info(f"Delete manifest {manifests}") if manifests: for manifest in manifests: - self.spawner.log.info(f"Un-apply manifest {manifest.name}") - self.unapply_manifests(manifest_content=manifest.content) + if not manifest.persist: + self.spawner.log.info(f"Un-apply manifest {manifest.name}") + self.unapply_manifests(manifest_content=manifest.content) # deal with the image pull secrets image_pull_secrets = self.config_parser.get_profile_image_pull_secrets( From 8f8df19ea349bf24e79dbbf8b43fc6b6d15281d6 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Sun, 29 Dec 2024 12:08:36 +0100 Subject: [PATCH 52/65] add crossplane kubernets object --- application_hub_context/app_hub_context.py | 26 +- files/hub/config.yml | 1324 ++++---------------- 2 files changed, 290 insertions(+), 1060 deletions(-) diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index b57b721..cfd76dc 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -566,6 +566,22 @@ def apply_manifest(self, manifest): plural=plural, body=yaml.safe_load(rendered_manifest) ) + if yaml.safe_load(rendered_manifest)["kind"] in ["Object"]: + # see https://github.com/kubernetes-client/python/blob/master/kubernetes/docs/CustomObjectsApi.md#create_namespaced_custom_object + # Define the Crossplane Kubernetes Object details + group = "kubernetes.crossplane.io" + version = "v1alpha2" + plural = "objects" + + self.spawner.log.info(f"Creating Crossplane Kubernetes Object {yaml.safe_load(rendered_manifest).get('metadata').get('name')}") + + # Create the Crossplane Kubernetes Object + self.custom_objects_api.create_cluster_custom_object( + group=group, + version=version, + plural=plural, + body=yaml.safe_load(rendered_manifest) + ) elif yaml.safe_load(rendered_manifest)["kind"] in ["ExternalSecret"]: self.spawner.log.info(f"Creating ExternalSecret {yaml.safe_load(rendered_manifest).get('metadata').get('name')} in namespace {self.namespace}") # Define the ExternalSecret details @@ -642,7 +658,15 @@ def unapply_manifests(self, manifest_content): name=name, ) self.spawner.log.info(f"Response: {response}") - + elif kind == "Object": + self.spawner.log.info(f"Deleting Crossplane Kubernetes Object {name}") + response = self.custom_objects_api.delete_cluster_custom_object( + group="kubernetes.crossplane.io", + version="v1alpha2", + plural="objects", + name=name, + ) + self.spawner.log.info(f"Response: {response}") elif kind == "ExternalSecret": self.spawner.log.info(f"Deleting ExternalSecret {name} from namespace {self.render(namespace)}") self.custom_objects_api.delete_namespaced_custom_object( diff --git a/files/hub/config.yml b/files/hub/config.yml index ac7ed97..b5f2e1a 100644 --- a/files/hub/config.yml +++ b/files/hub/config.yml @@ -4,1070 +4,51 @@ profiles: \ /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram\ \ 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\ - \nalias aws=\"aws --endpoint-url=http://localstack:4566\"\n\n# >>> conda initialize\ + \n#alias aws=\"aws --endpoint-url=http://localstack:4566\"\n\n# >>> conda initialize\ \ >>>\n# !! Contents within this block are managed by 'conda init' !!\n__conda_setup=\"\ $('/opt/conda/bin/conda' 'shell.bash' 'hook' 2> /dev/null)\"\nif [ $? -eq 0\ \ ]; then\n eval \"$__conda_setup\"\nelse\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\"\ \ ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\n else\n \ \ export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset __conda_setup\n\n\ if [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n . \"/opt/conda/etc/profile.d/mamba.sh\"\ - \nfi\n# <<< conda initialize <<<\n\na={{spawner.user.name}}" + \nfi\n# <<< conda initialize <<<\n\na={{spawner.user.name}}\n\nalias aws=\"\ + aws --endpoint-url=http://localstack-jupyter-{{spawner.user.name}}:4566\"" default_mode: null key: bash-rc mount_path: /workspace/.bashrc name: bash-rc - persist: true - readonly: true - default_url: null - definition: - default: false - description: null - display_name: Code Server Small - kubespawner_override: - cpu_guarantee: null - cpu_limit: 2 - extra_resource_guarantees: {} - extra_resource_limits: {} - image: eoepca/pde-code-server:develop - mem_guarantee: null - mem_limit: 8G - slug: eoepca_coder_slug_s - env_from_config_maps: - - my-config - env_from_secrets: - - my-secret - - data-by-name - groups: - - group-a - - group-b - id: profile_studio_coder1 - image_pull_secrets: - - data: null - name: eoepca-plus-secret-ro - persist: true - init_containers: [] - manifests: - - content: - - apiVersion: v1 - kind: ServiceAccount - metadata: - name: localstack - - apiVersion: rbac.authorization.k8s.io/v1 - kind: Role - metadata: - name: localstack - rules: - - apiGroups: - - '' - resources: - - pods - verbs: - - '*' - - apiGroups: - - '' - resources: - - pods/log - verbs: - - get - - apiGroups: - - '' - resources: - - pods/exec - verbs: - - get - - create - - apiGroups: - - '' - resources: - - services - verbs: - - get - - list - - apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding - metadata: - name: localstack - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: localstack - subjects: - - kind: ServiceAccount - name: localstack - - apiVersion: v1 - kind: Service - metadata: - name: localstack - spec: - ports: - - name: edge - port: 4566 - targetPort: 4566 - - name: external-service-port-4510 - port: 4510 - targetPort: ext-svc-4510 - - name: external-service-port-4511 - port: 4511 - targetPort: ext-svc-4511 - - name: external-service-port-4512 - port: 4512 - targetPort: ext-svc-4512 - - name: external-service-port-4513 - port: 4513 - targetPort: ext-svc-4513 - - name: external-service-port-4514 - port: 4514 - targetPort: ext-svc-4514 - - name: external-service-port-4515 - port: 4515 - targetPort: ext-svc-4515 - - name: external-service-port-4516 - port: 4516 - targetPort: ext-svc-4516 - - name: external-service-port-4517 - port: 4517 - targetPort: ext-svc-4517 - - name: external-service-port-4518 - port: 4518 - targetPort: ext-svc-4518 - - name: external-service-port-4519 - port: 4519 - targetPort: ext-svc-4519 - - name: external-service-port-4520 - port: 4520 - targetPort: ext-svc-4520 - - name: external-service-port-4521 - port: 4521 - targetPort: ext-svc-4521 - - name: external-service-port-4522 - port: 4522 - targetPort: ext-svc-4522 - - name: external-service-port-4523 - port: 4523 - targetPort: ext-svc-4523 - - name: external-service-port-4524 - port: 4524 - targetPort: ext-svc-4524 - - name: external-service-port-4525 - port: 4525 - targetPort: ext-svc-4525 - - name: external-service-port-4526 - port: 4526 - targetPort: ext-svc-4526 - - name: external-service-port-4527 - port: 4527 - targetPort: ext-svc-4527 - - name: external-service-port-4528 - port: 4528 - targetPort: ext-svc-4528 - - name: external-service-port-4529 - port: 4529 - targetPort: ext-svc-4529 - - name: external-service-port-4530 - port: 4530 - targetPort: ext-svc-4530 - - name: external-service-port-4531 - port: 4531 - targetPort: ext-svc-4531 - - name: external-service-port-4532 - port: 4532 - targetPort: ext-svc-4532 - - name: external-service-port-4533 - port: 4533 - targetPort: ext-svc-4533 - - name: external-service-port-4534 - port: 4534 - targetPort: ext-svc-4534 - - name: external-service-port-4535 - port: 4535 - targetPort: ext-svc-4535 - - name: external-service-port-4536 - port: 4536 - targetPort: ext-svc-4536 - - name: external-service-port-4537 - port: 4537 - targetPort: ext-svc-4537 - - name: external-service-port-4538 - port: 4538 - targetPort: ext-svc-4538 - - name: external-service-port-4539 - port: 4539 - targetPort: ext-svc-4539 - - name: external-service-port-4540 - port: 4540 - targetPort: ext-svc-4540 - - name: external-service-port-4541 - port: 4541 - targetPort: ext-svc-4541 - - name: external-service-port-4542 - port: 4542 - targetPort: ext-svc-4542 - - name: external-service-port-4543 - port: 4543 - targetPort: ext-svc-4543 - - name: external-service-port-4544 - port: 4544 - targetPort: ext-svc-4544 - - name: external-service-port-4545 - port: 4545 - targetPort: ext-svc-4545 - - name: external-service-port-4546 - port: 4546 - targetPort: ext-svc-4546 - - name: external-service-port-4547 - port: 4547 - targetPort: ext-svc-4547 - - name: external-service-port-4548 - port: 4548 - targetPort: ext-svc-4548 - - name: external-service-port-4549 - port: 4549 - targetPort: ext-svc-4549 - - name: external-service-port-4550 - port: 4550 - targetPort: ext-svc-4550 - - name: external-service-port-4551 - port: 4551 - targetPort: ext-svc-4551 - - name: external-service-port-4552 - port: 4552 - targetPort: ext-svc-4552 - - name: external-service-port-4553 - port: 4553 - targetPort: ext-svc-4553 - - name: external-service-port-4554 - port: 4554 - targetPort: ext-svc-4554 - - name: external-service-port-4555 - port: 4555 - targetPort: ext-svc-4555 - - name: external-service-port-4556 - port: 4556 - targetPort: ext-svc-4556 - - name: external-service-port-4557 - port: 4557 - targetPort: ext-svc-4557 - - name: external-service-port-4558 - port: 4558 - targetPort: ext-svc-4558 - - name: external-service-port-4559 - port: 4559 - targetPort: ext-svc-4559 - selector: - app.kubernetes.io/instance: localstack - app.kubernetes.io/name: localstack - type: ClusterIP - - apiVersion: apps/v1 - kind: Deployment - metadata: - name: localstack - spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/instance: localstack - app.kubernetes.io/name: localstack - strategy: - type: RollingUpdate - template: - metadata: - labels: - app: localstack-{{ spawner.user.name }} - app.kubernetes.io/instance: localstack - app.kubernetes.io/name: localstack - spec: - containers: - - env: - - name: DEBUG - value: '0' - - name: EXTERNAL_SERVICE_PORTS_START - value: '4510' - - name: EXTERNAL_SERVICE_PORTS_END - value: '4560' - - name: LOCALSTACK_K8S_SERVICE_NAME - value: localstack - - name: LOCALSTACK_K8S_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: LAMBDA_RUNTIME_EXECUTOR - value: docker - - name: LAMBDA_K8S_IMAGE_PREFIX - value: localstack/lambda- - - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT - value: '60' - - name: OVERRIDE_IN_DOCKER - value: '1' - image: localstack/localstack:latest - imagePullPolicy: IfNotPresent - livenessProbe: - failureThreshold: 3 - httpGet: - path: /_localstack/health - port: edge - initialDelaySeconds: 0 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - name: localstack - ports: - - containerPort: 4566 - name: edge - protocol: TCP - - containerPort: 4510 - name: ext-svc-4510 - protocol: TCP - - containerPort: 4511 - name: ext-svc-4511 - protocol: TCP - - containerPort: 4512 - name: ext-svc-4512 - protocol: TCP - - containerPort: 4513 - name: ext-svc-4513 - protocol: TCP - - containerPort: 4514 - name: ext-svc-4514 - protocol: TCP - - containerPort: 4515 - name: ext-svc-4515 - protocol: TCP - - containerPort: 4516 - name: ext-svc-4516 - protocol: TCP - - containerPort: 4517 - name: ext-svc-4517 - protocol: TCP - - containerPort: 4518 - name: ext-svc-4518 - protocol: TCP - - containerPort: 4519 - name: ext-svc-4519 - protocol: TCP - - containerPort: 4520 - name: ext-svc-4520 - protocol: TCP - - containerPort: 4521 - name: ext-svc-4521 - protocol: TCP - - containerPort: 4522 - name: ext-svc-4522 - protocol: TCP - - containerPort: 4523 - name: ext-svc-4523 - protocol: TCP - - containerPort: 4524 - name: ext-svc-4524 - protocol: TCP - - containerPort: 4525 - name: ext-svc-4525 - protocol: TCP - - containerPort: 4526 - name: ext-svc-4526 - protocol: TCP - - containerPort: 4527 - name: ext-svc-4527 - protocol: TCP - - containerPort: 4528 - name: ext-svc-4528 - protocol: TCP - - containerPort: 4529 - name: ext-svc-4529 - protocol: TCP - - containerPort: 4530 - name: ext-svc-4530 - protocol: TCP - - containerPort: 4531 - name: ext-svc-4531 - protocol: TCP - - containerPort: 4532 - name: ext-svc-4532 - protocol: TCP - - containerPort: 4533 - name: ext-svc-4533 - protocol: TCP - - containerPort: 4534 - name: ext-svc-4534 - protocol: TCP - - containerPort: 4535 - name: ext-svc-4535 - protocol: TCP - - containerPort: 4536 - name: ext-svc-4536 - protocol: TCP - - containerPort: 4537 - name: ext-svc-4537 - protocol: TCP - - containerPort: 4538 - name: ext-svc-4538 - protocol: TCP - - containerPort: 4539 - name: ext-svc-4539 - protocol: TCP - - containerPort: 4540 - name: ext-svc-4540 - protocol: TCP - - containerPort: 4541 - name: ext-svc-4541 - protocol: TCP - - containerPort: 4542 - name: ext-svc-4542 - protocol: TCP - - containerPort: 4543 - name: ext-svc-4543 - protocol: TCP - - containerPort: 4544 - name: ext-svc-4544 - protocol: TCP - - containerPort: 4545 - name: ext-svc-4545 - protocol: TCP - - containerPort: 4546 - name: ext-svc-4546 - protocol: TCP - - containerPort: 4547 - name: ext-svc-4547 - protocol: TCP - - containerPort: 4548 - name: ext-svc-4548 - protocol: TCP - - containerPort: 4549 - name: ext-svc-4549 - protocol: TCP - - containerPort: 4550 - name: ext-svc-4550 - protocol: TCP - - containerPort: 4551 - name: ext-svc-4551 - protocol: TCP - - containerPort: 4552 - name: ext-svc-4552 - protocol: TCP - - containerPort: 4553 - name: ext-svc-4553 - protocol: TCP - - containerPort: 4554 - name: ext-svc-4554 - protocol: TCP - - containerPort: 4555 - name: ext-svc-4555 - protocol: TCP - - containerPort: 4556 - name: ext-svc-4556 - protocol: TCP - - containerPort: 4557 - name: ext-svc-4557 - protocol: TCP - - containerPort: 4558 - name: ext-svc-4558 - protocol: TCP - - containerPort: 4559 - name: ext-svc-4559 - protocol: TCP - readinessProbe: - failureThreshold: 3 - httpGet: - path: /_localstack/health - port: edge - initialDelaySeconds: 0 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - resources: {} - securityContext: {} - securityContext: {} - serviceAccountName: localstack - volumes: [] - - apiVersion: v1 - data: - ENV_VAR1: value1 - ENV_VAR2: value2 - kind: ConfigMap - metadata: - name: my-config - - apiVersion: v1 - data: - SECRET_KEY1: dmFsdWUx - SECRET_KEY2: dmFsdWUy - kind: Secret - metadata: - name: my-secret - type: Opaque - - apiVersion: v1 - data: - credentials: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg= - kind: Secret - metadata: - name: aws-credentials-{{ spawner.user.name }} - type: Opaque - - apiVersion: helm.crossplane.io/v1beta1 - kind: Release - metadata: - name: wordpress-jupyter-{{ spawner.user.name }} - namespace: jupyter-{{ spawner.user.name }} - spec: - forProvider: - chart: - name: wordpress - repository: oci://registry-1.docker.io/bitnamicharts/ - version: 24.1.4 - namespace: jupyter-{{ spawner.user.name }} - values: - service: - type: ClusterIP - providerConfigRef: - name: helm-provider - - apiVersion: external-secrets.io/v1beta1 - kind: ExternalSecret - metadata: - name: data-by-name - namespace: jupyter-{{ spawner.user.name }} - spec: - data: - - remoteRef: - key: secret-one - property: the-key - secretKey: secret-value - refreshInterval: 15s - secretStoreRef: - kind: ClusterSecretStore - name: k8s-secret-store - target: - creationPolicy: Owner - name: data-by-name - - apiVersion: external-secrets.io/v1beta1 - kind: ExternalSecret - metadata: - name: eoepca-plus-secret-ro - namespace: jupyter-{{ spawner.user.name }} - spec: - data: - - remoteRef: - key: eoepca-plus-secret-ro - property: .dockerconfigjson - secretKey: .dockerconfigjson - refreshInterval: 15s - secretStoreRef: - kind: ClusterSecretStore - name: k8s-secret-store - target: - creationPolicy: Owner - name: eoepca-plus-secret-ro - key: manifests - name: manifests - persist: false - node_selector: {} - pod_env_vars: - CONDA_ENVS_PATH: ' /workspace/.envs' - HOME: /workspace - role_bindings: null - secret_mounts: - - mount_path: /workspace/.aws - name: aws-credentials-{{ spawner.user.name }} - - mount_path: /workspace/.data-by-name - name: data-by-name - - mount_path: /workspace/.docker - name: eoepca-plus-secret-ro - volumes: - - access_modes: - - ReadWriteMany - claim_name: calrissian-claim - name: calrissian-volume - persist: false - size: 50Gi - storage_class: standard - volume_mount: - mount_path: /calrissian - name: calrissian-volume - - access_modes: - - ReadWriteOnce - claim_name: workspace-claim - name: workspace-volume - persist: true - size: 50Gi - storage_class: standard - volume_mount: - mount_path: /workspace - name: workspace-volume -- config_maps: - - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors\ - \ /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram\ - \ 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ - \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\ - \nalias aws=\"aws --endpoint-url=http://localstack:4566\"\n\n# >>> conda initialize\ - \ >>>\n# !! Contents within this block are managed by 'conda init' !!\n__conda_setup=\"\ - $('/opt/conda/bin/conda' 'shell.bash' 'hook' 2> /dev/null)\"\nif [ $? -eq 0\ - \ ]; then\n eval \"$__conda_setup\"\nelse\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\"\ - \ ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\n else\n \ - \ export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset __conda_setup\n\n\ - if [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n . \"/opt/conda/etc/profile.d/mamba.sh\"\ - \nfi\n# <<< conda initialize <<<\n\na={{spawner.user.name}}" - default_mode: null - key: bash-rc - mount_path: /workspace/.bashrc - name: bash-rc - persist: true - readonly: true - default_url: null - definition: - default: false - description: null - display_name: Code Server Medium - kubespawner_override: - cpu_guarantee: null - cpu_limit: 4 - extra_resource_guarantees: {} - extra_resource_limits: {} - image: eoepca/pde-code-server:develop - mem_guarantee: null - mem_limit: 12G - slug: eoepca_coder_slug_m - env_from_config_maps: - - my-config - env_from_secrets: - - my-secret - - data-by-name - groups: - - group-a - - group-b - id: profile_studio_coder2 - image_pull_secrets: - - data: null - name: eoepca-plus-secret-ro - persist: true - init_containers: [] - manifests: - - content: - - apiVersion: v1 - kind: ServiceAccount - metadata: - name: localstack - - apiVersion: rbac.authorization.k8s.io/v1 - kind: Role - metadata: - name: localstack - rules: - - apiGroups: - - '' - resources: - - pods - verbs: - - '*' - - apiGroups: - - '' - resources: - - pods/log - verbs: - - get - - apiGroups: - - '' - resources: - - pods/exec - verbs: - - get - - create - - apiGroups: - - '' - resources: - - services - verbs: - - get - - list - - apiVersion: rbac.authorization.k8s.io/v1 - kind: RoleBinding - metadata: - name: localstack - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: localstack - subjects: - - kind: ServiceAccount - name: localstack - - apiVersion: v1 - kind: Service - metadata: - name: localstack - spec: - ports: - - name: edge - port: 4566 - targetPort: 4566 - - name: external-service-port-4510 - port: 4510 - targetPort: ext-svc-4510 - - name: external-service-port-4511 - port: 4511 - targetPort: ext-svc-4511 - - name: external-service-port-4512 - port: 4512 - targetPort: ext-svc-4512 - - name: external-service-port-4513 - port: 4513 - targetPort: ext-svc-4513 - - name: external-service-port-4514 - port: 4514 - targetPort: ext-svc-4514 - - name: external-service-port-4515 - port: 4515 - targetPort: ext-svc-4515 - - name: external-service-port-4516 - port: 4516 - targetPort: ext-svc-4516 - - name: external-service-port-4517 - port: 4517 - targetPort: ext-svc-4517 - - name: external-service-port-4518 - port: 4518 - targetPort: ext-svc-4518 - - name: external-service-port-4519 - port: 4519 - targetPort: ext-svc-4519 - - name: external-service-port-4520 - port: 4520 - targetPort: ext-svc-4520 - - name: external-service-port-4521 - port: 4521 - targetPort: ext-svc-4521 - - name: external-service-port-4522 - port: 4522 - targetPort: ext-svc-4522 - - name: external-service-port-4523 - port: 4523 - targetPort: ext-svc-4523 - - name: external-service-port-4524 - port: 4524 - targetPort: ext-svc-4524 - - name: external-service-port-4525 - port: 4525 - targetPort: ext-svc-4525 - - name: external-service-port-4526 - port: 4526 - targetPort: ext-svc-4526 - - name: external-service-port-4527 - port: 4527 - targetPort: ext-svc-4527 - - name: external-service-port-4528 - port: 4528 - targetPort: ext-svc-4528 - - name: external-service-port-4529 - port: 4529 - targetPort: ext-svc-4529 - - name: external-service-port-4530 - port: 4530 - targetPort: ext-svc-4530 - - name: external-service-port-4531 - port: 4531 - targetPort: ext-svc-4531 - - name: external-service-port-4532 - port: 4532 - targetPort: ext-svc-4532 - - name: external-service-port-4533 - port: 4533 - targetPort: ext-svc-4533 - - name: external-service-port-4534 - port: 4534 - targetPort: ext-svc-4534 - - name: external-service-port-4535 - port: 4535 - targetPort: ext-svc-4535 - - name: external-service-port-4536 - port: 4536 - targetPort: ext-svc-4536 - - name: external-service-port-4537 - port: 4537 - targetPort: ext-svc-4537 - - name: external-service-port-4538 - port: 4538 - targetPort: ext-svc-4538 - - name: external-service-port-4539 - port: 4539 - targetPort: ext-svc-4539 - - name: external-service-port-4540 - port: 4540 - targetPort: ext-svc-4540 - - name: external-service-port-4541 - port: 4541 - targetPort: ext-svc-4541 - - name: external-service-port-4542 - port: 4542 - targetPort: ext-svc-4542 - - name: external-service-port-4543 - port: 4543 - targetPort: ext-svc-4543 - - name: external-service-port-4544 - port: 4544 - targetPort: ext-svc-4544 - - name: external-service-port-4545 - port: 4545 - targetPort: ext-svc-4545 - - name: external-service-port-4546 - port: 4546 - targetPort: ext-svc-4546 - - name: external-service-port-4547 - port: 4547 - targetPort: ext-svc-4547 - - name: external-service-port-4548 - port: 4548 - targetPort: ext-svc-4548 - - name: external-service-port-4549 - port: 4549 - targetPort: ext-svc-4549 - - name: external-service-port-4550 - port: 4550 - targetPort: ext-svc-4550 - - name: external-service-port-4551 - port: 4551 - targetPort: ext-svc-4551 - - name: external-service-port-4552 - port: 4552 - targetPort: ext-svc-4552 - - name: external-service-port-4553 - port: 4553 - targetPort: ext-svc-4553 - - name: external-service-port-4554 - port: 4554 - targetPort: ext-svc-4554 - - name: external-service-port-4555 - port: 4555 - targetPort: ext-svc-4555 - - name: external-service-port-4556 - port: 4556 - targetPort: ext-svc-4556 - - name: external-service-port-4557 - port: 4557 - targetPort: ext-svc-4557 - - name: external-service-port-4558 - port: 4558 - targetPort: ext-svc-4558 - - name: external-service-port-4559 - port: 4559 - targetPort: ext-svc-4559 - selector: - app.kubernetes.io/instance: localstack - app.kubernetes.io/name: localstack - type: ClusterIP - - apiVersion: apps/v1 - kind: Deployment - metadata: - name: localstack - spec: - replicas: 1 - selector: - matchLabels: - app.kubernetes.io/instance: localstack - app.kubernetes.io/name: localstack - strategy: - type: RollingUpdate - template: - metadata: - labels: - app: localstack-{{ spawner.user.name }} - app.kubernetes.io/instance: localstack - app.kubernetes.io/name: localstack - spec: - containers: - - env: - - name: DEBUG - value: '0' - - name: EXTERNAL_SERVICE_PORTS_START - value: '4510' - - name: EXTERNAL_SERVICE_PORTS_END - value: '4560' - - name: LOCALSTACK_K8S_SERVICE_NAME - value: localstack - - name: LOCALSTACK_K8S_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: LAMBDA_RUNTIME_EXECUTOR - value: docker - - name: LAMBDA_K8S_IMAGE_PREFIX - value: localstack/lambda- - - name: LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT - value: '60' - - name: OVERRIDE_IN_DOCKER - value: '1' - image: localstack/localstack:latest - imagePullPolicy: IfNotPresent - livenessProbe: - failureThreshold: 3 - httpGet: - path: /_localstack/health - port: edge - initialDelaySeconds: 0 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - name: localstack - ports: - - containerPort: 4566 - name: edge - protocol: TCP - - containerPort: 4510 - name: ext-svc-4510 - protocol: TCP - - containerPort: 4511 - name: ext-svc-4511 - protocol: TCP - - containerPort: 4512 - name: ext-svc-4512 - protocol: TCP - - containerPort: 4513 - name: ext-svc-4513 - protocol: TCP - - containerPort: 4514 - name: ext-svc-4514 - protocol: TCP - - containerPort: 4515 - name: ext-svc-4515 - protocol: TCP - - containerPort: 4516 - name: ext-svc-4516 - protocol: TCP - - containerPort: 4517 - name: ext-svc-4517 - protocol: TCP - - containerPort: 4518 - name: ext-svc-4518 - protocol: TCP - - containerPort: 4519 - name: ext-svc-4519 - protocol: TCP - - containerPort: 4520 - name: ext-svc-4520 - protocol: TCP - - containerPort: 4521 - name: ext-svc-4521 - protocol: TCP - - containerPort: 4522 - name: ext-svc-4522 - protocol: TCP - - containerPort: 4523 - name: ext-svc-4523 - protocol: TCP - - containerPort: 4524 - name: ext-svc-4524 - protocol: TCP - - containerPort: 4525 - name: ext-svc-4525 - protocol: TCP - - containerPort: 4526 - name: ext-svc-4526 - protocol: TCP - - containerPort: 4527 - name: ext-svc-4527 - protocol: TCP - - containerPort: 4528 - name: ext-svc-4528 - protocol: TCP - - containerPort: 4529 - name: ext-svc-4529 - protocol: TCP - - containerPort: 4530 - name: ext-svc-4530 - protocol: TCP - - containerPort: 4531 - name: ext-svc-4531 - protocol: TCP - - containerPort: 4532 - name: ext-svc-4532 - protocol: TCP - - containerPort: 4533 - name: ext-svc-4533 - protocol: TCP - - containerPort: 4534 - name: ext-svc-4534 - protocol: TCP - - containerPort: 4535 - name: ext-svc-4535 - protocol: TCP - - containerPort: 4536 - name: ext-svc-4536 - protocol: TCP - - containerPort: 4537 - name: ext-svc-4537 - protocol: TCP - - containerPort: 4538 - name: ext-svc-4538 - protocol: TCP - - containerPort: 4539 - name: ext-svc-4539 - protocol: TCP - - containerPort: 4540 - name: ext-svc-4540 - protocol: TCP - - containerPort: 4541 - name: ext-svc-4541 - protocol: TCP - - containerPort: 4542 - name: ext-svc-4542 - protocol: TCP - - containerPort: 4543 - name: ext-svc-4543 - protocol: TCP - - containerPort: 4544 - name: ext-svc-4544 - protocol: TCP - - containerPort: 4545 - name: ext-svc-4545 - protocol: TCP - - containerPort: 4546 - name: ext-svc-4546 - protocol: TCP - - containerPort: 4547 - name: ext-svc-4547 - protocol: TCP - - containerPort: 4548 - name: ext-svc-4548 - protocol: TCP - - containerPort: 4549 - name: ext-svc-4549 - protocol: TCP - - containerPort: 4550 - name: ext-svc-4550 - protocol: TCP - - containerPort: 4551 - name: ext-svc-4551 - protocol: TCP - - containerPort: 4552 - name: ext-svc-4552 - protocol: TCP - - containerPort: 4553 - name: ext-svc-4553 - protocol: TCP - - containerPort: 4554 - name: ext-svc-4554 - protocol: TCP - - containerPort: 4555 - name: ext-svc-4555 - protocol: TCP - - containerPort: 4556 - name: ext-svc-4556 - protocol: TCP - - containerPort: 4557 - name: ext-svc-4557 - protocol: TCP - - containerPort: 4558 - name: ext-svc-4558 - protocol: TCP - - containerPort: 4559 - name: ext-svc-4559 - protocol: TCP - readinessProbe: - failureThreshold: 3 - httpGet: - path: /_localstack/health - port: edge - initialDelaySeconds: 0 - periodSeconds: 10 - successThreshold: 1 - timeoutSeconds: 1 - resources: {} - securityContext: {} - securityContext: {} - serviceAccountName: localstack - volumes: [] + persist: false + readonly: true + default_url: null + definition: + default: false + description: null + display_name: Code Server Small + kubespawner_override: + cpu_guarantee: null + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: eoepca/pde-code-server:develop + mem_guarantee: null + mem_limit: 8G + slug: eoepca_coder_slug_s + env_from_config_maps: + - my-config + env_from_secrets: + - my-secret + - data-by-name + groups: + - group-a + - group-b + id: profile_studio_coder1 + image_pull_secrets: + - data: null + name: eoepca-plus-secret-ro + persist: true + init_containers: [] + manifests: + - content: - apiVersion: v1 data: ENV_VAR1: value1 @@ -1093,14 +74,14 @@ profiles: - apiVersion: helm.crossplane.io/v1beta1 kind: Release metadata: - name: wordpress-jupyter-{{ spawner.user.name }} + name: localstack-jupyter-{{ spawner.user.name }} namespace: jupyter-{{ spawner.user.name }} spec: forProvider: chart: - name: wordpress - repository: oci://registry-1.docker.io/bitnamicharts/ - version: 24.1.4 + name: localstack + repository: https://localstack.github.io/helm-charts + version: 0.6.20 namespace: jupyter-{{ spawner.user.name }} values: service: @@ -1143,9 +124,231 @@ profiles: target: creationPolicy: Owner name: eoepca-plus-secret-ro + - apiVersion: kubernetes.crossplane.io/v1alpha2 + kind: Object + metadata: + annotations: + uptest.upbound.io/timeout: '60' + name: job-{{ spawner.user.name }} + spec: + forProvider: + manifest: + apiVersion: batch/v1 + kind: Job + metadata: + name: pi + namespace: jupyter-{{ spawner.user.name }} + spec: + backoffLimit: 4 + template: + spec: + containers: + - command: + - perl + - -Mbignum=bpi + - -wle + - print bpi(2000) + image: perl:5.34.0 + name: pi + restartPolicy: Never + providerConfigRef: + name: kubernetes-provider key: manifests name: manifests + persist: true + node_selector: {} + pod_env_vars: + CONDA_ENVS_PATH: ' /workspace/.envs' + HOME: /workspace + role_bindings: null + secret_mounts: + - mount_path: /workspace/.aws + name: aws-credentials-{{ spawner.user.name }} + sub_path: null + - mount_path: /workspace/.data-by-name + name: data-by-name + sub_path: null + - mount_path: /workspace/.docker/config.json + name: eoepca-plus-secret-ro + sub_path: .dockerconfigjson + volumes: + - access_modes: + - ReadWriteMany + claim_name: calrissian-claim + name: calrissian-volume + persist: false + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /calrissian + name: calrissian-volume + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: true + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: + - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors\ + \ /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram\ + \ 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ + \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\ + \n#alias aws=\"aws --endpoint-url=http://localstack:4566\"\n\n# >>> conda initialize\ + \ >>>\n# !! Contents within this block are managed by 'conda init' !!\n__conda_setup=\"\ + $('/opt/conda/bin/conda' 'shell.bash' 'hook' 2> /dev/null)\"\nif [ $? -eq 0\ + \ ]; then\n eval \"$__conda_setup\"\nelse\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\"\ + \ ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\n else\n \ + \ export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset __conda_setup\n\n\ + if [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n . \"/opt/conda/etc/profile.d/mamba.sh\"\ + \nfi\n# <<< conda initialize <<<\n\na={{spawner.user.name}}\n\nalias aws=\"\ + aws --endpoint-url=http://localstack-jupyter-{{spawner.user.name}}:4566\"" + default_mode: null + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc persist: false + readonly: true + default_url: null + definition: + default: false + description: null + display_name: Code Server Medium + kubespawner_override: + cpu_guarantee: null + cpu_limit: 4 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: eoepca/pde-code-server:develop + mem_guarantee: null + mem_limit: 12G + slug: eoepca_coder_slug_m + env_from_config_maps: + - my-config + env_from_secrets: + - my-secret + - data-by-name + groups: + - group-a + - group-b + id: profile_studio_coder2 + image_pull_secrets: + - data: null + name: eoepca-plus-secret-ro + persist: true + init_containers: [] + manifests: + - content: + - apiVersion: v1 + data: + ENV_VAR1: value1 + ENV_VAR2: value2 + kind: ConfigMap + metadata: + name: my-config + - apiVersion: v1 + data: + SECRET_KEY1: dmFsdWUx + SECRET_KEY2: dmFsdWUy + kind: Secret + metadata: + name: my-secret + type: Opaque + - apiVersion: v1 + data: + credentials: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg= + kind: Secret + metadata: + name: aws-credentials-{{ spawner.user.name }} + type: Opaque + - apiVersion: helm.crossplane.io/v1beta1 + kind: Release + metadata: + name: localstack-jupyter-{{ spawner.user.name }} + namespace: jupyter-{{ spawner.user.name }} + spec: + forProvider: + chart: + name: localstack + repository: https://localstack.github.io/helm-charts + version: 0.6.20 + namespace: jupyter-{{ spawner.user.name }} + values: + service: + type: ClusterIP + providerConfigRef: + name: helm-provider + - apiVersion: external-secrets.io/v1beta1 + kind: ExternalSecret + metadata: + name: data-by-name + namespace: jupyter-{{ spawner.user.name }} + spec: + data: + - remoteRef: + key: secret-one + property: the-key + secretKey: secret-value + refreshInterval: 15s + secretStoreRef: + kind: ClusterSecretStore + name: k8s-secret-store + target: + creationPolicy: Owner + name: data-by-name + - apiVersion: external-secrets.io/v1beta1 + kind: ExternalSecret + metadata: + name: eoepca-plus-secret-ro + namespace: jupyter-{{ spawner.user.name }} + spec: + data: + - remoteRef: + key: eoepca-plus-secret-ro + property: .dockerconfigjson + secretKey: .dockerconfigjson + refreshInterval: 15s + secretStoreRef: + kind: ClusterSecretStore + name: k8s-secret-store + target: + creationPolicy: Owner + name: eoepca-plus-secret-ro + - apiVersion: kubernetes.crossplane.io/v1alpha2 + kind: Object + metadata: + annotations: + uptest.upbound.io/timeout: '60' + name: job-{{ spawner.user.name }} + spec: + forProvider: + manifest: + apiVersion: batch/v1 + kind: Job + metadata: + name: pi + namespace: jupyter-{{ spawner.user.name }} + spec: + backoffLimit: 4 + template: + spec: + containers: + - command: + - perl + - -Mbignum=bpi + - -wle + - print bpi(2000) + image: perl:5.34.0 + name: pi + restartPolicy: Never + providerConfigRef: + name: kubernetes-provider + key: manifests + name: manifests + persist: true node_selector: {} pod_env_vars: CONDA_ENVS_PATH: ' /workspace/.envs' @@ -1154,10 +357,13 @@ profiles: secret_mounts: - mount_path: /workspace/.aws name: aws-credentials-{{ spawner.user.name }} + sub_path: null - mount_path: /workspace/.data-by-name name: data-by-name - - mount_path: /workspace/.docker + sub_path: null + - mount_path: /workspace/.docker/config.json name: eoepca-plus-secret-ro + sub_path: .dockerconfigjson volumes: - access_modes: - ReadWriteMany From 0ebeafe2c66ef3d99b8840b77172b0a7a46cf263 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Sun, 29 Dec 2024 12:21:48 +0100 Subject: [PATCH 53/65] fixes the manifest persistence --- application_hub_context/app_hub_context.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index cfd76dc..4bad389 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -1105,9 +1105,11 @@ def dispose(self): # process the manifests manifests = self.config_parser.get_profile_manifests(profile_id=profile_id) - self.spawner.log.info(f"Delete manifest {manifests}") + if manifests: for manifest in manifests: + if manifest.persist: + self.spawner.log.info(f"Persist manifest {manifest.name}") if not manifest.persist: self.spawner.log.info(f"Un-apply manifest {manifest.name}") self.unapply_manifests(manifest_content=manifest.content) From 975ff21f84678f21e84e1f1cb71ab0e8deaf808d Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Sun, 5 Jan 2025 14:25:59 +0100 Subject: [PATCH 54/65] adds render to environment variables --- application_hub_context/app_hub_context.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index 4bad389..9c80946 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -62,7 +62,7 @@ def set_pod_env_vars(self, **kwargs): for key, value in extended_vars.items(): if isinstance(value, str): # If value is a simple string - self.spawner.environment[key] = value + self.spawner.environment[key] = self.render(value) elif isinstance(value, ConfigMapEnvVarReference): # If value must be retrieved from an existing configmap config_map_name = value.from_config_map.name From c1c390ad317f2e50fd685fcc5341a61c573abc80 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 7 Jan 2025 13:55:14 +0100 Subject: [PATCH 55/65] render once --- application_hub_context/app_hub_context.py | 118 ++++++++++----------- application_hub_context/parser.py | 19 +++- 2 files changed, 74 insertions(+), 63 deletions(-) diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index 9c80946..946a02a 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -50,7 +50,7 @@ def __init__( # loads config self.config_parser = ConfigParser.read_file( - config_path=config_path, user_groups=self.user_groups + config_path=config_path, user_groups=self.user_groups, spawner=self.spawner ) # update class dict with kwargs @@ -62,7 +62,7 @@ def set_pod_env_vars(self, **kwargs): for key, value in extended_vars.items(): if isinstance(value, str): # If value is a simple string - self.spawner.environment[key] = self.render(value) + self.spawner.environment[key] = value elif isinstance(value, ConfigMapEnvVarReference): # If value must be retrieved from an existing configmap config_map_name = value.from_config_map.name @@ -621,11 +621,11 @@ def unapply_manifests(self, manifest_content): for k8_object in manifest_content: kind = k8_object.get("kind") self.spawner.log.info( - f"Deleting {kind} {self.render(k8_object.get('metadata', {}).get('name'))}" + f"Deleting {kind} {k8_object.get('metadata', {}).get('name')}" ) metadata = k8_object.get("metadata", {}) namespace = metadata.get("namespace", self.namespace) - name = self.render(metadata.get("name")) + name = metadata.get("name") if not kind or not name: continue @@ -668,11 +668,11 @@ def unapply_manifests(self, manifest_content): ) self.spawner.log.info(f"Response: {response}") elif kind == "ExternalSecret": - self.spawner.log.info(f"Deleting ExternalSecret {name} from namespace {self.render(namespace)}") + self.spawner.log.info(f"Deleting ExternalSecret {name} from namespace {namespace}") self.custom_objects_api.delete_namespaced_custom_object( group="external-secrets.io", version="v1beta1", - namespace=self.render(namespace), + namespace=namespace, plural="externalsecrets", name=name, ) @@ -698,10 +698,10 @@ def __init__(self, namespace, spawner, config_path: str, kubeconfig_file: TextIO def get_profile_list(self): return self.config_parser.get_profiles() - def render(self, manifest): - # render the manifest using the spawner object - template = Template(yaml.dump(manifest)) - return yaml.safe_load(template.render(spawner=self.spawner)) + # def render(self, manifest): + # # render the manifest using the spawner object + # template = Template(yaml.dump(manifest)) + # return yaml.safe_load(template.render(spawner=self.spawner)) def initialise(self): # set the spawner timeout to 10 minutes @@ -814,9 +814,9 @@ def initialise(self): if not self.is_config_map_created(name=config_map.name): self.spawner.log.info(f"Creating configmap {config_map.name}") self.create_configmap( - name=self.render(config_map.name), + name=config_map.name, key=config_map.key, - content=self.render(config_map.content), + content=config_map.content, annotations=None, labels=None, ) @@ -826,7 +826,7 @@ def initialise(self): self.spawner.volume_mounts.extend( [ { - "name": self.render(config_map.name), + "name": config_map.name, "mountPath": config_map.mount_path, "subPath": config_map.key, }, @@ -835,9 +835,9 @@ def initialise(self): self.spawner.volumes.extend( [ { - "name": self.render(config_map.name), + "name": config_map.name, "configMap": { - "name": self.render(config_map.name), + "name": config_map.name, "defaultMode": int(config_map.default_mode, 8) if config_map.default_mode else 0o644, # noqa: E501 @@ -846,12 +846,12 @@ def initialise(self): ] ) self.spawner.log.info( - f"Mounted configmap {self.render(config_map.name)} (key {config_map.key})" # noqa: E501 + f"Mounted configmap {config_map.name} (key {config_map.key})" # noqa: E501 ) except Exception as err: self.spawner.log.error(f"Unexpected {err=}, {type(err)=}") self.spawner.log.error( - f"Skipping creation of configmap {self.render(config_map.name)}" + f"Skipping creation of configmap {config_map.name}" ) # process the volumes @@ -859,25 +859,25 @@ def initialise(self): if volumes: for volume in volumes: - self.spawner.log.info(f"Mounting volume {self.render(volume.name)}") + self.spawner.log.info(f"Mounting volume {volume.name}") try: - if not self.is_pvc_created(name=self.render(volume.claim_name)): + if not self.is_pvc_created(name=volume.claim_name): self.spawner.log.info( - f"Creating volume claim {self.render(volume.claim_name)})" + f"Creating volume claim {volume.claim_name})" ) self.create_pvc( - name=self.render(volume.claim_name), + name=volume.claim_name, access_modes=volume.access_modes, size=volume.size, storage_class=volume.storage_class, ) self.spawner.log.info( - f"Mounting volume {self.render(volume.name)} (claim {self.render(volume.claim_name)}))" + f"Mounting volume {volume.name} (claim {volume.claim_name}))" ) self.spawner.volume_mounts.extend( [ { - "name": self.render(volume.volume_mount.name), + "name": volume.volume_mount.name, "mountPath": volume.volume_mount.mount_path, }, ] @@ -886,9 +886,9 @@ def initialise(self): self.spawner.volumes.extend( [ { - "name": self.render(volume.name), + "name": volume.name, "persistentVolumeClaim": { - "claimName": self.render(volume.claim_name) + "claimName": volume.claim_name }, } ] @@ -896,7 +896,7 @@ def initialise(self): except Exception as err: self.spawner.log.error(f"Unexpected {err}, {type(err)}") - self.spawner.log.error(f"Skipping creation of volume {self.render(volume.name)}") + self.spawner.log.error(f"Skipping creation of volume {volume.name}") # process the role bindings role_bindings = self.config_parser.get_profile_role_bindings( @@ -905,17 +905,17 @@ def initialise(self): if role_bindings: for role_binding in role_bindings: - self.spawner.log.info(f"Creating role binding {self.render(role_binding.name)}") + self.spawner.log.info(f"Creating role binding {role_binding.name}") try: # checking if role binding is already created - if not self.is_role_binding_created(name=self.render(role_binding.name)): + if not self.is_role_binding_created(name=role_binding.name): # checking if role is already created - if not self.is_role_created(name=self.render(role_binding.role.name)): + if not self.is_role_created(name=role_binding.role.name): self.spawner.log.info( - f"Creating role {self.render(role_binding.role.name)}" + f"Creating role {role_binding.role.name}" ) self.create_role( - name=self.render(role_binding.role.name), + name=role_binding.role.name, verbs=role_binding.role.verbs, resources=role_binding.role.resources, api_groups=role_binding.role.api_groups, @@ -923,7 +923,7 @@ def initialise(self): # creating role binding self.create_role_binding( - name=self.render(role_binding.name), + name=role_binding.name, role=role_binding.role, subjects=role_binding.subjects, ) @@ -931,7 +931,7 @@ def initialise(self): except Exception as err: self.spawner.log.error(f"Unexpected {err}, {type(err)}") self.spawner.log.error( - f"Skipping creation of role binding {self.render(role_binding.name)}" + f"Skipping creation of role binding {role_binding.name}" ) # process the role bindings image_pull_secrets = self.config_parser.get_profile_image_pull_secrets( @@ -941,29 +941,29 @@ def initialise(self): if image_pull_secrets: for image_pull_secret in image_pull_secrets: self.spawner.log.info( - f"Create image pull secret {self.render(image_pull_secret.name)}" + f"Create image pull secret {image_pull_secret.name}" ) try: if not self.is_image_pull_secret_created( - name=self.render(image_pull_secret.name) + name=image_pull_secret.name ): self.spawner.log.info( - f"Creating image pull secret {self.render(image_pull_secret.name)})" + f"Creating image pull secret {image_pull_secret.name})" ) self.create_image_pull_secret( - name=self.render(image_pull_secret.name), + name=image_pull_secret.name, data=image_pull_secret.data, ) except Exception as err: self.spawner.log.error(f"Unexpected {err}, {type(err)}") self.spawner.log.error( "Skipping creation of image pull secret" - f" {self.render(image_pull_secret.name)}" + f" {image_pull_secret.name}" ) self.spawner.log.info( - f"Patch service account with image pull secret {self.render(image_pull_secret.name)}" + f"Patch service account with image pull secret {image_pull_secret.name}" ) - self.patch_service_account(secret_name=self.render(image_pull_secret.name)) + self.patch_service_account(secret_name=image_pull_secret.name) # process the init containers init_containers = self.config_parser.get_profile_init_containers( @@ -973,13 +973,13 @@ def initialise(self): if init_containers: for init_container in init_containers: self.spawner.log.info( - f"Create init container {self.render(init_container.name)}" + f"Create init container {init_container.name}" ) try: self.spawner.init_containers.extend( [ { - "name": self.render(init_container.name), + "name": init_container.name, "image": init_container.image, "command": init_container.command, "volumeMounts": [ @@ -994,7 +994,7 @@ def initialise(self): except Exception as err: self.spawner.log.error(f"Unexpected {err}, {type(err)}") self.spawner.log.error( - f"Skipping creation of init container {self.render(init_container.name)}" + f"Skipping creation of init container {init_container.name}" ) @@ -1011,7 +1011,7 @@ def initialise(self): self.spawner.extra_container_config["env_from"].append( { "configMapRef": { - "name": self.render(env_from_config_map), + "name": env_from_config_map, }}) self.spawner.log.info(f"extra_container_config {self.spawner.extra_container_config}") @@ -1029,7 +1029,7 @@ def initialise(self): self.spawner.extra_container_config["env_from"].append( { "secretRef": { - "name": self.render(env_from_secret), + "name": env_from_secret, }}) self.spawner.log.info(f"extra_container_config {self.spawner.extra_container_config}") @@ -1040,11 +1040,11 @@ def initialise(self): if secret_mounts: for secret_mount in secret_mounts: - self.spawner.log.info(f"Mounting secret {self.render(secret_mount.name)}") + self.spawner.log.info(f"Mounting secret {secret_mount.name}") self.spawner.volume_mounts.extend( [ { - "name": self.render(secret_mount.name), + "name": secret_mount.name, "mountPath": secret_mount.mount_path, "subPath": secret_mount.sub_path, }, @@ -1054,15 +1054,15 @@ def initialise(self): self.spawner.volumes.extend( [ { - "name": self.render(secret_mount.name), + "name": secret_mount.name, "secret": { - "secretName": self.render(secret_mount.name), + "secretName": secret_mount.name, }, } ] ) - self.spawner.log.info(f"Mounted secret {self.render(secret_mount.name)}") + self.spawner.log.info(f"Mounted secret {secret_mount.name}") def dispose(self): profile_id = self.config_parser.get_profile_by_slug(slug=self.profile_slug).id @@ -1076,9 +1076,9 @@ def dispose(self): if config_maps: for config_map in config_maps: if not config_map.persist: - self.spawner.log.info(f"Dispose config map {self.render(config_map.name)}") + self.spawner.log.info(f"Dispose config map {config_map.name}") - self.delete_config_map(name=self.render(config_map.name)) + self.delete_config_map(name=config_map.name) # deal with the volumes volumes = self.config_parser.get_profile_volumes(profile_id=profile_id) @@ -1087,10 +1087,10 @@ def dispose(self): for volume in volumes: if not volume.persist: self.spawner.log.info( - f"Dispose volume {self.render(volume.name)}, claim {self.render(volume.claim_name)}" + f"Dispose volume {volume.name}, claim {volume.claim_name}" ) - self.delete_pvc(name=self.render(volume.claim_name)) + self.delete_pvc(name=volume.claim_name) # deal with the role bindings role_bindings = self.config_parser.get_profile_role_bindings( @@ -1100,7 +1100,7 @@ def dispose(self): if role_bindings: for role_binding in role_bindings: if not role_binding.persist: - self.spawner.log.info(f"Dispose role binding {self.render(role_binding.name)}") + self.spawner.log.info(f"Dispose role binding {role_binding.name}") self.delete_role_binding(role_binding=role_binding) # process the manifests @@ -1123,9 +1123,9 @@ def dispose(self): for image_pull_secret in image_pull_secrets: if not image_pull_secret.persist: self.spawner.log.info( - f"Dispose image pull secret {self.render(image_pull_secret.name)}" + f"Dispose image pull secret {image_pull_secret.name}" ) - self.delete_image_pull_secret(name=self.render(image_pull_secret.name)) + self.delete_image_pull_secret(name=image_pull_secret.name) service_account_body = ( self.core_v1_api.read_namespaced_service_account( @@ -1133,13 +1133,13 @@ def dispose(self): ) ) for elem in service_account_body.image_pull_secrets: - if elem.name == self.render(image_pull_secret.name): + if elem.name == image_pull_secret.name: service_account_body.image_pull_secrets.remove( {"name": elem.name} ) self.spawner.log.info( - f"Remove image pull secret {self.render(image_pull_secret.name)}" + f"Remove image pull secret {image_pull_secret.name}" " from default service account" ) diff --git a/application_hub_context/parser.py b/application_hub_context/parser.py index a60c290..679c3b1 100644 --- a/application_hub_context/parser.py +++ b/application_hub_context/parser.py @@ -1,5 +1,5 @@ import yaml - +from jinja2 import Template from application_hub_context.models import Config undefined = object() @@ -12,13 +12,24 @@ def __init__(self, config_data, user_groups): self.user_groups = user_groups @classmethod - def read_file(cls, config_path, user_groups): + def read_file(cls, config_path, user_groups, spawner): """reads a config file encoded in YAML""" with open(config_path, "r") as stream: try: - config_data = yaml.safe_load(stream) + # Read the file as a raw string + raw_content = stream.read() + + # Render the content as a Jinja2 template + template = Template(raw_content) + rendered_content = template.render(spawner=spawner) + + # Parse the rendered content as YAML + config_data = yaml.safe_load(rendered_content) + except yaml.YAMLError as exc: - print(exc) + print(f"YAML Error: {exc}") + except Exception as e: + print(f"Error: {e}") return cls(config_data=config_data, user_groups=user_groups) From 0e5a79e50e444f585b2ac7023cd5056757961844 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 7 Jan 2025 13:55:47 +0100 Subject: [PATCH 56/65] adds config generator script --- config-generator/__init__.py | 0 config-generator/generate-config.sh | 4 + config-generator/generate_config.py | 152 ++++++++++++++++++++++++++++ config-generator/helpers.py | 62 ++++++++++++ 4 files changed, 218 insertions(+) create mode 100644 config-generator/__init__.py create mode 100755 config-generator/generate-config.sh create mode 100644 config-generator/generate_config.py create mode 100644 config-generator/helpers.py diff --git a/config-generator/__init__.py b/config-generator/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config-generator/generate-config.sh b/config-generator/generate-config.sh new file mode 100755 index 0000000..279e056 --- /dev/null +++ b/config-generator/generate-config.sh @@ -0,0 +1,4 @@ +#exit 0 +export PYTHONPATH=$PWD/`dirname $0`/. + +.env-config-generator/bin/python3 -m generate_config \ No newline at end of file diff --git a/config-generator/generate_config.py b/config-generator/generate_config.py new file mode 100644 index 0000000..d28c85b --- /dev/null +++ b/config-generator/generate_config.py @@ -0,0 +1,152 @@ +from models import * +import yaml +import os +from loguru import logger +from helpers import ( + load_config_map, + load_manifests, + create_init_container, + load_init_script, +) +import click + +storage_class_rwo = "standard" +storage_class_rwx = "standard" +profiles = [] +workspace_volume_size = "50Gi" +calrissian_volume_size = "50Gi" +image = "eoepca/pde-code-server:develop" +node_selector = {} + +# get the current directory +current_dir = os.path.dirname(os.path.realpath(__file__)) + +# load the manifests + +# localstack_manifest +localstack_manifest = load_manifests( + name="localstack", + key="localstack", + file_path=os.path.join(current_dir, "manifests/manifest.yaml"), +) + +# Dask Gateway manifest +dask_gateway_manifest = load_manifests( + name="dask-gateway", + key="dask-gateway", + file_path=os.path.join(current_dir, "manifests/dask-gateway.yaml"), +) + +# volumes + +workspace_volume = Volume( + name="workspace-volume", + size=workspace_volume_size, + claim_name="workspace-claim", + mount_path="/workspace", + storage_class=storage_class_rwo, + access_modes=["ReadWriteOnce"], + volume_mount=VolumeMount(name="workspace-volume", mount_path="/workspace"), + persist=True, +) + +calrissian_volume = Volume( + name="calrissian-volume", + claim_name="calrissian-claim", + size=calrissian_volume_size, + storage_class=storage_class_rwx, + access_modes=["ReadWriteMany"], + volume_mount=VolumeMount(name="calrissian-volume", mount_path="/calrissian"), + persist=False, +) + + +bash_login_cm = load_config_map( + name="bash-login", + key="bash-login", + file_name=os.path.join(current_dir, "config-maps/bash-login"), + mount_path="/etc/profile.d/bash-login.sh", +) + +bash_rc_cm = load_config_map( + name="bash-rc", + key="bash-rc", + file_name=os.path.join(current_dir, "config-maps/bash-rc"), + mount_path="/workspace/.bashrc", +) + +init_cm = load_init_script(os.path.join(current_dir, "config-maps/init.sh")) + +init_container = create_init_container( + image=image, + volume=workspace_volume, + mount_path="/calrissian", +) + +profile_1 = Profile( + id=f"profile_1", + groups=["group-a", "group-b"], + definition=ProfileDefinition( + display_name="Coder demo init script", + description="This profile is used to demonstrate the use of an init script", + slug="profile_1", + default=False, + kubespawner_override=KubespawnerOverride( + cpu_guarantee=1, + cpu_limit=2, + mem_guarantee="4G", + mem_limit="6G", + image=image, + ), + ), + node_selector=node_selector, + volumes=[calrissian_volume, workspace_volume], + config_maps=[ + init_cm, + ConfigMap( + name="dask-gateway-config", + key="gateway", + mount_path="/etc/dask/gateway.yaml", + readonly=True, + ), + ConfigMap( + name="dask-gateway-config-ws", + key="gateway", + mount_path="/workspace/.config/dask/gateway.yaml", + readonly=True, + ), + bash_login_cm, + bash_rc_cm, + ], + pod_env_vars={ + "HOME": "/workspace", + "CONDA_ENVS_PATH": "/workspace/.envs", + "CONDARC": "/workspace/.condarc", + "XDG_RUNTIME_DIR": "/workspace/.local", + "CODE_SERVER_WS": "/workspace/mastering-app-package", + "DASK_GATEWAY": "http://traefik-dask-gw-jupyter-{{ spawner.user.name }}-dask-gateway.jupyter-{{ spawner.user.name }}.svc.cluster.local:80", + }, + init_containers=[init_container], + manifests=[localstack_manifest, dask_gateway_manifest], + env_from_config_maps=["my-config"], + env_from_secrets=["my-secret", "data-by-name"], + secret_mounts=[ + SecretMount( + name="aws-credentials-{{ spawner.user.name }}", mount_path="/workspace/.aws" + ), + SecretMount(name="data-by-name", mount_path="/workspace/.data-by-name"), + SecretMount( + name="eoepca-plus-secret-ro", + mount_path="/workspace/.docker/config.json", + sub_path=".dockerconfigjson", + ), + ], + image_pull_secrets=[ImagePullSecret(name="eoepca-plus-secret-ro")], +) + +profiles.append(profile_1) + +config = Config(profiles=profiles) + +with open("files/hub/config.yml", "w") as file: + yaml.dump(config.dict(), file, width=200) diff --git a/config-generator/helpers.py b/config-generator/helpers.py new file mode 100644 index 0000000..1ae9d98 --- /dev/null +++ b/config-generator/helpers.py @@ -0,0 +1,62 @@ +import yaml + +from models import ( + Volume, + InitContainer, + VolumeMount, + ConfigMap, + InitContainerVolumeMount, + Manifest, +) + + +def load_config_map(name, key, file_name, mount_path): + with open(file_name, "r") as f: + content = f.read() + return ConfigMap( + name=name, + key=key, + content=content, + readonly=True, + persist=True, + mount_path=mount_path, + ) + + +def load_manifests(name, key, file_path): + with open(file_path, "r") as f: + content = yaml.safe_load_all(f.read()) + return Manifest( + name=name, key=key, readonly=True, persist=False, content=[e for e in content] + ) + + +def create_init_container(image: str, volume: Volume, mount_path: str) -> InitContainer: + + init_context_volume_mount = InitContainerVolumeMount( + mount_path="/opt/init/.init.sh", name="init", sub_path="init" + ) + + return InitContainer( + name="init-file-on-volume", + image=image, + command=["sh", "-c", "sh /opt/init/.init.sh"], + volume_mounts=[ + VolumeMount(name=volume.name, mount_path=mount_path), + init_context_volume_mount, + ], + ) + + +def load_init_script(file_name: str) -> ConfigMap: + with open(file_name, "r") as f: + content = f.read() + return ConfigMap( + name="init", + key="init", + content=content, + readonly=True, + persist=False, + mount_path="/opt/init/.init.sh", + default_mode="0660", + ) From d6d418d5d1e0f8f497c146af998d159380c6f086 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 7 Jan 2025 13:56:09 +0100 Subject: [PATCH 57/65] adds config generation in skaffold --- skaffold.yaml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/skaffold.yaml b/skaffold.yaml index 02a88ec..fc5b677 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -58,7 +58,11 @@ profiles: setFiles: configYml: ./files/hub/config.yml jupyterConfig: ./files/hub/jupyterhub_config.py - + hooks: + before: + - host: + command: ["sh", "-c", "config-generator/generate-config.sh"] + os: [darwin, linux] manifests: rawYaml: - sk-k8s/cluster-role-binding.yaml From 2ab01682f92d9baaa57d56bee1db56e4742bd0f2 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 7 Jan 2025 13:57:42 +0100 Subject: [PATCH 58/65] fixes typo in tests --- tests/tests_k8s.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/tests_k8s.py b/tests/tests_k8s.py index 316f6a0..3b6fe57 100644 --- a/tests/tests_k8s.py +++ b/tests/tests_k8s.py @@ -45,7 +45,7 @@ class TestK8s(unittest.TestCase): def setUpClass(cls): os.environ["KUBECONFIG"] = "/home/mambauser/.kube/kubeconfig-t2-dev.yaml" - cls.app_hub_context = DefaulfApplicationHubContext( + cls.app_hub_context = DefaultApplicationHubContext( namespace="a_namespace", spawner=spawner, config_path="tests/data/config.yml", @@ -54,7 +54,7 @@ def setUpClass(cls): ) def test_obj(self): - self.assertIs(type(self.app_hub_context), DefaulfApplicationHubContext) + self.assertIs(type(self.app_hub_context), DefaultApplicationHubContext) def test_client(self): self.assertIsInstance(self.app_hub_context._get_core_v1_api(), client.CoreV1Api) From 151d36ea002e00cfe9822daf96a378fde8b3df23 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 7 Jan 2025 17:20:14 +0100 Subject: [PATCH 59/65] updates --- config-generator/config-maps/bash-rc | 6 +- config-generator/generate_config.py | 7 +- config-generator/manifests/dask-gateway.yaml | 109 +++ config-generator/manifests/kaniko.yaml | 58 ++ config-generator/manifests/manifest.yaml | 38 +- files/hub/config.yml | 730 ++++++------------- 6 files changed, 416 insertions(+), 532 deletions(-) create mode 100644 config-generator/manifests/dask-gateway.yaml create mode 100644 config-generator/manifests/kaniko.yaml diff --git a/config-generator/config-maps/bash-rc b/config-generator/config-maps/bash-rc index 0d387fd..3f643a6 100644 --- a/config-generator/config-maps/bash-rc +++ b/config-generator/config-maps/bash-rc @@ -3,7 +3,7 @@ alias calrissian="/opt/conda/bin/calrissian --pod-nodeselectors /etc/calrissian/ alias cwltool="/opt/conda/bin/cwltool --podman" . /home/jovyan/.bashrc -alias aws="aws --endpoint-url=http://localstack:4566" +#alias aws="aws --endpoint-url=http://localstack:4566" # >>> conda initialize >>> # !! Contents within this block are managed by 'conda init' !! @@ -24,4 +24,6 @@ if [ -f "/opt/conda/etc/profile.d/mamba.sh" ]; then fi # <<< conda initialize <<< -a={{spawner.user.name}} \ No newline at end of file +a={{spawner.user.name}} + +alias aws="aws --endpoint-url=http://localstack-jupyter-{{spawner.user.name}}:4566" \ No newline at end of file diff --git a/config-generator/generate_config.py b/config-generator/generate_config.py index d28c85b..4847780 100644 --- a/config-generator/generate_config.py +++ b/config-generator/generate_config.py @@ -37,6 +37,11 @@ file_path=os.path.join(current_dir, "manifests/dask-gateway.yaml"), ) +kaniko_manifest = load_manifests( + name="kaniko", + key="kaniko", + file_path=os.path.join(current_dir, "manifests/kaniko.yaml"), +) # volumes workspace_volume = Volume( @@ -127,7 +132,7 @@ "DASK_GATEWAY": "http://traefik-dask-gw-jupyter-{{ spawner.user.name }}-dask-gateway.jupyter-{{ spawner.user.name }}.svc.cluster.local:80", }, init_containers=[init_container], - manifests=[localstack_manifest, dask_gateway_manifest], + manifests=[localstack_manifest, dask_gateway_manifest, kaniko_manifest], env_from_config_maps=["my-config"], env_from_secrets=["my-secret", "data-by-name"], secret_mounts=[ diff --git a/config-generator/manifests/dask-gateway.yaml b/config-generator/manifests/dask-gateway.yaml new file mode 100644 index 0000000..c760862 --- /dev/null +++ b/config-generator/manifests/dask-gateway.yaml @@ -0,0 +1,109 @@ +apiVersion: helm.crossplane.io/v1beta1 +kind: Release +metadata: + name: dask-gw-jupyter-{{ spawner.user.name }} + namespace: jupyter-{{ spawner.user.name }} +spec: + forProvider: + chart: + name: dask-gateway + version: "2024.1.0" + repository: https://helm.dask.org + namespace: jupyter-{{ spawner.user.name }} + values: + gateway: + backend: + image: + name: ghcr.io/fabricebrito/dev-platform-dask-gateway/worker + tag: 1.0.0 + extraConfig: + dask_gateway_config.py: | + c = get_config() + from dask_gateway_server.options import Options, String, Integer, Float + c.Backend.cluster_options = Options( + Float("worker_cores_limit", default=1, label="Worker Cores Limit"), + Float("worker_cores", default=1, label="Worker Cores"), + String("worker_memory", default="1 G", label="Worker Memory"), + String("image", default="daskgateway/dask-worker:latest", label="Worker Image") + ) + traefik: + service: + type: "ClusterIP" + providerConfigRef: + name: helm-provider +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: dask-gateway-config +data: + gateway: | + gateway: + address: http://traefik-dask-gw-jupyter-{{ spawner.user.name }}-dask-gateway.jupyter-{{ spawner.user.name }}.svc.cluster.local:80 + + cluster: + options: + image: "ghcr.io/fabricebrito/dev-platform-dask-gateway/worker:1.0.0" + worker_cores: 0.5 + worker_cores_limit: 1 + worker_memory: "4 G" +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: dask-gateway-config-ws +data: + gateway: | + gateway: + address: http://traefik-dask-gw-jupyter-{{ spawner.user.name }}-dask-gateway.jupyter-{{ spawner.user.name }}.svc.cluster.local:80 + + cluster: + options: + image: "ghcr.io/fabricebrito/dev-platform-dask-gateway/worker:1.0.0" + worker_cores: 0.5 + worker_cores_limit: 1 + worker_memory: "4 G" +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: access-services + namespace: jupyter-{{ spawner.user.name }} +rules: + - verbs: + - get + - list + - watch + apiGroups: + - '' + resources: + - services + - pods/exec + - verbs: + - get + - list + - watch + apiGroups: + - '' + resources: + - pods + - verbs: + - create + apiGroups: + - '' + resources: + - pods/portforward +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: bind-default-to-services + namespace: jupyter-{{ spawner.user.name }} +subjects: + - kind: ServiceAccount + name: default + namespace: jupyter-{{ spawner.user.name }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: access-services \ No newline at end of file diff --git a/config-generator/manifests/kaniko.yaml b/config-generator/manifests/kaniko.yaml new file mode 100644 index 0000000..e7280b9 --- /dev/null +++ b/config-generator/manifests/kaniko.yaml @@ -0,0 +1,58 @@ + apiVersion: v1 + kind: Pod + metadata: + name: kaniko-build + namespace: jupyter-{{ spawner.user.name }} + spec: + containers: + - name: kaniko + image: gcr.io/kaniko-project/executor:debug + command: + - /bin/sh + - -c + - "sleep infinity" # Keep the container running for manual access + volumeMounts: + - name: build-context + mountPath: /calrissian # Mount your build files here + - name: kaniko-secret + mountPath: /kaniko/.docker + restartPolicy: Never + volumes: + - name: build-context + persistentVolumeClaim: + claimName: calrissian-claim # Replace with your PVC name + - name: kaniko-secret + secret: + secretName: kaniko-secret + items: + - key: .dockerconfigjson + path: config.json +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: Role +metadata: + name: pod-exec + namespace: jupyter-{{ spawner.user.name }} +rules: + - verbs: + - get + - list + - watch + apiGroups: + - '' + resources: + - pods/exec +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: RoleBinding +metadata: + name: bind-default-to-opd-exec + namespace: jupyter-{{ spawner.user.name }} +subjects: + - kind: ServiceAccount + name: default + namespace: jupyter-{{ spawner.user.name }} +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: pod-exec diff --git a/config-generator/manifests/manifest.yaml b/config-generator/manifests/manifest.yaml index 9ab5c8b..829b1f7 100644 --- a/config-generator/manifests/manifest.yaml +++ b/config-generator/manifests/manifest.yaml @@ -445,24 +445,24 @@ metadata: type: Opaque data: credentials: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg= ---- -apiVersion: helm.crossplane.io/v1beta1 -kind: Release -metadata: - name: wordpress-jupyter-{{ spawner.user.name }} - namespace: jupyter-{{ spawner.user.name }} -spec: - forProvider: - chart: - name: wordpress - version: "24.1.4" - repository: oci://registry-1.docker.io/bitnamicharts/ - namespace: jupyter-{{ spawner.user.name }} - values: - service: - type: ClusterIP - providerConfigRef: - name: helm-provider +# --- +# apiVersion: helm.crossplane.io/v1beta1 +# kind: Release +# metadata: +# name: wordpress-jupyter-{{ spawner.user.name }} +# namespace: jupyter-{{ spawner.user.name }} +# spec: +# forProvider: +# chart: +# name: wordpress +# version: "24.1.4" +# repository: oci://registry-1.docker.io/bitnamicharts/ +# namespace: jupyter-{{ spawner.user.name }} +# values: +# service: +# type: ClusterIP +# providerConfigRef: +# name: helm-provider --- apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret @@ -502,4 +502,4 @@ spec: - secretKey: .dockerconfigjson remoteRef: key: eoepca-plus-secret-ro # name of the secret - property: ".dockerconfigjson" # key in the secret \ No newline at end of file + property: ".dockerconfigjson" # key in the secret diff --git a/files/hub/config.yml b/files/hub/config.yml index b5f2e1a..9d2021b 100644 --- a/files/hub/config.yml +++ b/files/hub/config.yml @@ -1,390 +1,4 @@ profiles: -- config_maps: - - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors\ - \ /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram\ - \ 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ - \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\ - \n#alias aws=\"aws --endpoint-url=http://localstack:4566\"\n\n# >>> conda initialize\ - \ >>>\n# !! Contents within this block are managed by 'conda init' !!\n__conda_setup=\"\ - $('/opt/conda/bin/conda' 'shell.bash' 'hook' 2> /dev/null)\"\nif [ $? -eq 0\ - \ ]; then\n eval \"$__conda_setup\"\nelse\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\"\ - \ ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\n else\n \ - \ export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset __conda_setup\n\n\ - if [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n . \"/opt/conda/etc/profile.d/mamba.sh\"\ - \nfi\n# <<< conda initialize <<<\n\na={{spawner.user.name}}\n\nalias aws=\"\ - aws --endpoint-url=http://localstack-jupyter-{{spawner.user.name}}:4566\"" - default_mode: null - key: bash-rc - mount_path: /workspace/.bashrc - name: bash-rc - persist: false - readonly: true - default_url: null - definition: - default: false - description: null - display_name: Code Server Small - kubespawner_override: - cpu_guarantee: null - cpu_limit: 2 - extra_resource_guarantees: {} - extra_resource_limits: {} - image: eoepca/pde-code-server:develop - mem_guarantee: null - mem_limit: 8G - slug: eoepca_coder_slug_s - env_from_config_maps: - - my-config - env_from_secrets: - - my-secret - - data-by-name - groups: - - group-a - - group-b - id: profile_studio_coder1 - image_pull_secrets: - - data: null - name: eoepca-plus-secret-ro - persist: true - init_containers: [] - manifests: - - content: - - apiVersion: v1 - data: - ENV_VAR1: value1 - ENV_VAR2: value2 - kind: ConfigMap - metadata: - name: my-config - - apiVersion: v1 - data: - SECRET_KEY1: dmFsdWUx - SECRET_KEY2: dmFsdWUy - kind: Secret - metadata: - name: my-secret - type: Opaque - - apiVersion: v1 - data: - credentials: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg= - kind: Secret - metadata: - name: aws-credentials-{{ spawner.user.name }} - type: Opaque - - apiVersion: helm.crossplane.io/v1beta1 - kind: Release - metadata: - name: localstack-jupyter-{{ spawner.user.name }} - namespace: jupyter-{{ spawner.user.name }} - spec: - forProvider: - chart: - name: localstack - repository: https://localstack.github.io/helm-charts - version: 0.6.20 - namespace: jupyter-{{ spawner.user.name }} - values: - service: - type: ClusterIP - providerConfigRef: - name: helm-provider - - apiVersion: external-secrets.io/v1beta1 - kind: ExternalSecret - metadata: - name: data-by-name - namespace: jupyter-{{ spawner.user.name }} - spec: - data: - - remoteRef: - key: secret-one - property: the-key - secretKey: secret-value - refreshInterval: 15s - secretStoreRef: - kind: ClusterSecretStore - name: k8s-secret-store - target: - creationPolicy: Owner - name: data-by-name - - apiVersion: external-secrets.io/v1beta1 - kind: ExternalSecret - metadata: - name: eoepca-plus-secret-ro - namespace: jupyter-{{ spawner.user.name }} - spec: - data: - - remoteRef: - key: eoepca-plus-secret-ro - property: .dockerconfigjson - secretKey: .dockerconfigjson - refreshInterval: 15s - secretStoreRef: - kind: ClusterSecretStore - name: k8s-secret-store - target: - creationPolicy: Owner - name: eoepca-plus-secret-ro - - apiVersion: kubernetes.crossplane.io/v1alpha2 - kind: Object - metadata: - annotations: - uptest.upbound.io/timeout: '60' - name: job-{{ spawner.user.name }} - spec: - forProvider: - manifest: - apiVersion: batch/v1 - kind: Job - metadata: - name: pi - namespace: jupyter-{{ spawner.user.name }} - spec: - backoffLimit: 4 - template: - spec: - containers: - - command: - - perl - - -Mbignum=bpi - - -wle - - print bpi(2000) - image: perl:5.34.0 - name: pi - restartPolicy: Never - providerConfigRef: - name: kubernetes-provider - key: manifests - name: manifests - persist: true - node_selector: {} - pod_env_vars: - CONDA_ENVS_PATH: ' /workspace/.envs' - HOME: /workspace - role_bindings: null - secret_mounts: - - mount_path: /workspace/.aws - name: aws-credentials-{{ spawner.user.name }} - sub_path: null - - mount_path: /workspace/.data-by-name - name: data-by-name - sub_path: null - - mount_path: /workspace/.docker/config.json - name: eoepca-plus-secret-ro - sub_path: .dockerconfigjson - volumes: - - access_modes: - - ReadWriteMany - claim_name: calrissian-claim - name: calrissian-volume - persist: false - size: 50Gi - storage_class: standard - volume_mount: - mount_path: /calrissian - name: calrissian-volume - - access_modes: - - ReadWriteOnce - claim_name: workspace-claim - name: workspace-volume - persist: true - size: 50Gi - storage_class: standard - volume_mount: - mount_path: /workspace - name: workspace-volume -- config_maps: - - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors\ - \ /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram\ - \ 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\ - \nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\ - \n#alias aws=\"aws --endpoint-url=http://localstack:4566\"\n\n# >>> conda initialize\ - \ >>>\n# !! Contents within this block are managed by 'conda init' !!\n__conda_setup=\"\ - $('/opt/conda/bin/conda' 'shell.bash' 'hook' 2> /dev/null)\"\nif [ $? -eq 0\ - \ ]; then\n eval \"$__conda_setup\"\nelse\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\"\ - \ ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\n else\n \ - \ export PATH=\"/srv/conda/bin:$PATH\"\n fi\nfi\nunset __conda_setup\n\n\ - if [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n . \"/opt/conda/etc/profile.d/mamba.sh\"\ - \nfi\n# <<< conda initialize <<<\n\na={{spawner.user.name}}\n\nalias aws=\"\ - aws --endpoint-url=http://localstack-jupyter-{{spawner.user.name}}:4566\"" - default_mode: null - key: bash-rc - mount_path: /workspace/.bashrc - name: bash-rc - persist: false - readonly: true - default_url: null - definition: - default: false - description: null - display_name: Code Server Medium - kubespawner_override: - cpu_guarantee: null - cpu_limit: 4 - extra_resource_guarantees: {} - extra_resource_limits: {} - image: eoepca/pde-code-server:develop - mem_guarantee: null - mem_limit: 12G - slug: eoepca_coder_slug_m - env_from_config_maps: - - my-config - env_from_secrets: - - my-secret - - data-by-name - groups: - - group-a - - group-b - id: profile_studio_coder2 - image_pull_secrets: - - data: null - name: eoepca-plus-secret-ro - persist: true - init_containers: [] - manifests: - - content: - - apiVersion: v1 - data: - ENV_VAR1: value1 - ENV_VAR2: value2 - kind: ConfigMap - metadata: - name: my-config - - apiVersion: v1 - data: - SECRET_KEY1: dmFsdWUx - SECRET_KEY2: dmFsdWUy - kind: Secret - metadata: - name: my-secret - type: Opaque - - apiVersion: v1 - data: - credentials: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg= - kind: Secret - metadata: - name: aws-credentials-{{ spawner.user.name }} - type: Opaque - - apiVersion: helm.crossplane.io/v1beta1 - kind: Release - metadata: - name: localstack-jupyter-{{ spawner.user.name }} - namespace: jupyter-{{ spawner.user.name }} - spec: - forProvider: - chart: - name: localstack - repository: https://localstack.github.io/helm-charts - version: 0.6.20 - namespace: jupyter-{{ spawner.user.name }} - values: - service: - type: ClusterIP - providerConfigRef: - name: helm-provider - - apiVersion: external-secrets.io/v1beta1 - kind: ExternalSecret - metadata: - name: data-by-name - namespace: jupyter-{{ spawner.user.name }} - spec: - data: - - remoteRef: - key: secret-one - property: the-key - secretKey: secret-value - refreshInterval: 15s - secretStoreRef: - kind: ClusterSecretStore - name: k8s-secret-store - target: - creationPolicy: Owner - name: data-by-name - - apiVersion: external-secrets.io/v1beta1 - kind: ExternalSecret - metadata: - name: eoepca-plus-secret-ro - namespace: jupyter-{{ spawner.user.name }} - spec: - data: - - remoteRef: - key: eoepca-plus-secret-ro - property: .dockerconfigjson - secretKey: .dockerconfigjson - refreshInterval: 15s - secretStoreRef: - kind: ClusterSecretStore - name: k8s-secret-store - target: - creationPolicy: Owner - name: eoepca-plus-secret-ro - - apiVersion: kubernetes.crossplane.io/v1alpha2 - kind: Object - metadata: - annotations: - uptest.upbound.io/timeout: '60' - name: job-{{ spawner.user.name }} - spec: - forProvider: - manifest: - apiVersion: batch/v1 - kind: Job - metadata: - name: pi - namespace: jupyter-{{ spawner.user.name }} - spec: - backoffLimit: 4 - template: - spec: - containers: - - command: - - perl - - -Mbignum=bpi - - -wle - - print bpi(2000) - image: perl:5.34.0 - name: pi - restartPolicy: Never - providerConfigRef: - name: kubernetes-provider - key: manifests - name: manifests - persist: true - node_selector: {} - pod_env_vars: - CONDA_ENVS_PATH: ' /workspace/.envs' - HOME: /workspace - role_bindings: null - secret_mounts: - - mount_path: /workspace/.aws - name: aws-credentials-{{ spawner.user.name }} - sub_path: null - - mount_path: /workspace/.data-by-name - name: data-by-name - sub_path: null - - mount_path: /workspace/.docker/config.json - name: eoepca-plus-secret-ro - sub_path: .dockerconfigjson - volumes: - - access_modes: - - ReadWriteMany - claim_name: calrissian-claim - name: calrissian-volume - persist: false - size: 50Gi - storage_class: standard - volume_mount: - mount_path: /calrissian - name: calrissian-volume - - access_modes: - - ReadWriteOnce - claim_name: workspace-claim - name: workspace-volume - persist: true - size: 50Gi - storage_class: standard - volume_mount: - mount_path: /workspace - name: workspace-volume - config_maps: - content: 'set -x @@ -416,6 +30,41 @@ profiles: name: init persist: false readonly: true + - content: null + default_mode: null + key: gateway + mount_path: /etc/dask/gateway.yaml + name: dask-gateway-config + persist: true + readonly: true + - content: null + default_mode: null + key: gateway + mount_path: /workspace/.config/dask/gateway.yaml + name: dask-gateway-config-ws + persist: true + readonly: true + - content: 'source /workspace/.bashrc + + ' + default_mode: null + key: bash-login + mount_path: /etc/profile.d/bash-login.sh + name: bash-login + persist: true + readonly: true + - content: "alias ll=\"ls -l\"\nalias calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors /etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json --max-ram 16G --max-cores \"8\"\ + \ --tmp-outdir-prefix /calrissian/tmp/ --outdir /calrissian/\"\nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\n. /home/jovyan/.bashrc\n\n#alias aws=\"aws --endpoint-url=http://localstack:4566\"\ + \n\n# >>> conda initialize >>>\n# !! Contents within this block are managed by 'conda init' !!\n__conda_setup=\"$('/opt/conda/bin/conda' 'shell.bash' 'hook' 2> /dev/null)\"\nif [ $? -eq 0 ]; then\n\ + \ eval \"$__conda_setup\"\nelse\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; then\n . \"/opt/conda/etc/profile.d/conda.sh\"\n else\n export PATH=\"/srv/conda/bin:$PATH\"\ + \n fi\nfi\nunset __conda_setup\n\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\n . \"/opt/conda/etc/profile.d/mamba.sh\"\nfi\n# <<< conda initialize <<<\n\na={{spawner.user.name}}\n\ + \nalias aws=\"aws --endpoint-url=http://localstack-jupyter-{{spawner.user.name}}:4566\"" + default_mode: null + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: true + readonly: true default_url: null definition: default: false @@ -429,14 +78,20 @@ profiles: image: eoepca/pde-code-server:develop mem_guarantee: 4G mem_limit: 6G - slug: eoepca_demo_init_script - env_from_config_maps: null - env_from_secrets: null + slug: profile_1 + env_from_config_maps: + - my-config + env_from_secrets: + - my-secret + - data-by-name groups: - group-a - group-b - id: profile_demo_init_script - image_pull_secrets: [] + id: profile_1 + image_pull_secrets: + - data: null + name: eoepca-plus-secret-ro + persist: true init_containers: - command: - sh @@ -445,108 +100,11 @@ profiles: image: eoepca/pde-code-server:develop name: init-file-on-volume volume_mounts: - - mount_path: /workspace + - mount_path: /calrissian name: workspace-volume - mount_path: /opt/init/.init.sh name: init sub_path: init - manifests: null - node_selector: {} - pod_env_vars: - CODE_SERVER_WS: /workspace/mastering-app-package - CONDARC: /workspace/.condarc - CONDA_ENVS_PATH: /workspace/.envs - HOME: /workspace - XDG_RUNTIME_DIR: /workspace/.local - role_bindings: null - secret_mounts: null - volumes: - - access_modes: - - ReadWriteMany - claim_name: calrissian-claim - name: calrissian-volume - persist: false - size: 50Gi - storage_class: standard - volume_mount: - mount_path: /calrissian - name: calrissian-volume - - access_modes: - - ReadWriteOnce - claim_name: workspace-claim - name: workspace-volume - persist: true - size: 50Gi - storage_class: standard - volume_mount: - mount_path: /workspace - name: workspace-volume -- config_maps: [] - default_url: null - definition: - default: false - description: Jupyter Lab with Python 3.11 - display_name: Jupyter Lab - kubespawner_override: - cpu_guarantee: 1 - cpu_limit: 2 - extra_resource_guarantees: {} - extra_resource_limits: {} - image: jupyter/scipy-notebook - mem_guarantee: 4G - mem_limit: 6G - slug: eoepca_jupyter_lab - env_from_config_maps: null - env_from_secrets: null - groups: - - group-c - id: profile_jupyter_lab - image_pull_secrets: [] - init_containers: [] - manifests: null - node_selector: {} - pod_env_vars: - HOME: /workspace - XDG_CONFIG_HOME: /workspace/.config - XDG_RUNTIME_DIR: /workspace/.local - role_bindings: null - secret_mounts: null - volumes: - - access_modes: - - ReadWriteOnce - claim_name: workspace-claim - name: workspace-volume - persist: true - size: 50Gi - storage_class: standard - volume_mount: - mount_path: /workspace - name: workspace-volume -- config_maps: [] - default_url: null - definition: - default: false - description: Jupyter Lab with Python 3.11 - demoes the use of an image pull secret - display_name: Jupyter Lab - profile 2 - kubespawner_override: - cpu_guarantee: 1 - cpu_limit: 2 - extra_resource_guarantees: {} - extra_resource_limits: {} - image: jupyter/scipy-notebook - mem_guarantee: 4G - mem_limit: 6G - slug: eoepca_jupyter_lab_2 - env_from_config_maps: null - env_from_secrets: null - groups: - - group-c - id: profile_jupyter_lab_2 - image_pull_secrets: - - data: '' - name: cr-config - persist: false - init_containers: [] manifests: - content: - apiVersion: v1 @@ -1001,23 +559,6 @@ profiles: metadata: name: aws-credentials-{{ spawner.user.name }} type: Opaque - - apiVersion: helm.crossplane.io/v1beta1 - kind: Release - metadata: - name: wordpress-jupyter-{{ spawner.user.name }} - namespace: jupyter-{{ spawner.user.name }} - spec: - forProvider: - chart: - name: wordpress - repository: oci://registry-1.docker.io/bitnamicharts/ - version: 24.1.4 - namespace: jupyter-{{ spawner.user.name }} - values: - service: - type: ClusterIP - providerConfigRef: - name: helm-provider - apiVersion: external-secrets.io/v1beta1 kind: ExternalSecret metadata: @@ -1054,17 +595,186 @@ profiles: target: creationPolicy: Owner name: eoepca-plus-secret-ro - key: manifests - name: manifests + key: localstack + name: localstack + persist: false + - content: + - apiVersion: helm.crossplane.io/v1beta1 + kind: Release + metadata: + name: dask-gw-jupyter-{{ spawner.user.name }} + namespace: jupyter-{{ spawner.user.name }} + spec: + forProvider: + chart: + name: dask-gateway + repository: https://helm.dask.org + version: 2024.1.0 + namespace: jupyter-{{ spawner.user.name }} + values: + gateway: + backend: + image: + name: ghcr.io/fabricebrito/dev-platform-dask-gateway/worker + tag: 1.0.0 + extraConfig: + dask_gateway_config.py: "c = get_config()\nfrom dask_gateway_server.options import Options, String, Integer, Float\nc.Backend.cluster_options = Options(\n Float(\"worker_cores_limit\",\ + \ default=1, label=\"Worker Cores Limit\"),\n Float(\"worker_cores\", default=1, label=\"Worker Cores\"),\n String(\"worker_memory\", default=\"1 G\", label=\"Worker Memory\"),\n String(\"\ + image\", default=\"daskgateway/dask-worker:latest\", label=\"Worker Image\")\n)\n" + traefik: + service: + type: ClusterIP + providerConfigRef: + name: helm-provider + - apiVersion: v1 + data: + gateway: "gateway:\n address: http://traefik-dask-gw-jupyter-{{ spawner.user.name }}-dask-gateway.jupyter-{{ spawner.user.name }}.svc.cluster.local:80\n\n cluster:\n options: \n image:\ + \ \"ghcr.io/fabricebrito/dev-platform-dask-gateway/worker:1.0.0\"\n worker_cores: 0.5\n worker_cores_limit: 1\n worker_memory: \"4 G\"\n" + kind: ConfigMap + metadata: + name: dask-gateway-config + - apiVersion: v1 + data: + gateway: "gateway:\n address: http://traefik-dask-gw-jupyter-{{ spawner.user.name }}-dask-gateway.jupyter-{{ spawner.user.name }}.svc.cluster.local:80\n\n cluster:\n options: \n image:\ + \ \"ghcr.io/fabricebrito/dev-platform-dask-gateway/worker:1.0.0\"\n worker_cores: 0.5\n worker_cores_limit: 1\n worker_memory: \"4 G\"\n" + kind: ConfigMap + metadata: + name: dask-gateway-config-ws + - apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + name: access-services + namespace: jupyter-{{ spawner.user.name }} + rules: + - apiGroups: + - '' + resources: + - services + - pods/exec + verbs: + - get + - list + - watch + - apiGroups: + - '' + resources: + - pods + verbs: + - get + - list + - watch + - apiGroups: + - '' + resources: + - pods/portforward + verbs: + - create + - apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + name: bind-default-to-services + namespace: jupyter-{{ spawner.user.name }} + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: access-services + subjects: + - kind: ServiceAccount + name: default + namespace: jupyter-{{ spawner.user.name }} + key: dask-gateway + name: dask-gateway + persist: false + - content: + - apiVersion: v1 + kind: Pod + metadata: + name: kaniko-build + namespace: jupyter-{{ spawner.user.name }} + spec: + containers: + - command: + - /bin/sh + - -c + - sleep infinity + image: gcr.io/kaniko-project/executor:debug + name: kaniko + volumeMounts: + - mountPath: /calrissian + name: build-context + - mountPath: /kaniko/.docker + name: kaniko-secret + restartPolicy: Never + volumes: + - name: build-context + persistentVolumeClaim: + claimName: calrissian-claim + - name: kaniko-secret + secret: + items: + - key: .dockerconfigjson + path: config.json + secretName: kaniko-secret + - apiVersion: rbac.authorization.k8s.io/v1 + kind: Role + metadata: + name: pod-exec + namespace: jupyter-{{ spawner.user.name }} + rules: + - apiGroups: + - '' + resources: + - pods/exec + verbs: + - get + - list + - watch + - apiVersion: rbac.authorization.k8s.io/v1 + kind: RoleBinding + metadata: + name: bind-default-to-opd-exec + namespace: jupyter-{{ spawner.user.name }} + roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: pod-exec + subjects: + - kind: ServiceAccount + name: default + namespace: jupyter-{{ spawner.user.name }} + key: kaniko + name: kaniko persist: false node_selector: {} pod_env_vars: + CODE_SERVER_WS: /workspace/mastering-app-package + CONDARC: /workspace/.condarc + CONDA_ENVS_PATH: /workspace/.envs + DASK_GATEWAY: http://traefik-dask-gw-jupyter-{{ spawner.user.name }}-dask-gateway.jupyter-{{ spawner.user.name }}.svc.cluster.local:80 HOME: /workspace - XDG_CONFIG_HOME: /workspace/.config XDG_RUNTIME_DIR: /workspace/.local role_bindings: null - secret_mounts: null + secret_mounts: + - mount_path: /workspace/.aws + name: aws-credentials-{{ spawner.user.name }} + sub_path: null + - mount_path: /workspace/.data-by-name + name: data-by-name + sub_path: null + - mount_path: /workspace/.docker/config.json + name: eoepca-plus-secret-ro + sub_path: .dockerconfigjson volumes: + - access_modes: + - ReadWriteMany + claim_name: calrissian-claim + name: calrissian-volume + persist: false + size: 50Gi + storage_class: standard + volume_mount: + mount_path: /calrissian + name: calrissian-volume - access_modes: - ReadWriteOnce claim_name: workspace-claim From e0099dc335ac807f87e4df3cb5a171b2454d79ea Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 7 Jan 2025 17:22:38 +0100 Subject: [PATCH 60/65] clean-up --- demo-eoepca-values.yaml | 677 ---------------------------------------- demo-values.yaml | 677 ---------------------------------------- skaffold.yaml | 59 ---- 3 files changed, 1413 deletions(-) delete mode 100644 demo-eoepca-values.yaml delete mode 100644 demo-values.yaml diff --git a/demo-eoepca-values.yaml b/demo-eoepca-values.yaml deleted file mode 100644 index 6551ff4..0000000 --- a/demo-eoepca-values.yaml +++ /dev/null @@ -1,677 +0,0 @@ - -# fullnameOverride and nameOverride distinguishes blank strings, null values, -# and non-blank strings. For more details, see the configuration reference. -fullnameOverride: "" -nameOverride: - -# custom can contain anything you want to pass to the hub pod, as all passed -# Helm template values will be made available there. -custom: {} - -# imagePullSecret is configuration to create a k8s Secret that Helm chart's pods -# can get credentials from to pull their images. -imagePullSecret: - create: false - automaticReferenceInjection: true - registry: - username: - password: - email: -# imagePullSecrets is configuration to reference the k8s Secret resources the -# Helm chart's pods can get credentials from to pull their images. -imagePullSecrets: [] - -# hub relates to the hub pod, responsible for running JupyterHub, its configured -# Authenticator class KubeSpawner, and its configured Proxy class -# ConfigurableHTTPProxy. KubeSpawner creates the user pods, and -# ConfigurableHTTPProxy speaks with the actual ConfigurableHTTPProxy server in -# the proxy pod. -hub: - revisionHistoryLimit: - config: - JupyterHub: - admin_access: true - authenticator_class: dummy - service: - type: ClusterIP - annotations: {} - ports: - nodePort: - extraPorts: [] - loadBalancerIP: - baseUrl: / - cookieSecret: - initContainers: [] - nodeSelector: {} - tolerations: [] - concurrentSpawnLimit: 64 - consecutiveFailureLimit: 5 - activeServerLimit: - deploymentStrategy: - ## type: Recreate - ## - sqlite-pvc backed hubs require the Recreate deployment strategy as a - ## typical PVC storage can only be bound to one pod at the time. - ## - JupyterHub isn't designed to support being run in parallell. More work - ## needs to be done in JupyterHub itself for a fully highly available (HA) - ## deployment of JupyterHub on k8s is to be possible. - type: Recreate - db: - type: sqlite-pvc - upgrade: - pvc: - annotations: {} - selector: {} - accessModes: - - ReadWriteOnce - storage: 10Gi - subPath: - storageClassName: standard - url: - password: - labels: {} - annotations: {} - command: [] - args: [] - extraConfig: {} - extraFiles: {} - extraEnv: { - JUPYTERHUB_ENV: "eoepca", - JUPYTERHUB_SINGLE_USER_IMAGE_NOTEBOOKS: "", - } - extraContainers: [] - extraVolumes: [] - # - name: application-hub-config - # configMap: - # name: application-hub-jupyter-config - # defaultMode: 0744 - # - name: application-hub-config-theme - # configMap: - # name: application-hub-jupyter-config-theme - # defaultMode: 0744 - extraVolumeMounts: [] - # - name: application-hub-config - # mountPath: /usr/local/etc/applicationhub - # - name: application-hub-config-theme - # mountPath: /opt/jupyterhub/template/ - - image: - name: hubimage - tag: "dev" - pullPolicy: - pullSecrets: [] - resources: {} - podSecurityContext: - fsGroup: 1000 - containerSecurityContext: - runAsUser: 1000 - runAsGroup: 1000 - allowPrivilegeEscalation: true - lifecycle: {} - loadRoles: {} - services: {} - pdb: - enabled: false - maxUnavailable: - minAvailable: 1 - networkPolicy: - enabled: false - ingress: [] - egress: [] - egressAllowRules: - cloudMetadataServer: true - dnsPortsPrivateIPs: true - nonPrivateIPs: true - privateIPs: true - interNamespaceAccessLabels: ignore - allowedIngressPorts: [] - allowNamedServers: true - namedServerLimitPerUser: - authenticatePrometheus: - redirectToServer: - shutdownOnLogout: - templatePaths: [] - templateVars: {} - livenessProbe: - # The livenessProbe's aim to give JupyterHub sufficient time to startup but - # be able to restart if it becomes unresponsive for ~5 min. - enabled: true - initialDelaySeconds: 300 - periodSeconds: 10 - failureThreshold: 30 - timeoutSeconds: 3 - readinessProbe: - # The readinessProbe's aim is to provide a successful startup indication, - # but following that never become unready before its livenessProbe fail and - # restarts it if needed. To become unready following startup serves no - # purpose as there are no other pod to fallback to in our non-HA deployment. - enabled: true - initialDelaySeconds: 0 - periodSeconds: 2 - failureThreshold: 1000 - timeoutSeconds: 1 - existingSecret: - serviceAccount: - create: true - name: - annotations: {} - extraPodSpec: {} - -rbac: - create: true - -# proxy relates to the proxy pod, the proxy-public service, and the autohttps -# pod and proxy-http service. -proxy: - secretToken: '8af21006c7c3dc381c5d3b4b27e2c99e6311d5fc243fqbf9a14646020197d67c' - annotations: {} - deploymentStrategy: - ## type: Recreate - ## - JupyterHub's interaction with the CHP proxy becomes a lot more robust - ## with this configuration. To understand this, consider that JupyterHub - ## during startup will interact a lot with the k8s service to reach a - ## ready proxy pod. If the hub pod during a helm upgrade is restarting - ## directly while the proxy pod is making a rolling upgrade, the hub pod - ## could end up running a sequence of interactions with the old proxy pod - ## and finishing up the sequence of interactions with the new proxy pod. - ## As CHP proxy pods carry individual state this is very error prone. One - ## outcome when not using Recreate as a strategy has been that user pods - ## have been deleted by the hub pod because it considered them unreachable - ## as it only configured the old proxy pod but not the new before trying - ## to reach them. - type: Recreate - ## rollingUpdate: - ## - WARNING: - ## This is required to be set explicitly blank! Without it being - ## explicitly blank, k8s will let eventual old values under rollingUpdate - ## remain and then the Deployment becomes invalid and a helm upgrade would - ## fail with an error like this: - ## - ## UPGRADE FAILED - ## Error: Deployment.apps "proxy" is invalid: spec.strategy.rollingUpdate: Forbidden: may not be specified when strategy `type` is 'Recreate' - ## Error: UPGRADE FAILED: Deployment.apps "proxy" is invalid: spec.strategy.rollingUpdate: Forbidden: may not be specified when strategy `type` is 'Recreate' - rollingUpdate: - # service relates to the proxy-public service - service: - type: ClusterIP - labels: {} - annotations: {} - nodePorts: - http: - https: - disableHttpPort: false - extraPorts: [] - loadBalancerIP: - loadBalancerSourceRanges: [] - # chp relates to the proxy pod, which is responsible for routing traffic based - # on dynamic configuration sent from JupyterHub to CHP's REST API. - chp: - revisionHistoryLimit: - containerSecurityContext: - runAsUser: 65534 # nobody user - runAsGroup: 65534 # nobody group - allowPrivilegeEscalation: false - image: - name: jupyterhub/configurable-http-proxy - # tag is automatically bumped to new patch versions by the - # watch-dependencies.yaml workflow. - # - tag: "4.5.3" # https://github.com/jupyterhub/configurable-http-proxy/releases - pullPolicy: - pullSecrets: [] - extraCommandLineFlags: [] - livenessProbe: - enabled: true - initialDelaySeconds: 60 - periodSeconds: 10 - failureThreshold: 30 - timeoutSeconds: 3 - readinessProbe: - enabled: true - initialDelaySeconds: 0 - periodSeconds: 2 - failureThreshold: 1000 - timeoutSeconds: 1 - resources: {} - defaultTarget: - errorTarget: - extraEnv: {} - nodeSelector: {} - tolerations: [] - networkPolicy: - enabled: false - ingress: [] - egress: [] - egressAllowRules: - cloudMetadataServer: true - dnsPortsPrivateIPs: true - nonPrivateIPs: true - privateIPs: true - interNamespaceAccessLabels: ignore - allowedIngressPorts: [http, https] - pdb: - enabled: false - maxUnavailable: - minAvailable: 1 - extraPodSpec: {} - # traefik relates to the autohttps pod, which is responsible for TLS - # termination when proxy.https.type=letsencrypt. - traefik: - revisionHistoryLimit: - containerSecurityContext: - runAsUser: 65534 # nobody user - runAsGroup: 65534 # nobody group - allowPrivilegeEscalation: false - image: - name: traefik - # tag is automatically bumped to new patch versions by the - # watch-dependencies.yaml workflow. - # - tag: "v2.8.4" # ref: https://hub.docker.com/_/traefik?tab=tags - pullPolicy: - pullSecrets: [] - hsts: - includeSubdomains: false - preload: false - maxAge: 15724800 # About 6 months - resources: {} - labels: {} - extraInitContainers: [] - extraEnv: {} - extraVolumes: [] - extraVolumeMounts: [] - extraStaticConfig: {} - extraDynamicConfig: {} - nodeSelector: {} - tolerations: [] - extraPorts: [] - networkPolicy: - enabled: false - ingress: [] - egress: [] - egressAllowRules: - cloudMetadataServer: true - dnsPortsPrivateIPs: true - nonPrivateIPs: true - privateIPs: true - interNamespaceAccessLabels: ignore - allowedIngressPorts: [http, https] - pdb: - enabled: false - maxUnavailable: - minAvailable: 1 - serviceAccount: - create: true - name: - annotations: {} - extraPodSpec: {} - secretSync: - containerSecurityContext: - runAsUser: 65534 # nobody user - runAsGroup: 65534 # nobody group - allowPrivilegeEscalation: false - image: - name: jupyterhub/k8s-secret-sync - tag: "2.0.0" - pullPolicy: - pullSecrets: [] - resources: {} - labels: {} - https: - enabled: false - type: letsencrypt - #type: letsencrypt, manual, offload, secret - letsencrypt: - contactEmail: - # Specify custom server here (https://acme-staging-v02.api.letsencrypt.org/directory) to hit staging LE - acmeServer: https://acme-v02.api.letsencrypt.org/directory - manual: - key: - cert: - secret: - name: - key: tls.key - crt: tls.crt - hosts: [] - -# singleuser relates to the configuration of KubeSpawner which runs in the hub -# pod, and its spawning of user pods such as jupyter-myusername. -singleuser: - podNameTemplate: - extraTolerations: [] - nodeSelector: {"k8s.scaleway.com/pool-name": "processing-node-pool-dev"} - extraNodeAffinity: - required: [] - preferred: [] - extraPodAffinity: - required: [] - preferred: [] - extraPodAntiAffinity: - required: [] - preferred: [] - networkTools: - image: - name: jupyterhub/k8s-network-tools - tag: "2.0.0" - pullPolicy: - pullSecrets: [] - resources: {} - cloudMetadata: - # block set to true will append a privileged initContainer using the - # iptables to block the sensitive metadata server at the provided ip. - blockWithIptables: true - ip: 169.254.169.254 - networkPolicy: - enabled: false - ingress: [] - egress: [] - egressAllowRules: - cloudMetadataServer: false - dnsPortsPrivateIPs: true - nonPrivateIPs: true - privateIPs: false - interNamespaceAccessLabels: ignore - allowedIngressPorts: [] - events: true - extraAnnotations: {} - extraLabels: - hub.jupyter.org/network-access-hub: "true" - extraFiles: {} - extraEnv: {} - lifecycleHooks: {} - initContainers: [] - extraContainers: [] - allowPrivilegeEscalation: false - uid: 1000 - fsGid: 100 - serviceAccountName: - storage: - type: dynamic - extraLabels: {} - extraVolumes: [] - extraVolumeMounts: [] - static: - pvcName: - subPath: "{username}" - capacity: 10Gi - homeMountPath: /home/jovyan - dynamic: - storageClass: standard - pvcNameTemplate: claim-{username}{servername} - volumeNameTemplate: volume-{username}{servername} - storageAccessModes: [ReadWriteOnce] - image: - name: jupyterhub/k8s-singleuser-sample - tag: "2.0.0" - pullPolicy: - pullSecrets: [] - startTimeout: 6000 - cpu: - limit: - guarantee: - memory: - limit: - guarantee: 1G - extraResource: - limits: {} - guarantees: {} - cmd: jupyterhub-singleuser - defaultUrl: - extraPodConfig: {} - profileList: [] - -# scheduling relates to the user-scheduler pods and user-placeholder pods. -scheduling: - userScheduler: - enabled: true - revisionHistoryLimit: - replicas: 2 - logLevel: 4 - # plugins are configured on the user-scheduler to make us score how we - # schedule user pods in a way to help us schedule on the most busy node. By - # doing this, we help scale down more effectively. It isn't obvious how to - # enable/disable scoring plugins, and configure them, to accomplish this. - # - # plugins ref: https://kubernetes.io/docs/reference/scheduling/config/#scheduling-plugins-1 - # migration ref: https://kubernetes.io/docs/reference/scheduling/config/#scheduler-configuration-migrations - # - plugins: - score: - # These scoring plugins are enabled by default according to - # https://kubernetes.io/docs/reference/scheduling/config/#scheduling-plugins - # 2022-02-22. - # - # Enabled with high priority: - # - NodeAffinity - # - InterPodAffinity - # - NodeResourcesFit - # - ImageLocality - # Remains enabled with low default priority: - # - TaintToleration - # - PodTopologySpread - # - VolumeBinding - # Disabled for scoring: - # - NodeResourcesBalancedAllocation - # - disabled: - # We disable these plugins (with regards to scoring) to not interfere - # or complicate our use of NodeResourcesFit. - - name: NodeResourcesBalancedAllocation - # Disable plugins to be allowed to enable them again with a different - # weight and avoid an error. - - name: NodeAffinity - - name: InterPodAffinity - - name: NodeResourcesFit - - name: ImageLocality - enabled: - - name: NodeAffinity - weight: 14631 - - name: InterPodAffinity - weight: 1331 - - name: NodeResourcesFit - weight: 121 - - name: ImageLocality - weight: 11 - pluginConfig: - # Here we declare that we should optimize pods to fit based on a - # MostAllocated strategy instead of the default LeastAllocated. - - name: NodeResourcesFit - args: - scoringStrategy: - resources: - - name: cpu - weight: 1 - - name: memory - weight: 1 - type: MostAllocated - containerSecurityContext: - runAsUser: 65534 # nobody user - runAsGroup: 65534 # nobody group - allowPrivilegeEscalation: false - image: - # IMPORTANT: Bumping the minor version of this binary should go hand in - # hand with an inspection of the user-scheduelrs RBAC resources - # that we have forked in - # templates/scheduling/user-scheduler/rbac.yaml. - # - # Debugging advice: - # - # - Is configuration of kube-scheduler broken in - # templates/scheduling/user-scheduler/configmap.yaml? - # - # - Is the kube-scheduler binary's compatibility to work - # against a k8s api-server that is too new or too old? - # - # - You can update the GitHub workflow that runs tests to - # include "deploy/user-scheduler" in the k8s namespace report - # and reduce the user-scheduler deployments replicas to 1 in - # dev-config.yaml to get relevant logs from the user-scheduler - # pods. Inspect the "Kubernetes namespace report" action! - # - # - Typical failures are that kube-scheduler fails to search for - # resources via its "informers", and won't start trying to - # schedule pods before they succeed which may require - # additional RBAC permissions or that the k8s api-server is - # aware of the resources. - # - # - If "successfully acquired lease" can be seen in the logs, it - # is a good sign kube-scheduler is ready to schedule pods. - # - name: k8s.gcr.io/kube-scheduler - # tag is automatically bumped to new patch versions by the - # watch-dependencies.yaml workflow. The minor version is pinned in the - # workflow, and should be updated there if a minor version bump is done - # here. - # - tag: "v1.23.10" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md - pullPolicy: - pullSecrets: [] - nodeSelector: {} - tolerations: [] - labels: {} - annotations: {} - pdb: - enabled: true - maxUnavailable: 1 - minAvailable: - resources: {} - serviceAccount: - create: true - name: - annotations: {} - extraPodSpec: {} - podPriority: - enabled: false - globalDefault: false - defaultPriority: 0 - imagePullerPriority: -5 - userPlaceholderPriority: -10 - userPlaceholder: - enabled: true - image: - name: k8s.gcr.io/pause - # tag is automatically bumped to new patch versions by the - # watch-dependencies.yaml workflow. - # - # If you update this, also update prePuller.pause.image.tag - # - tag: "3.8" - pullPolicy: - pullSecrets: [] - revisionHistoryLimit: - replicas: 0 - labels: {} - annotations: {} - containerSecurityContext: - runAsUser: 65534 # nobody user - runAsGroup: 65534 # nobody group - allowPrivilegeEscalation: false - resources: {} - corePods: - tolerations: - - key: hub.jupyter.org/dedicated - operator: Equal - value: core - effect: NoSchedule - - key: hub.jupyter.org_dedicated - operator: Equal - value: core - effect: NoSchedule - nodeAffinity: - matchNodePurpose: prefer - userPods: - tolerations: - - key: hub.jupyter.org/dedicated - operator: Equal - value: user - effect: NoSchedule - - key: hub.jupyter.org_dedicated - operator: Equal - value: user - effect: NoSchedule - nodeAffinity: - matchNodePurpose: prefer - -# prePuller relates to the hook|continuous-image-puller DaemonsSets -prePuller: - revisionHistoryLimit: - labels: {} - annotations: {} - resources: {} - containerSecurityContext: - runAsUser: 65534 # nobody user - runAsGroup: 65534 # nobody group - allowPrivilegeEscalation: false - extraTolerations: [] - # hook relates to the hook-image-awaiter Job and hook-image-puller DaemonSet - hook: - enabled: true - pullOnlyOnChanges: true - # image and the configuration below relates to the hook-image-awaiter Job - image: - name: jupyterhub/k8s-image-awaiter - tag: "2.0.0" - pullPolicy: - pullSecrets: [] - containerSecurityContext: - runAsUser: 65534 # nobody user - runAsGroup: 65534 # nobody group - allowPrivilegeEscalation: false - podSchedulingWaitDuration: 10 - nodeSelector: {} - tolerations: [] - resources: {} - serviceAccount: - create: true - name: - annotations: {} - continuous: - enabled: true - pullProfileListImages: true - extraImages: {} - pause: - containerSecurityContext: - runAsUser: 65534 # nobody user - runAsGroup: 65534 # nobody group - allowPrivilegeEscalation: false - image: - name: k8s.gcr.io/pause - # tag is automatically bumped to new patch versions by the - # watch-dependencies.yaml workflow. - # - # If you update this, also update scheduling.userPlaceholder.image.tag - # - tag: "3.8" - pullPolicy: - pullSecrets: [] - -ingress: - enabled: false - annotations: {} - ingressClassName: - hosts: [] - pathSuffix: - pathType: Prefix - tls: [] - -# cull relates to the jupyterhub-idle-culler service, responsible for evicting -# inactive singleuser pods. -# -# The configuration below, except for enabled, corresponds to command-line flags -# for jupyterhub-idle-culler as documented here: -# https://github.com/jupyterhub/jupyterhub-idle-culler#as-a-standalone-script -# -cull: - enabled: true - users: false # --cull-users - adminUsers: true # --cull-admin-users - removeNamedServers: false # --remove-named-servers - timeout: 3600 # --timeout - every: 600 # --cull-every - concurrency: 10 # --concurrency - maxAge: 0 # --max-age - -debug: - enabled: false - -global: - safeToShowValues: false diff --git a/demo-values.yaml b/demo-values.yaml deleted file mode 100644 index 6551ff4..0000000 --- a/demo-values.yaml +++ /dev/null @@ -1,677 +0,0 @@ - -# fullnameOverride and nameOverride distinguishes blank strings, null values, -# and non-blank strings. For more details, see the configuration reference. -fullnameOverride: "" -nameOverride: - -# custom can contain anything you want to pass to the hub pod, as all passed -# Helm template values will be made available there. -custom: {} - -# imagePullSecret is configuration to create a k8s Secret that Helm chart's pods -# can get credentials from to pull their images. -imagePullSecret: - create: false - automaticReferenceInjection: true - registry: - username: - password: - email: -# imagePullSecrets is configuration to reference the k8s Secret resources the -# Helm chart's pods can get credentials from to pull their images. -imagePullSecrets: [] - -# hub relates to the hub pod, responsible for running JupyterHub, its configured -# Authenticator class KubeSpawner, and its configured Proxy class -# ConfigurableHTTPProxy. KubeSpawner creates the user pods, and -# ConfigurableHTTPProxy speaks with the actual ConfigurableHTTPProxy server in -# the proxy pod. -hub: - revisionHistoryLimit: - config: - JupyterHub: - admin_access: true - authenticator_class: dummy - service: - type: ClusterIP - annotations: {} - ports: - nodePort: - extraPorts: [] - loadBalancerIP: - baseUrl: / - cookieSecret: - initContainers: [] - nodeSelector: {} - tolerations: [] - concurrentSpawnLimit: 64 - consecutiveFailureLimit: 5 - activeServerLimit: - deploymentStrategy: - ## type: Recreate - ## - sqlite-pvc backed hubs require the Recreate deployment strategy as a - ## typical PVC storage can only be bound to one pod at the time. - ## - JupyterHub isn't designed to support being run in parallell. More work - ## needs to be done in JupyterHub itself for a fully highly available (HA) - ## deployment of JupyterHub on k8s is to be possible. - type: Recreate - db: - type: sqlite-pvc - upgrade: - pvc: - annotations: {} - selector: {} - accessModes: - - ReadWriteOnce - storage: 10Gi - subPath: - storageClassName: standard - url: - password: - labels: {} - annotations: {} - command: [] - args: [] - extraConfig: {} - extraFiles: {} - extraEnv: { - JUPYTERHUB_ENV: "eoepca", - JUPYTERHUB_SINGLE_USER_IMAGE_NOTEBOOKS: "", - } - extraContainers: [] - extraVolumes: [] - # - name: application-hub-config - # configMap: - # name: application-hub-jupyter-config - # defaultMode: 0744 - # - name: application-hub-config-theme - # configMap: - # name: application-hub-jupyter-config-theme - # defaultMode: 0744 - extraVolumeMounts: [] - # - name: application-hub-config - # mountPath: /usr/local/etc/applicationhub - # - name: application-hub-config-theme - # mountPath: /opt/jupyterhub/template/ - - image: - name: hubimage - tag: "dev" - pullPolicy: - pullSecrets: [] - resources: {} - podSecurityContext: - fsGroup: 1000 - containerSecurityContext: - runAsUser: 1000 - runAsGroup: 1000 - allowPrivilegeEscalation: true - lifecycle: {} - loadRoles: {} - services: {} - pdb: - enabled: false - maxUnavailable: - minAvailable: 1 - networkPolicy: - enabled: false - ingress: [] - egress: [] - egressAllowRules: - cloudMetadataServer: true - dnsPortsPrivateIPs: true - nonPrivateIPs: true - privateIPs: true - interNamespaceAccessLabels: ignore - allowedIngressPorts: [] - allowNamedServers: true - namedServerLimitPerUser: - authenticatePrometheus: - redirectToServer: - shutdownOnLogout: - templatePaths: [] - templateVars: {} - livenessProbe: - # The livenessProbe's aim to give JupyterHub sufficient time to startup but - # be able to restart if it becomes unresponsive for ~5 min. - enabled: true - initialDelaySeconds: 300 - periodSeconds: 10 - failureThreshold: 30 - timeoutSeconds: 3 - readinessProbe: - # The readinessProbe's aim is to provide a successful startup indication, - # but following that never become unready before its livenessProbe fail and - # restarts it if needed. To become unready following startup serves no - # purpose as there are no other pod to fallback to in our non-HA deployment. - enabled: true - initialDelaySeconds: 0 - periodSeconds: 2 - failureThreshold: 1000 - timeoutSeconds: 1 - existingSecret: - serviceAccount: - create: true - name: - annotations: {} - extraPodSpec: {} - -rbac: - create: true - -# proxy relates to the proxy pod, the proxy-public service, and the autohttps -# pod and proxy-http service. -proxy: - secretToken: '8af21006c7c3dc381c5d3b4b27e2c99e6311d5fc243fqbf9a14646020197d67c' - annotations: {} - deploymentStrategy: - ## type: Recreate - ## - JupyterHub's interaction with the CHP proxy becomes a lot more robust - ## with this configuration. To understand this, consider that JupyterHub - ## during startup will interact a lot with the k8s service to reach a - ## ready proxy pod. If the hub pod during a helm upgrade is restarting - ## directly while the proxy pod is making a rolling upgrade, the hub pod - ## could end up running a sequence of interactions with the old proxy pod - ## and finishing up the sequence of interactions with the new proxy pod. - ## As CHP proxy pods carry individual state this is very error prone. One - ## outcome when not using Recreate as a strategy has been that user pods - ## have been deleted by the hub pod because it considered them unreachable - ## as it only configured the old proxy pod but not the new before trying - ## to reach them. - type: Recreate - ## rollingUpdate: - ## - WARNING: - ## This is required to be set explicitly blank! Without it being - ## explicitly blank, k8s will let eventual old values under rollingUpdate - ## remain and then the Deployment becomes invalid and a helm upgrade would - ## fail with an error like this: - ## - ## UPGRADE FAILED - ## Error: Deployment.apps "proxy" is invalid: spec.strategy.rollingUpdate: Forbidden: may not be specified when strategy `type` is 'Recreate' - ## Error: UPGRADE FAILED: Deployment.apps "proxy" is invalid: spec.strategy.rollingUpdate: Forbidden: may not be specified when strategy `type` is 'Recreate' - rollingUpdate: - # service relates to the proxy-public service - service: - type: ClusterIP - labels: {} - annotations: {} - nodePorts: - http: - https: - disableHttpPort: false - extraPorts: [] - loadBalancerIP: - loadBalancerSourceRanges: [] - # chp relates to the proxy pod, which is responsible for routing traffic based - # on dynamic configuration sent from JupyterHub to CHP's REST API. - chp: - revisionHistoryLimit: - containerSecurityContext: - runAsUser: 65534 # nobody user - runAsGroup: 65534 # nobody group - allowPrivilegeEscalation: false - image: - name: jupyterhub/configurable-http-proxy - # tag is automatically bumped to new patch versions by the - # watch-dependencies.yaml workflow. - # - tag: "4.5.3" # https://github.com/jupyterhub/configurable-http-proxy/releases - pullPolicy: - pullSecrets: [] - extraCommandLineFlags: [] - livenessProbe: - enabled: true - initialDelaySeconds: 60 - periodSeconds: 10 - failureThreshold: 30 - timeoutSeconds: 3 - readinessProbe: - enabled: true - initialDelaySeconds: 0 - periodSeconds: 2 - failureThreshold: 1000 - timeoutSeconds: 1 - resources: {} - defaultTarget: - errorTarget: - extraEnv: {} - nodeSelector: {} - tolerations: [] - networkPolicy: - enabled: false - ingress: [] - egress: [] - egressAllowRules: - cloudMetadataServer: true - dnsPortsPrivateIPs: true - nonPrivateIPs: true - privateIPs: true - interNamespaceAccessLabels: ignore - allowedIngressPorts: [http, https] - pdb: - enabled: false - maxUnavailable: - minAvailable: 1 - extraPodSpec: {} - # traefik relates to the autohttps pod, which is responsible for TLS - # termination when proxy.https.type=letsencrypt. - traefik: - revisionHistoryLimit: - containerSecurityContext: - runAsUser: 65534 # nobody user - runAsGroup: 65534 # nobody group - allowPrivilegeEscalation: false - image: - name: traefik - # tag is automatically bumped to new patch versions by the - # watch-dependencies.yaml workflow. - # - tag: "v2.8.4" # ref: https://hub.docker.com/_/traefik?tab=tags - pullPolicy: - pullSecrets: [] - hsts: - includeSubdomains: false - preload: false - maxAge: 15724800 # About 6 months - resources: {} - labels: {} - extraInitContainers: [] - extraEnv: {} - extraVolumes: [] - extraVolumeMounts: [] - extraStaticConfig: {} - extraDynamicConfig: {} - nodeSelector: {} - tolerations: [] - extraPorts: [] - networkPolicy: - enabled: false - ingress: [] - egress: [] - egressAllowRules: - cloudMetadataServer: true - dnsPortsPrivateIPs: true - nonPrivateIPs: true - privateIPs: true - interNamespaceAccessLabels: ignore - allowedIngressPorts: [http, https] - pdb: - enabled: false - maxUnavailable: - minAvailable: 1 - serviceAccount: - create: true - name: - annotations: {} - extraPodSpec: {} - secretSync: - containerSecurityContext: - runAsUser: 65534 # nobody user - runAsGroup: 65534 # nobody group - allowPrivilegeEscalation: false - image: - name: jupyterhub/k8s-secret-sync - tag: "2.0.0" - pullPolicy: - pullSecrets: [] - resources: {} - labels: {} - https: - enabled: false - type: letsencrypt - #type: letsencrypt, manual, offload, secret - letsencrypt: - contactEmail: - # Specify custom server here (https://acme-staging-v02.api.letsencrypt.org/directory) to hit staging LE - acmeServer: https://acme-v02.api.letsencrypt.org/directory - manual: - key: - cert: - secret: - name: - key: tls.key - crt: tls.crt - hosts: [] - -# singleuser relates to the configuration of KubeSpawner which runs in the hub -# pod, and its spawning of user pods such as jupyter-myusername. -singleuser: - podNameTemplate: - extraTolerations: [] - nodeSelector: {"k8s.scaleway.com/pool-name": "processing-node-pool-dev"} - extraNodeAffinity: - required: [] - preferred: [] - extraPodAffinity: - required: [] - preferred: [] - extraPodAntiAffinity: - required: [] - preferred: [] - networkTools: - image: - name: jupyterhub/k8s-network-tools - tag: "2.0.0" - pullPolicy: - pullSecrets: [] - resources: {} - cloudMetadata: - # block set to true will append a privileged initContainer using the - # iptables to block the sensitive metadata server at the provided ip. - blockWithIptables: true - ip: 169.254.169.254 - networkPolicy: - enabled: false - ingress: [] - egress: [] - egressAllowRules: - cloudMetadataServer: false - dnsPortsPrivateIPs: true - nonPrivateIPs: true - privateIPs: false - interNamespaceAccessLabels: ignore - allowedIngressPorts: [] - events: true - extraAnnotations: {} - extraLabels: - hub.jupyter.org/network-access-hub: "true" - extraFiles: {} - extraEnv: {} - lifecycleHooks: {} - initContainers: [] - extraContainers: [] - allowPrivilegeEscalation: false - uid: 1000 - fsGid: 100 - serviceAccountName: - storage: - type: dynamic - extraLabels: {} - extraVolumes: [] - extraVolumeMounts: [] - static: - pvcName: - subPath: "{username}" - capacity: 10Gi - homeMountPath: /home/jovyan - dynamic: - storageClass: standard - pvcNameTemplate: claim-{username}{servername} - volumeNameTemplate: volume-{username}{servername} - storageAccessModes: [ReadWriteOnce] - image: - name: jupyterhub/k8s-singleuser-sample - tag: "2.0.0" - pullPolicy: - pullSecrets: [] - startTimeout: 6000 - cpu: - limit: - guarantee: - memory: - limit: - guarantee: 1G - extraResource: - limits: {} - guarantees: {} - cmd: jupyterhub-singleuser - defaultUrl: - extraPodConfig: {} - profileList: [] - -# scheduling relates to the user-scheduler pods and user-placeholder pods. -scheduling: - userScheduler: - enabled: true - revisionHistoryLimit: - replicas: 2 - logLevel: 4 - # plugins are configured on the user-scheduler to make us score how we - # schedule user pods in a way to help us schedule on the most busy node. By - # doing this, we help scale down more effectively. It isn't obvious how to - # enable/disable scoring plugins, and configure them, to accomplish this. - # - # plugins ref: https://kubernetes.io/docs/reference/scheduling/config/#scheduling-plugins-1 - # migration ref: https://kubernetes.io/docs/reference/scheduling/config/#scheduler-configuration-migrations - # - plugins: - score: - # These scoring plugins are enabled by default according to - # https://kubernetes.io/docs/reference/scheduling/config/#scheduling-plugins - # 2022-02-22. - # - # Enabled with high priority: - # - NodeAffinity - # - InterPodAffinity - # - NodeResourcesFit - # - ImageLocality - # Remains enabled with low default priority: - # - TaintToleration - # - PodTopologySpread - # - VolumeBinding - # Disabled for scoring: - # - NodeResourcesBalancedAllocation - # - disabled: - # We disable these plugins (with regards to scoring) to not interfere - # or complicate our use of NodeResourcesFit. - - name: NodeResourcesBalancedAllocation - # Disable plugins to be allowed to enable them again with a different - # weight and avoid an error. - - name: NodeAffinity - - name: InterPodAffinity - - name: NodeResourcesFit - - name: ImageLocality - enabled: - - name: NodeAffinity - weight: 14631 - - name: InterPodAffinity - weight: 1331 - - name: NodeResourcesFit - weight: 121 - - name: ImageLocality - weight: 11 - pluginConfig: - # Here we declare that we should optimize pods to fit based on a - # MostAllocated strategy instead of the default LeastAllocated. - - name: NodeResourcesFit - args: - scoringStrategy: - resources: - - name: cpu - weight: 1 - - name: memory - weight: 1 - type: MostAllocated - containerSecurityContext: - runAsUser: 65534 # nobody user - runAsGroup: 65534 # nobody group - allowPrivilegeEscalation: false - image: - # IMPORTANT: Bumping the minor version of this binary should go hand in - # hand with an inspection of the user-scheduelrs RBAC resources - # that we have forked in - # templates/scheduling/user-scheduler/rbac.yaml. - # - # Debugging advice: - # - # - Is configuration of kube-scheduler broken in - # templates/scheduling/user-scheduler/configmap.yaml? - # - # - Is the kube-scheduler binary's compatibility to work - # against a k8s api-server that is too new or too old? - # - # - You can update the GitHub workflow that runs tests to - # include "deploy/user-scheduler" in the k8s namespace report - # and reduce the user-scheduler deployments replicas to 1 in - # dev-config.yaml to get relevant logs from the user-scheduler - # pods. Inspect the "Kubernetes namespace report" action! - # - # - Typical failures are that kube-scheduler fails to search for - # resources via its "informers", and won't start trying to - # schedule pods before they succeed which may require - # additional RBAC permissions or that the k8s api-server is - # aware of the resources. - # - # - If "successfully acquired lease" can be seen in the logs, it - # is a good sign kube-scheduler is ready to schedule pods. - # - name: k8s.gcr.io/kube-scheduler - # tag is automatically bumped to new patch versions by the - # watch-dependencies.yaml workflow. The minor version is pinned in the - # workflow, and should be updated there if a minor version bump is done - # here. - # - tag: "v1.23.10" # ref: https://github.com/kubernetes/website/blob/main/content/en/releases/patch-releases.md - pullPolicy: - pullSecrets: [] - nodeSelector: {} - tolerations: [] - labels: {} - annotations: {} - pdb: - enabled: true - maxUnavailable: 1 - minAvailable: - resources: {} - serviceAccount: - create: true - name: - annotations: {} - extraPodSpec: {} - podPriority: - enabled: false - globalDefault: false - defaultPriority: 0 - imagePullerPriority: -5 - userPlaceholderPriority: -10 - userPlaceholder: - enabled: true - image: - name: k8s.gcr.io/pause - # tag is automatically bumped to new patch versions by the - # watch-dependencies.yaml workflow. - # - # If you update this, also update prePuller.pause.image.tag - # - tag: "3.8" - pullPolicy: - pullSecrets: [] - revisionHistoryLimit: - replicas: 0 - labels: {} - annotations: {} - containerSecurityContext: - runAsUser: 65534 # nobody user - runAsGroup: 65534 # nobody group - allowPrivilegeEscalation: false - resources: {} - corePods: - tolerations: - - key: hub.jupyter.org/dedicated - operator: Equal - value: core - effect: NoSchedule - - key: hub.jupyter.org_dedicated - operator: Equal - value: core - effect: NoSchedule - nodeAffinity: - matchNodePurpose: prefer - userPods: - tolerations: - - key: hub.jupyter.org/dedicated - operator: Equal - value: user - effect: NoSchedule - - key: hub.jupyter.org_dedicated - operator: Equal - value: user - effect: NoSchedule - nodeAffinity: - matchNodePurpose: prefer - -# prePuller relates to the hook|continuous-image-puller DaemonsSets -prePuller: - revisionHistoryLimit: - labels: {} - annotations: {} - resources: {} - containerSecurityContext: - runAsUser: 65534 # nobody user - runAsGroup: 65534 # nobody group - allowPrivilegeEscalation: false - extraTolerations: [] - # hook relates to the hook-image-awaiter Job and hook-image-puller DaemonSet - hook: - enabled: true - pullOnlyOnChanges: true - # image and the configuration below relates to the hook-image-awaiter Job - image: - name: jupyterhub/k8s-image-awaiter - tag: "2.0.0" - pullPolicy: - pullSecrets: [] - containerSecurityContext: - runAsUser: 65534 # nobody user - runAsGroup: 65534 # nobody group - allowPrivilegeEscalation: false - podSchedulingWaitDuration: 10 - nodeSelector: {} - tolerations: [] - resources: {} - serviceAccount: - create: true - name: - annotations: {} - continuous: - enabled: true - pullProfileListImages: true - extraImages: {} - pause: - containerSecurityContext: - runAsUser: 65534 # nobody user - runAsGroup: 65534 # nobody group - allowPrivilegeEscalation: false - image: - name: k8s.gcr.io/pause - # tag is automatically bumped to new patch versions by the - # watch-dependencies.yaml workflow. - # - # If you update this, also update scheduling.userPlaceholder.image.tag - # - tag: "3.8" - pullPolicy: - pullSecrets: [] - -ingress: - enabled: false - annotations: {} - ingressClassName: - hosts: [] - pathSuffix: - pathType: Prefix - tls: [] - -# cull relates to the jupyterhub-idle-culler service, responsible for evicting -# inactive singleuser pods. -# -# The configuration below, except for enabled, corresponds to command-line flags -# for jupyterhub-idle-culler as documented here: -# https://github.com/jupyterhub/jupyterhub-idle-culler#as-a-standalone-script -# -cull: - enabled: true - users: false # --cull-users - adminUsers: true # --cull-admin-users - removeNamedServers: false # --remove-named-servers - timeout: 3600 # --timeout - every: 600 # --cull-every - concurrency: 10 # --concurrency - maxAge: 0 # --max-age - -debug: - enabled: false - -global: - safeToShowValues: false diff --git a/skaffold.yaml b/skaffold.yaml index fc5b677..54d1aed 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -5,36 +5,6 @@ build: - image: hubimage profiles: - - name: reference - deploy: - helm: - releases: - - name: jupyterhub - remoteChart: eoepca/application-hub - namespace: jupyter - version: 4.0.1 - createNamespace: true - valuesFiles: [] - setValueTemplates: - jupyterhub.hub.image.name: "{{.IMAGE_REPO_hubimage}}" - jupyterhub.hub.image.tag: "{{.IMAGE_TAG_hubimage}}@{{.IMAGE_DIGEST_hubimage}}" - setValues: - jupyterhub.hub.image.pullSecrets: ["eoepca-plus-secret-ro"] - jupyterhub.hub.db.pvc.storageClassName: scw-bssd - jupyterhub.hub.extraEnv.STORAGE_CLASS: scw-bssd - jupyterhub.proxy.secretToken: "032d5bfe141a7eab86d57587879b33c5d168617cacb339823d7f47fe2933f880" - jupyterhub.hub.extraEnv.APP_HUB_NAMESPACE: jupyter - jupyterhub.hub.networkPolicy.enabled: false - setFiles: - configYml: ./files/hub/config.yml - jupyterConfig: ./files/hub/jupyterhub_config.py - - manifests: - rawYaml: - - sk-k8s/cluster-role-binding.yaml - - sk-k8s/script.yaml - - sk-k8s/job.yaml - - name: minikube deploy: helm: @@ -69,35 +39,6 @@ profiles: - sk-k8s/script.yaml - sk-k8s/job.yaml - # - name: eoepca-demo - # deploy: - # helm: - # releases: - # - name: jupyterhub - # chartPath: jupyterhub - # namespace: jupyter - # createNamespace: true - # valuesFiles: - # - demo-eoepca-values.yaml - # setValueTemplates: - # hub.image.name: "{{.IMAGE_REPO_hubimage}}" - # hub.image.tag: "{{.IMAGE_TAG_hubimage}}" - # hub.image.pullPolicy: "Always" - # hub.db.pvc.storageClassName: "managed-nfs-storage" - # hub.image.pullSecrets: - # - "eoepca-plus-secret-ro" - # setFiles: { - # appHubConfig: ./eoepca-demo/config.yml, - # pageTheme: ./files/theme/page.html, - # spawnPendingTheme: ./files/theme/spawn_pending.html, - # spawnTheme: ./files/theme/spawn.html - # } - - # manifests: - # rawYaml: - # - sk-k8s/cluster-role-binding.yaml - # - sk-k8s/script.yaml - # - sk-k8s/job.yaml portForward: - resourceType: service resourceName: application-hub-proxy-public From 7771cb16e0945660eb36d093c069fae9fab4f16d Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 7 Jan 2025 17:26:35 +0100 Subject: [PATCH 61/65] updates docs CI --- .github/workflows/docs.yml | 22 ++++++++-------------- 1 file changed, 8 insertions(+), 14 deletions(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index b31ea7b..7a6d0eb 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -4,7 +4,6 @@ on: branches: - develop - main - - secrets paths: # Only rebuild website when docs have changed - 'README.md' @@ -16,18 +15,13 @@ jobs: deploy: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - name: Checkout master + uses: actions/checkout@v2 - - name: Install Conda environment from environment.yml - uses: mamba-org/provision-with-micromamba@main + - name: Set up Python 3.x + uses: actions/setup-python@v2 with: - environment-file: docs/environment.yml - environment-name: env_zoo_calrissian - channels: terradue,eoepca,conda-forge - channel-priority: flexible - - - name: Install project - run: | - /home/runner/micromamba-root/envs/env_zoo_calrissian/bin/python setup.py install - - - run: /home/runner/micromamba-root/envs/env_zoo_calrissian/bin/mkdocs gh-deploy --force + python-version: 3.x + - run: | + pip install mkdocs-material mkdocs-mermaid2-plugin mkdocs-jupyter odc-stac ipykernel cwltool zarr matplotlib + mkdocs gh-deploy --force \ No newline at end of file From 0870ae496a5307d72647c968f4cb469d8cf28b37 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 7 Jan 2025 17:28:13 +0100 Subject: [PATCH 62/65] removes docs libraries in CI --- .github/workflows/docs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index 7a6d0eb..0ebef06 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -23,5 +23,5 @@ jobs: with: python-version: 3.x - run: | - pip install mkdocs-material mkdocs-mermaid2-plugin mkdocs-jupyter odc-stac ipykernel cwltool zarr matplotlib + pip install mkdocs-material mkdocs-mermaid2-plugin mkdocs-jupyter mkdocs gh-deploy --force \ No newline at end of file From dc21b826aa0ec902487c39915d010e927630a1d0 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 7 Jan 2025 17:31:03 +0100 Subject: [PATCH 63/65] trying trivy CI --- .github/workflows/.github-ci.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/.github-ci.yaml b/.github/workflows/.github-ci.yaml index ae7cc5c..81eb52a 100644 --- a/.github/workflows/.github-ci.yaml +++ b/.github/workflows/.github-ci.yaml @@ -2,7 +2,8 @@ name: Build, Test, and Deploy Docker Image on: push: - branches: [eoepca-beta01] + branches: + - develop jobs: build: From 1ed806dfd60ff7dfb61a066fb432e1f911c0aaa2 Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 7 Jan 2025 17:39:52 +0100 Subject: [PATCH 64/65] bumps to 1.4.0 --- build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.yml b/build.yml index 511c9dd..5e78c85 100644 --- a/build.yml +++ b/build.yml @@ -1,2 +1,2 @@ docker_image_name: eoepca/application-hub -docker_image_version: 1.3.1 +docker_image_version: 1.4.0 From 7ad1d9b3ef0cf84daed2b6ec6e6791fdb5453afe Mon Sep 17 00:00:00 2001 From: Fabrice Brito Date: Tue, 7 Jan 2025 17:40:29 +0100 Subject: [PATCH 65/65] removes old docker build CI config --- .github/workflows/build_publish.yml | 60 ----------------------------- 1 file changed, 60 deletions(-) delete mode 100644 .github/workflows/build_publish.yml diff --git a/.github/workflows/build_publish.yml b/.github/workflows/build_publish.yml deleted file mode 100644 index c177e3f..0000000 --- a/.github/workflows/build_publish.yml +++ /dev/null @@ -1,60 +0,0 @@ -name: Build and Publish Docker - -on: - push: - branches: - - develop - - main -jobs: - build-and-publish-docker: - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Read docker name - id: yaml-docker-name - uses: jbutcher5/read-yaml@main - with: - file: 'build.yml' - key-path: '["docker_image_name"]' - - - name: Read docker version - id: yaml-docker-version - uses: jbutcher5/read-yaml@main - with: - file: 'build.yml' - key-path: '["docker_image_version"]' - - - name: Generate docker tag - env: - GITHUB_BRANCH: ${{ github.ref }} - docker_image_name: ${{ steps.yaml-docker-name.outputs.data }} - docker_image_version: ${{ steps.yaml-docker-version.outputs.data }} - run: | - branch_name=${GITHUB_BRANCH#refs/heads/} - echo "branch_name=${GITHUB_BRANCH#refs/heads/}" >> $GITHUB_ENV - if [[ "$branch_name" = "main" ]] - then - mType="" - else - mType="dev" - fi - echo "docker_tag=$docker_image_name:$mType$docker_image_version" >> $GITHUB_ENV - echo "docker_tag_latest=$docker_image_name:${mType}latest" >> $GITHUB_ENV - - - name: Login to Docker Hub - uses: docker/login-action@v2 - with: - username: ${{ secrets.DOCKER_USERNAME }} - password: ${{ secrets.DOCKER_PASSWORD }} - - - name: Build and push - uses: docker/build-push-action@v4 - with: - context: . - push: true - no-cache: true - tags: | - ${{ env.docker_tag }} - ${{ env.docker_tag_latest }}