diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index dc1d6b4..53d8ee3 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -23,6 +23,5 @@ jobs: with: python-version: 3.x - run: | - cp apphub-configurator/examples/config-generator.ipynb docs/config-generator.ipynb pip install mkdocs-material mkdocs-mermaid2-plugin mkdocs-jupyter - mkdocs gh-deploy --force \ No newline at end of file + mkdocs gh-deploy --force diff --git a/.github/workflows/package.yaml b/.github/workflows/package.yaml deleted file mode 100644 index d5bc319..0000000 --- a/.github/workflows/package.yaml +++ /dev/null @@ -1,43 +0,0 @@ -name: publish-to-pypi - -on: - push: - tags: - - 'v*' - - -permissions: - contents: read - -jobs: - deploy: - runs-on: ubuntu-latest - environment: release - permissions: - id-token: write # IMPORTANT: this permission is mandatory for trusted publishing - - steps: - - uses: actions/checkout@v4 - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.10' - cache: 'pip' - - name: Install dependencies - run: | - # python -m pip install --upgrade pip - pip install hatch - - name: Build package - run: | - rm -rf application_hub_context - rm -rf setup* - cd apphub-configurator/ - hatch build - mv -f dist/ ../ - cd - - - - name: Publish package distributions to PyPI (main) - if: startsWith(github.ref, 'refs/tags/v') - uses: pypa/gh-action-pypi-publish@release/v1 - with: - repository-url: https://upload.pypi.org/legacy/ diff --git a/apphub-configurator/LICENSE.txt b/apphub-configurator/LICENSE.txt deleted file mode 100644 index dba06ff..0000000 --- a/apphub-configurator/LICENSE.txt +++ /dev/null @@ -1,9 +0,0 @@ -MIT License - -Copyright (c) 2025-present pmembari - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/apphub-configurator/config-maps/bash-login b/apphub-configurator/config-maps/bash-login deleted file mode 100644 index 743cb42..0000000 --- a/apphub-configurator/config-maps/bash-login +++ /dev/null @@ -1 +0,0 @@ -source /workspace/.bashrc diff --git a/apphub-configurator/config-maps/bash-rc b/apphub-configurator/config-maps/bash-rc deleted file mode 100644 index 5098e90..0000000 --- a/apphub-configurator/config-maps/bash-rc +++ /dev/null @@ -1,31 +0,0 @@ -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)" -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 <<< - -a={{spawner.user.name}} - -alias aws="aws --endpoint-url=http://localstack-jupyter-{{spawner.user.name}}:4566" - -export PATH=/workspace/.local/bin:$PATH \ No newline at end of file diff --git a/apphub-configurator/config-maps/conda-rc.yml b/apphub-configurator/config-maps/conda-rc.yml deleted file mode 100644 index f8a34eb..0000000 --- a/apphub-configurator/config-maps/conda-rc.yml +++ /dev/null @@ -1,5 +0,0 @@ -auto_update_conda: false -show_channel_urls: true -channels: - - conda-forge - - terradue diff --git a/apphub-configurator/config-maps/init-qgis.sh b/apphub-configurator/config-maps/init-qgis.sh deleted file mode 100644 index 738dc2e..0000000 --- a/apphub-configurator/config-maps/init-qgis.sh +++ /dev/null @@ -1,17 +0,0 @@ -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/apphub-configurator/config-maps/init-stac.sh b/apphub-configurator/config-maps/init-stac.sh deleted file mode 100644 index f61d57a..0000000 --- a/apphub-configurator/config-maps/init-stac.sh +++ /dev/null @@ -1,31 +0,0 @@ -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/apphub-configurator/config-maps/init.sh b/apphub-configurator/config-maps/init.sh deleted file mode 100644 index 014fdf3..0000000 --- a/apphub-configurator/config-maps/init.sh +++ /dev/null @@ -1,14 +0,0 @@ -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/apphub-configurator/examples/config-generator.ipynb b/apphub-configurator/examples/config-generator.ipynb deleted file mode 100644 index cd1db94..0000000 --- a/apphub-configurator/examples/config-generator.ipynb +++ /dev/null @@ -1,1348 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Generate App Hub's configuration\n", - "\n", - "This notebook aim to produce a `config.yml` file to generate all the configuration needs to be set for deployment of App Hub on a remote cluster using Devops tools. \n", - "\n", - "This Notebook can be used for the following requirements:\n", - "\n", - "* Initial dependencies\n", - "* Create the configuration for kubernetes' `PV`s, and `PVC`s(e.g. workspace volume, and calrissian volume)\n", - "* Generate the configuration for kubernetes' config maps\n", - "* Creation of different Profiles \n", - "\n", - "Table of Content:\n", - "- [Initial dependencies](#initial-dependencies)\n", - " - [Configuration](#configuration)\n", - "- [Volumes](#volumes)\n", - " - [Workspace volume](#workspace-volume)\n", - " - [Calrissian volume](#calrissian-volume)\n", - "- [Configmaps](#configmaps)\n", - " - [bash login Configmap](#bash-login-configmap)\n", - " - [bash rc Configmap](#bash-rc-configmap) \n", - " - [QGIS Configmap](#qgis-configmap) \n", - " - [Stage-in/out Configmap](#stage-inout-configmap)\n", - "- [Profiles](#profiles)\n", - " - [Coder](#coder)\n", - " - [Coder with init.sh](#coder-with-initsh)\n", - " - [JupyterLab](#jupyterlab)\n", - " - [JupyterLab Plus](#jupyterlab-plus)\n", - " - [E-Learning](#e-learning)\n", - " - [QGIS](#qgis)\n", - "- [Write Configuration](#write-configuration)\n" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Initial dependencies" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [ - "!pip install apphub-configurator" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "PosixPath('/home/t2/Desktop/p/EOEPCA/application-hub-context/apphub-configurator/examples')" - ] - }, - "execution_count": 1, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "import yaml\n", - "from apphub_configurator.models import *\n", - "from pathlib import Path\n", - "import os\n", - "import json\n", - "from pprint import pprint\n", - "current_dir = Path(os.getcwd())\n", - "parent_dir = current_dir.parent\n", - "current_dir" - ] - }, - { - "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\"" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Volumes\n", - "\n", - "In this section, the user will provide the data class objects for creation of volume:\n", - "- Volume for workspace\n", - "- Volume for calrissian\n", - "These two configuration will be use " - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Workspace Volume\n", - "In this section, the user will providing the configuration of a kubernetes Volume for development environment (i.e workspace). It is important to mention that this volume must keep data persistently. therefore, the user set `persist=True`" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='managed-nfs-storage', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "workspace_volume = Volume( # type: ignore\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\"), # type: ignore\n", - " persist=True,\n", - ")\n", - "workspace_volume" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Calrissian Volume\n", - "In this section, the user will configure a **Kubernetes Volume** for Calrissian jobs. Since the job runs within Calrissian and does not require data retention on the Calrissian pod, therefore the user should set `persist=False`. " - ] - }, - { - "cell_type": "code", - "execution_count": 4, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='managed-nfs-storage', access_modes=['ReadWriteMany'], volume_mount=VolumeMount(name='calrissian-volume', mount_path='/calrissian'), persist=False)" - ] - }, - "execution_count": 4, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "calrissian_volume = Volume( # type: ignore\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\"), # type: ignore\n", - " persist=False,\n", - ")\n", - "calrissian_volume" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## ConfigMaps\n", - "\n", - "In this section, the user will provide the configuration for some kubernetes configmaps that is commonly used in this notebook including:\n", - "- bash login Configmap\n", - "- bash rc Configmap\n", - "- QGIS Configmap\n", - "- Stage-in/out Configmap\n", - "These configmaps will be mounted as files on different pods. " - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### bash login Configmap\n", - "\n", - "This configmap file aims to configure the Terminal across different profiles. " - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "ConfigMap(name='bash-login', key='bash-login', mount_path='/workspace/.bash_login', default_mode=None, readonly=True, content='source /workspace/.bashrc\\n', persist=False)" - ] - }, - "execution_count": 5, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bash_login_file_path = os.path.join(parent_dir, \"config-maps/bash-login\") \n", - "with open(bash_login_file_path, \"r\") as f:\n", - " content = f.read()\n", - "\n", - "bash_login_cm = ConfigMap( # type: ignore\n", - " name=\"bash-login\",\n", - " key=\"bash-login\",\n", - " content=content,\n", - " readonly=True,\n", - " persist=False,\n", - " mount_path=\"/workspace/.bash_login\",\n", - ")\n", - "bash_login_cm" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### bash-rc Configmap\n", - "This section explains how we configure a **Kubernetes ConfigMap** to manage the `.bashrc` file inside a pod. This config map will:\n", - "- It provides useful aliases and environment settings for users.\n", - "- It ensures a consistent environment across different pods." - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "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\\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\"', persist=False)" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "bash_rc_cm_file_path = os.path.join(parent_dir, \"config-maps/bash-rc\") \n", - "with open(bash_rc_cm_file_path, \"r\") as f:\n", - " content = f.read()\n", - "bash_rc_cm = ConfigMap( # type: ignore\n", - " name=\"bash-rc\",\n", - " key=\"bash-rc\",\n", - " content=content,\n", - " readonly=True,\n", - " persist=False,\n", - " mount_path=\"/workspace/.bashrc\",\n", - ")\n", - "bash_rc_cm" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### QGIS Configmap\n", - "The cell below will provide a Configmap configuration for QGIS setup." - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "ConfigMap(name='init', key='init', mount_path='/opt/init/.init.sh', default_mode=None, readonly=True, 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\\nComment=qgis\\nExec=qgis\\nOnlyShowIn=XFCE;\\nRunHook=0\\nStartupNotify=false\\nTerminal=false\\nHidden=false\\nEOF', persist=False)" - ] - }, - "execution_count": 7, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "init_qgis_file_path = os.path.join(parent_dir, \"config-maps/init-qgis.sh\") \n", - "with open(init_qgis_file_path, \"r\") as f:\n", - " content = f.read()\n", - "\n", - "init_qgis_cm = ConfigMap( # type: ignore\n", - " name=\"init\",\n", - " key=\"init\",\n", - " content=content,\n", - " readonly=True,\n", - " persist=False,\n", - " mount_path=\"/opt/init/.init.sh\",\n", - ")\n", - "init_qgis_cm" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Stage-in/out Configmap\n", - "The cell below will provide a Configmap configuration for stage-in/out for [e-learning](#e-learning) profile." - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "ConfigMap(name='init', key='init', mount_path='/opt/init/.init.sh', default_mode=None, readonly=True, 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 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', persist=False)" - ] - }, - "execution_count": 8, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "init_stac_file_path = os.path.join(parent_dir, \"config-maps/init-stac.sh\")\n", - "with open(init_stac_file_path, \"r\") as f:\n", - " content = f.read()\n", - "\n", - "init_stac_cm = ConfigMap( # type: ignore\n", - " name=\"init\",\n", - " key=\"init\",\n", - " content=content,\n", - " readonly=True,\n", - " persist=False,\n", - " mount_path=\"/opt/init/.init.sh\",\n", - ")\n", - "init_stac_cm" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Coder init Configmap\n", - "The cell below will provide a Configmap configuration for stage-in/out for [coder](#coder) profile." - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "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)" - ] - }, - "execution_count": 9, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "init_cm_file_path = os.path.join(parent_dir, \"config-maps/init.sh\") \n", - "with open(init_cm_file_path, \"r\") as f:\n", - " content = f.read()\n", - "\n", - "init_coder_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", - "init_coder_cm" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Profiles\n", - "In the following section, the user will generate the configuration for different profiles on a remote cluster including:\n", - "- [Coder](#coder)\n", - "- [Coder with init.sh](#coder-with-initsh)\n", - "- [Jupyter Lab](#jupyterlab)\n", - "- [JupyterLab Plus](#jupyterlab-plus)\n", - "- [E-learning](#e-learning)\n", - "- [QGIS](#qgis)" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [], - "source": [ - "profiles = []" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Coder\n", - "In the cell below, the user configures two code-server profiles to be deployed on a remote cluster. One profile (**medium**) has more resources than the other (**small**). Two Kubernetes Volumes, discussed in the [Volume section](#volumes), are assigned, and a ConfigMap is mounted. \n" - ] - }, - { - "cell_type": "code", - "execution_count": 11, - "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\\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\"', persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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\\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\"', persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=None)]" - ] - }, - "execution_count": 11, - "metadata": {}, - "output_type": "execute_result" - } - ], - "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( # type: ignore\n", - " display_name=value[\"display_name\"],\n", - " slug=value[\"slug\"],\n", - " default=False,\n", - " kubespawner_override=KubespawnerOverride( # type: ignore\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( # type: ignore\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)\n", - "profiles" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### Coder with init.sh\n", - "In the cell below, the user configures the code-server with a new profile, which extends the existing [coder](#coder) profile. The init container is responsible for executing a Bash script to initialize the code-server pod. In this case, the script [`init.sh`](../config-maps/init.sh) runs during pod initialization to clone a Git repository and install extensions on the deployed code-server." - ] - }, - { - "cell_type": "code", - "execution_count": 12, - "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\\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\"', persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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\\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\"', persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=None)]" - ] - }, - "execution_count": 12, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "\n", - "\n", - "init_context_volume_mount = InitContainerVolumeMount( # type: ignore\n", - " mount_path=\"/opt/init/.init.sh\", name=\"init\", sub_path=\"init\"\n", - ")\n", - "init_container = InitContainer( # type: ignore\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\"), # type: ignore\n", - " init_context_volume_mount,\n", - " ],\n", - ")\n", - "\n", - "eoepca_demo_init_script_profile = Profile( # type: ignore\n", - " id=f\"profile_demo_init_script\",\n", - " groups=[\"group-a\", \"group-b\"],\n", - " definition=ProfileDefinition( # type: ignore\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( # type: ignore\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_coder_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", - ")\n", - "profiles.append(eoepca_demo_init_script_profile)\n", - "profiles" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### JupyterLab\n", - "\n", - "In the cell below, the user adds a [JupyterLab](https://github.com/jupyter/docker-stacks) profile to the stack of profiles. The [workspace volume](#workspace-volume) is mounted to persist user files, and necessary environment variables are set to ensure a smooth user experience." - ] - }, - { - "cell_type": "code", - "execution_count": 13, - "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\\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\"', persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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\\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\"', persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=None)]" - ] - }, - "execution_count": 13, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "image = \"jupyter/scipy-notebook\"\n", - "\n", - "\n", - "eoepca_jupyter_lab_profile = Profile( # type: ignore\n", - " id=\"profile_jupyter_lab\",\n", - " groups=[\"group-c\"],\n", - " definition=ProfileDefinition( # type: ignore\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( # type: ignore\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)\n", - "profiles" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### JupyterLab Plus \n", - "JupyterLab Plus is an extended version of the JupyterLab profile with additional features and capabilities. The Docker image for this profile is hosted on a private Docker registry, requiring the user to configure a pull secret to authenticate and successfully pull the JupyterLab Plus image on the remote cluster." - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "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\\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\"', persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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\\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\"', persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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 private image - 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='eoepca/iat-jupyterlab:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=None)]" - ] - }, - "execution_count": 14, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "image_pull_secret = ImagePullSecret( # type: ignore\n", - " name=\"cr-config\",\n", - " persist=False,\n", - " data=\"ewogICAgImF1dGhzIjogewogICAgICAgICJjci50ZXJyYWR1ZS5jb20iOiB7CiAgICAgICAgICAgICJ1c2VybmFtZSI6ICJyb2JvdCRlb2VwY2EtcGx1cy1ybyIsCiAgICAgICAgICAgICJwYXNzd29yZCI6ICJQMlE4TnkyZ0lHODhkZkxveXlLN05QVUZVbHJOekFZSiIsCiAgICAgICAgICAgICJlbWFpbCI6ICJlb2VwY2EtcGx1c0B0ZXJyYWR1ZS5jb20iLAogICAgICAgICAgICAiYXV0aCI6ICJjbTlpYjNRa1pXOWxjR05oTFhCc2RYTXRjbTg2VURKUk9FNTVNbWRKUnpnNFpHWk1iM2w1U3pkT1VGVkdWV3h5VG5wQldVbz0iCiAgICAgICAgfQogICAgfQp9\",\n", - ")\n", - "image = \"cr.terradue.com/eoepca-plus/scipy-notebook@sha256:f339a9fa98d3d0c1fa8d7cc850e7f5a46845781f49bee86aacba059669d02d54\"\n", - "image = \"eoepca/iat-jupyterlab:develop\"\n", - "\n", - "eoepca_jupyter_lab_profile_2 = Profile( # type: ignore\n", - " id=\"profile_jupyter_lab_2\",\n", - " groups=[\"group-c\"],\n", - " definition=ProfileDefinition( # type: ignore\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( # type: ignore\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)\n", - "profiles" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### E-Learning \n", - "This profile is an extended version of code-server, configured to initialize with an init container using [init.sh](../config-maps/init.sh). It also mounts several ConfigMaps, including [bash-rc](#bash-rc), [bash-login](#bash-login), and [stage-in/out](#stage-inout-configmap). Additionally, key Kubernetes objects such as Role, RoleBinding, and Service are defined using a Kubernetes [manifest](../manifests/manifest.yaml). This manifest ultimately exposes **LocalStack** as a Service, allowing users to emulate AWS locally." - ] - }, - { - "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\\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\"', persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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\\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\"', persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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 private image - 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='eoepca/iat-jupyterlab:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=None),\n", - " Profile(id='profile_studio_coder_stac', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='Understanding STAC for input/output data modelling', description='Understand the role of STAC in input/output data manifests in EO data processing workflows', slug='eoepca_coder_slug_stac', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='docker.io/eoepca/pde-code-server@sha256:f57a3d5eabcae667e0db6e84a57b0c07c692c88f0fb5c8f6900ab8d5e38fcd40', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[ConfigMap(name='init', key='init', mount_path='/opt/init/.init.sh', default_mode=None, readonly=True, 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 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', persist=False), 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\\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\"', persist=False), ConfigMap(name='bash-login', key='bash-login', mount_path='/workspace/.bash_login', default_mode=None, readonly=True, content='source /workspace/.bashrc\\n', persist=False)], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='managed-nfs-storage', 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', 'XDG_CONFIG_HOME': '/workspace/.local', 'XDG_DATA_HOME': '/workspace/.local/share/', 'CWLTOOL_OPTIONS': '--podman', 'CODE_SERVER_WS': '/workspace/stac-eoap', 'AWS_DEFAULT_REGION': 'us-east-1', 'AWS_ACCESS_KEY_ID': 'test', 'AWS_SECRET_ACCESS_KEY': 'test'}, default_url=None, node_selector={}, role_bindings=[], image_pull_secrets=[ImagePullSecret(name='cr-config', persist=False, data='ewogICAgImF1dGhzIjogewogICAgICAgICJjci50ZXJyYWR1ZS5jb20iOiB7CiAgICAgICAgICAgICJ1c2VybmFtZSI6ICJyb2JvdCRlb2VwY2EtcGx1cy1ybyIsCiAgICAgICAgICAgICJwYXNzd29yZCI6ICJQMlE4TnkyZ0lHODhkZkxveXlLN05QVUZVbHJOekFZSiIsCiAgICAgICAgICAgICJlbWFpbCI6ICJlb2VwY2EtcGx1c0B0ZXJyYWR1ZS5jb20iLAogICAgICAgICAgICAiYXV0aCI6ICJjbTlpYjNRa1pXOWxjR05oTFhCc2RYTXRjbTg2VURKUk9FNTVNbWRKUnpnNFpHWk1iM2w1U3pkT1VGVkdWV3h5VG5wQldVbz0iCiAgICAgICAgfQogICAgfQp9')], 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=[Manifest(name='manifests', key='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'}, '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': 'localstack-{{ spawner.user.name }}', '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': []}}}}, {'apiVersion': 'v1', 'kind': 'ConfigMap', 'metadata': {'name': 'my-config'}, 'data': {'ENV_VAR1': 'value1', 'ENV_VAR2': 'value2'}}, {'apiVersion': 'v1', 'kind': 'Secret', 'metadata': {'name': 'my-secret'}, 'type': 'Opaque', 'data': {'SECRET_KEY1': 'dmFsdWUx', 'SECRET_KEY2': 'dmFsdWUy'}}, {'apiVersion': 'v1', 'kind': 'Secret', 'metadata': {'name': 'aws-credentials-{{ spawner.user.name }}'}, 'type': 'Opaque', 'data': {'credentials': 'W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg='}}, {'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', 'property': 'the-key'}}]}}, {'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', 'property': '.dockerconfigjson'}}]}}], persist=False)], env_from_config_maps=None, env_from_secrets=None, secret_mounts=None)]" - ] - }, - "execution_count": 15, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "localstack_manifest_path = os.path.join(parent_dir, \"manifests/manifest.yaml\") \n", - "with open(localstack_manifest_path, \"r\") as f:\n", - " content = yaml.safe_load_all(f.read())\n", - "\n", - "\n", - "localstack_manifest = Manifest( # type: ignore\n", - " name=\"manifests\",\n", - " key=\"manifests\",\n", - " readonly=True,\n", - " persist=False,\n", - " content=[e for e in content],\n", - ")\n", - "\n", - "image = \"docker.io/eoepca/pde-code-server@sha256:f57a3d5eabcae667e0db6e84a57b0c07c692c88f0fb5c8f6900ab8d5e38fcd40\"\n", - "\n", - "coder_profile_stac = Profile( # type: ignore\n", - " id=f\"profile_studio_coder_stac\",\n", - " groups=[\"group-a\", \"group-b\"], \n", - " definition=ProfileDefinition( # type: ignore\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=\"eoepca_coder_slug_stac\",\n", - " default=False,\n", - " kubespawner_override=KubespawnerOverride( # type: ignore\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_stac_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)\n", - "profiles" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "### QGIS \n", - "This profile is configured to deploy the [QGIS](https://github.com/qgis/QGIS) application on the **App Hub**, enabling users to visualize and analyze Earth observation data directly within the platform. The deployment ensures that QGIS is configured with the necessary resources, storage, and dependencies to support geospatial analysis, map rendering, and data processing. By integrating QGIS into the App Hub, users can seamlessly interact with satellite imagery, vector layers, and raster data for advanced geospatial analysis." - ] - }, - { - "cell_type": "code", - "execution_count": 16, - "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\\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\"', persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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\\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\"', persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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 private image - 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='eoepca/iat-jupyterlab:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=None),\n", - " Profile(id='profile_studio_coder_stac', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='Understanding STAC for input/output data modelling', description='Understand the role of STAC in input/output data manifests in EO data processing workflows', slug='eoepca_coder_slug_stac', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='docker.io/eoepca/pde-code-server@sha256:f57a3d5eabcae667e0db6e84a57b0c07c692c88f0fb5c8f6900ab8d5e38fcd40', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[ConfigMap(name='init', key='init', mount_path='/opt/init/.init.sh', default_mode=None, readonly=True, 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 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', persist=False), 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\\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\"', persist=False), ConfigMap(name='bash-login', key='bash-login', mount_path='/workspace/.bash_login', default_mode=None, readonly=True, content='source /workspace/.bashrc\\n', persist=False)], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='managed-nfs-storage', 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', 'XDG_CONFIG_HOME': '/workspace/.local', 'XDG_DATA_HOME': '/workspace/.local/share/', 'CWLTOOL_OPTIONS': '--podman', 'CODE_SERVER_WS': '/workspace/stac-eoap', 'AWS_DEFAULT_REGION': 'us-east-1', 'AWS_ACCESS_KEY_ID': 'test', 'AWS_SECRET_ACCESS_KEY': 'test'}, default_url=None, node_selector={}, role_bindings=[], image_pull_secrets=[ImagePullSecret(name='cr-config', persist=False, data='ewogICAgImF1dGhzIjogewogICAgICAgICJjci50ZXJyYWR1ZS5jb20iOiB7CiAgICAgICAgICAgICJ1c2VybmFtZSI6ICJyb2JvdCRlb2VwY2EtcGx1cy1ybyIsCiAgICAgICAgICAgICJwYXNzd29yZCI6ICJQMlE4TnkyZ0lHODhkZkxveXlLN05QVUZVbHJOekFZSiIsCiAgICAgICAgICAgICJlbWFpbCI6ICJlb2VwY2EtcGx1c0B0ZXJyYWR1ZS5jb20iLAogICAgICAgICAgICAiYXV0aCI6ICJjbTlpYjNRa1pXOWxjR05oTFhCc2RYTXRjbTg2VURKUk9FNTVNbWRKUnpnNFpHWk1iM2w1U3pkT1VGVkdWV3h5VG5wQldVbz0iCiAgICAgICAgfQogICAgfQp9')], 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=[Manifest(name='manifests', key='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'}, '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': 'localstack-{{ spawner.user.name }}', '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': []}}}}, {'apiVersion': 'v1', 'kind': 'ConfigMap', 'metadata': {'name': 'my-config'}, 'data': {'ENV_VAR1': 'value1', 'ENV_VAR2': 'value2'}}, {'apiVersion': 'v1', 'kind': 'Secret', 'metadata': {'name': 'my-secret'}, 'type': 'Opaque', 'data': {'SECRET_KEY1': 'dmFsdWUx', 'SECRET_KEY2': 'dmFsdWUy'}}, {'apiVersion': 'v1', 'kind': 'Secret', 'metadata': {'name': 'aws-credentials-{{ spawner.user.name }}'}, 'type': 'Opaque', 'data': {'credentials': 'W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg='}}, {'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', 'property': 'the-key'}}]}}, {'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', 'property': '.dockerconfigjson'}}]}}], persist=False)], env_from_config_maps=None, env_from_secrets=None, secret_mounts=None),\n", - " Profile(id='profile_studio_desktop_qgis', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='QGIS on a Remote Desktop', description='Spatial visualization and decision-making tools for everyone', slug='eoepca_desktop_qgis', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=None, mem_limit='2G', mem_guarantee=None, image='eoepca/iga-remote-desktop-qgis:1.1.3', 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\\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\"', persist=False), ConfigMap(name='bash-login', key='bash-login', mount_path='/workspace/.bash_login', default_mode=None, readonly=True, content='source /workspace/.bashrc\\n', persist=False)], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='managed-nfs-storage', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace'}, default_url='desktop', node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[], manifests=None, env_from_config_maps=None, env_from_secrets=None, secret_mounts=None)]" - ] - }, - "execution_count": 16, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "image = \"eoepca/iga-remote-desktop-qgis:1.1.3\"\n", - "\n", - "qgis_profile = Profile( # type: ignore\n", - " id=\"profile_studio_desktop_qgis\",\n", - " groups=[\"group-a\", \"group-b\"],\n", - " definition=ProfileDefinition( # type: ignore\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( # type: ignore\n", - " cpu_limit=2,\n", - " mem_limit=\"2G\",\n", - " image=image,\n", - " ),\n", - " ),\n", - " node_selector={},\n", - " volumes=[workspace_volume],\n", - " config_maps=[bash_rc_cm, bash_login_cm],\n", - " pod_env_vars={\"HOME\": \"/workspace\"},\n", - " default_url=\"desktop\",\n", - " init_containers=[],\n", - ")\n", - "\n", - "profiles.append(qgis_profile)\n", - "profiles" - ] - }, - { - "attachments": {}, - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Write Configuration \n", - "In the code below, the user inspects all the configurations made in the previous section and then generates a [config.yaml](../../files/hub/config.yml) file. This file contains the complete configuration of the **App Hub**, including the different profiles defined for deployment. The `config.yaml` serves as the configuration blueprint, allowing the user to easily apply and share the profiles across different environments or clusters." - ] - }, - { - "cell_type": "code", - "execution_count": 17, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "[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\\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\"', persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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\\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\"', persist=False)], volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', size='50Gi', storage_class='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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='managed-nfs-storage', 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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=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 private image - 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='eoepca/iat-jupyterlab:develop', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='managed-nfs-storage', 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, env_from_config_maps=None, env_from_secrets=None, secret_mounts=None),\n", - " Profile(id='profile_studio_coder_stac', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='Understanding STAC for input/output data modelling', description='Understand the role of STAC in input/output data manifests in EO data processing workflows', slug='eoepca_coder_slug_stac', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', image='docker.io/eoepca/pde-code-server@sha256:f57a3d5eabcae667e0db6e84a57b0c07c692c88f0fb5c8f6900ab8d5e38fcd40', extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[ConfigMap(name='init', key='init', mount_path='/opt/init/.init.sh', default_mode=None, readonly=True, 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 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', persist=False), 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\\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\"', persist=False), ConfigMap(name='bash-login', key='bash-login', mount_path='/workspace/.bash_login', default_mode=None, readonly=True, content='source /workspace/.bashrc\\n', persist=False)], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='managed-nfs-storage', 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', 'XDG_CONFIG_HOME': '/workspace/.local', 'XDG_DATA_HOME': '/workspace/.local/share/', 'CWLTOOL_OPTIONS': '--podman', 'CODE_SERVER_WS': '/workspace/stac-eoap', 'AWS_DEFAULT_REGION': 'us-east-1', 'AWS_ACCESS_KEY_ID': 'test', 'AWS_SECRET_ACCESS_KEY': 'test'}, default_url=None, node_selector={}, role_bindings=[], image_pull_secrets=[ImagePullSecret(name='cr-config', persist=False, data='ewogICAgImF1dGhzIjogewogICAgICAgICJjci50ZXJyYWR1ZS5jb20iOiB7CiAgICAgICAgICAgICJ1c2VybmFtZSI6ICJyb2JvdCRlb2VwY2EtcGx1cy1ybyIsCiAgICAgICAgICAgICJwYXNzd29yZCI6ICJQMlE4TnkyZ0lHODhkZkxveXlLN05QVUZVbHJOekFZSiIsCiAgICAgICAgICAgICJlbWFpbCI6ICJlb2VwY2EtcGx1c0B0ZXJyYWR1ZS5jb20iLAogICAgICAgICAgICAiYXV0aCI6ICJjbTlpYjNRa1pXOWxjR05oTFhCc2RYTXRjbTg2VURKUk9FNTVNbWRKUnpnNFpHWk1iM2w1U3pkT1VGVkdWV3h5VG5wQldVbz0iCiAgICAgICAgfQogICAgfQp9')], 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=[Manifest(name='manifests', key='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'}, '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': 'localstack-{{ spawner.user.name }}', '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': []}}}}, {'apiVersion': 'v1', 'kind': 'ConfigMap', 'metadata': {'name': 'my-config'}, 'data': {'ENV_VAR1': 'value1', 'ENV_VAR2': 'value2'}}, {'apiVersion': 'v1', 'kind': 'Secret', 'metadata': {'name': 'my-secret'}, 'type': 'Opaque', 'data': {'SECRET_KEY1': 'dmFsdWUx', 'SECRET_KEY2': 'dmFsdWUy'}}, {'apiVersion': 'v1', 'kind': 'Secret', 'metadata': {'name': 'aws-credentials-{{ spawner.user.name }}'}, 'type': 'Opaque', 'data': {'credentials': 'W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg='}}, {'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', 'property': 'the-key'}}]}}, {'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', 'property': '.dockerconfigjson'}}]}}], persist=False)], env_from_config_maps=None, env_from_secrets=None, secret_mounts=None),\n", - " Profile(id='profile_studio_desktop_qgis', groups=['group-a', 'group-b'], definition=ProfileDefinition(display_name='QGIS on a Remote Desktop', description='Spatial visualization and decision-making tools for everyone', slug='eoepca_desktop_qgis', default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=None, mem_limit='2G', mem_guarantee=None, image='eoepca/iga-remote-desktop-qgis:1.1.3', 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\\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\"', persist=False), ConfigMap(name='bash-login', key='bash-login', mount_path='/workspace/.bash_login', default_mode=None, readonly=True, content='source /workspace/.bashrc\\n', persist=False)], volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', size='50Gi', storage_class='managed-nfs-storage', access_modes=['ReadWriteOnce'], volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), persist=True)], pod_env_vars={'HOME': '/workspace'}, default_url='desktop', node_selector={}, role_bindings=None, image_pull_secrets=[], init_containers=[], manifests=None, env_from_config_maps=None, env_from_secrets=None, secret_mounts=None)]\n", - "(\"config: profiles=[Profile(id='profile_studio_coder1', groups=['group-a', \"\n", - " \"'group-b'], definition=ProfileDefinition(display_name='Code Server Small', \"\n", - " \"description=None, slug='ellip_studio_coder_slug_s', default=False, \"\n", - " 'kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=None, '\n", - " \"mem_limit='8G', mem_guarantee=None, image='eoepca/pde-code-server:develop', \"\n", - " 'extra_resource_limits={}, extra_resource_guarantees={})), '\n", - " \"config_maps=[ConfigMap(name='bash-rc', key='bash-rc', \"\n", - " \"mount_path='/workspace/.bashrc', default_mode=None, readonly=True, \"\n", - " 'content=\\'alias ll=\"ls -l\"\\\\nalias calrissian=\"/opt/conda/bin/calrissian '\n", - " '--pod-nodeselectors /etc/calrissian/pod-node-selector.yml --stdout '\n", - " '/calrissian/results.json --max-ram 16G --max-cores \"8\" --tmp-outdir-prefix '\n", - " '/calrissian/tmp/ --outdir /calrissian/\"\\\\nalias '\n", - " 'cwltool=\"/opt/conda/bin/cwltool --podman\"\\\\n. '\n", - " '/home/jovyan/.bashrc\\\\n\\\\n#alias aws=\"aws '\n", - " '--endpoint-url=http://localstack:4566\"\\\\n\\\\n# >>> conda initialize >>>\\\\n# '\n", - " \"!! Contents within this block are managed by \\\\'conda init\\\\' \"\n", - " '!!\\\\n__conda_setup=\"$(\\\\\\'/opt/conda/bin/conda\\\\\\' \\\\\\'shell.bash\\\\\\' '\n", - " '\\\\\\'hook\\\\\\' 2> /dev/null)\"\\\\nif [ $? -eq 0 ]; then\\\\n eval '\n", - " '\"$__conda_setup\"\\\\nelse\\\\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; '\n", - " 'then\\\\n . \"/opt/conda/etc/profile.d/conda.sh\"\\\\n else\\\\n '\n", - " 'export PATH=\"/srv/conda/bin:$PATH\"\\\\n fi\\\\nfi\\\\nunset '\n", - " '__conda_setup\\\\n\\\\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\\\\n '\n", - " '. \"/opt/conda/etc/profile.d/mamba.sh\"\\\\nfi\\\\n# <<< conda initialize '\n", - " '<<<\\\\n\\\\na={{spawner.user.name}}\\\\n\\\\nalias aws=\"aws '\n", - " '--endpoint-url=http://localstack-jupyter-{{spawner.user.name}}:4566\"\\', '\n", - " \"persist=False)], volumes=[Volume(name='calrissian-volume', \"\n", - " \"claim_name='calrissian-claim', size='50Gi', \"\n", - " \"storage_class='managed-nfs-storage', access_modes=['ReadWriteMany'], \"\n", - " \"volume_mount=VolumeMount(name='calrissian-volume', \"\n", - " \"mount_path='/calrissian'), persist=False), Volume(name='workspace-volume', \"\n", - " \"claim_name='workspace-claim', size='50Gi', \"\n", - " \"storage_class='managed-nfs-storage', access_modes=['ReadWriteOnce'], \"\n", - " \"volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), \"\n", - " \"persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': \"\n", - " \"'/workspace/.envs'}, default_url=None, node_selector={}, role_bindings=None, \"\n", - " 'image_pull_secrets=[], init_containers=[], manifests=None, '\n", - " 'env_from_config_maps=None, env_from_secrets=None, secret_mounts=None), '\n", - " \"Profile(id='profile_studio_coder2', groups=['group-a', 'group-b'], \"\n", - " \"definition=ProfileDefinition(display_name='Code Server Medium', \"\n", - " \"description=None, slug='ellip_studio_coder_slug_m', default=False, \"\n", - " 'kubespawner_override=KubespawnerOverride(cpu_limit=4, cpu_guarantee=None, '\n", - " \"mem_limit='12G', mem_guarantee=None, image='eoepca/pde-code-server:develop', \"\n", - " 'extra_resource_limits={}, extra_resource_guarantees={})), '\n", - " \"config_maps=[ConfigMap(name='bash-rc', key='bash-rc', \"\n", - " \"mount_path='/workspace/.bashrc', default_mode=None, readonly=True, \"\n", - " 'content=\\'alias ll=\"ls -l\"\\\\nalias calrissian=\"/opt/conda/bin/calrissian '\n", - " '--pod-nodeselectors /etc/calrissian/pod-node-selector.yml --stdout '\n", - " '/calrissian/results.json --max-ram 16G --max-cores \"8\" --tmp-outdir-prefix '\n", - " '/calrissian/tmp/ --outdir /calrissian/\"\\\\nalias '\n", - " 'cwltool=\"/opt/conda/bin/cwltool --podman\"\\\\n. '\n", - " '/home/jovyan/.bashrc\\\\n\\\\n#alias aws=\"aws '\n", - " '--endpoint-url=http://localstack:4566\"\\\\n\\\\n# >>> conda initialize >>>\\\\n# '\n", - " \"!! Contents within this block are managed by \\\\'conda init\\\\' \"\n", - " '!!\\\\n__conda_setup=\"$(\\\\\\'/opt/conda/bin/conda\\\\\\' \\\\\\'shell.bash\\\\\\' '\n", - " '\\\\\\'hook\\\\\\' 2> /dev/null)\"\\\\nif [ $? -eq 0 ]; then\\\\n eval '\n", - " '\"$__conda_setup\"\\\\nelse\\\\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; '\n", - " 'then\\\\n . \"/opt/conda/etc/profile.d/conda.sh\"\\\\n else\\\\n '\n", - " 'export PATH=\"/srv/conda/bin:$PATH\"\\\\n fi\\\\nfi\\\\nunset '\n", - " '__conda_setup\\\\n\\\\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\\\\n '\n", - " '. \"/opt/conda/etc/profile.d/mamba.sh\"\\\\nfi\\\\n# <<< conda initialize '\n", - " '<<<\\\\n\\\\na={{spawner.user.name}}\\\\n\\\\nalias aws=\"aws '\n", - " '--endpoint-url=http://localstack-jupyter-{{spawner.user.name}}:4566\"\\', '\n", - " \"persist=False)], volumes=[Volume(name='calrissian-volume', \"\n", - " \"claim_name='calrissian-claim', size='50Gi', \"\n", - " \"storage_class='managed-nfs-storage', access_modes=['ReadWriteMany'], \"\n", - " \"volume_mount=VolumeMount(name='calrissian-volume', \"\n", - " \"mount_path='/calrissian'), persist=False), Volume(name='workspace-volume', \"\n", - " \"claim_name='workspace-claim', size='50Gi', \"\n", - " \"storage_class='managed-nfs-storage', access_modes=['ReadWriteOnce'], \"\n", - " \"volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), \"\n", - " \"persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': \"\n", - " \"'/workspace/.envs'}, default_url=None, node_selector={}, role_bindings=None, \"\n", - " 'image_pull_secrets=[], init_containers=[], manifests=None, '\n", - " 'env_from_config_maps=None, env_from_secrets=None, secret_mounts=None), '\n", - " \"Profile(id='profile_demo_init_script', groups=['group-a', 'group-b'], \"\n", - " \"definition=ProfileDefinition(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', default=False, \"\n", - " 'kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, '\n", - " \"mem_limit='6G', mem_guarantee='4G', image='eoepca/pde-code-server:develop', \"\n", - " 'extra_resource_limits={}, extra_resource_guarantees={})), '\n", - " \"config_maps=[ConfigMap(name='init', key='init', \"\n", - " \"mount_path='/opt/init/.init.sh', default_mode='0660', readonly=True, \"\n", - " 'content=\"set -x\\\\n\\\\ncd /workspace\\\\n\\\\ngit clone '\n", - " \"'https://github.com/eoap/mastering-app-package.git'\\\\n\\\\ncode-server \"\n", - " '--install-extension ms-python.python\\\\ncode-server --install-extension '\n", - " 'redhat.vscode-yaml\\\\ncode-server --install-extension '\n", - " 'sbg-rabix.benten-cwl\\\\ncode-server --install-extension '\n", - " 'ms-toolsai.jupyter\\\\n\\\\nln -s /workspace/.local/share/code-server/extensions '\n", - " '/workspace/extensions\\\\n\\\\nexit 0\\\\n\", persist=False)], '\n", - " \"volumes=[Volume(name='calrissian-volume', claim_name='calrissian-claim', \"\n", - " \"size='50Gi', storage_class='managed-nfs-storage', \"\n", - " \"access_modes=['ReadWriteMany'], \"\n", - " \"volume_mount=VolumeMount(name='calrissian-volume', \"\n", - " \"mount_path='/calrissian'), persist=False), Volume(name='workspace-volume', \"\n", - " \"claim_name='workspace-claim', size='50Gi', \"\n", - " \"storage_class='managed-nfs-storage', access_modes=['ReadWriteOnce'], \"\n", - " \"volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), \"\n", - " \"persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': \"\n", - " \"'/workspace/.envs', 'CONDARC': '/workspace/.condarc', 'XDG_RUNTIME_DIR': \"\n", - " \"'/workspace/.local', 'CODE_SERVER_WS': '/workspace/mastering-app-package'}, \"\n", - " 'default_url=None, node_selector={}, role_bindings=None, '\n", - " 'image_pull_secrets=[], '\n", - " \"init_containers=[InitContainer(name='init-file-on-volume', \"\n", - " \"image='eoepca/pde-code-server:develop', command=['sh', '-c', 'sh \"\n", - " \"/opt/init/.init.sh'], volume_mounts=[VolumeMount(name='workspace-volume', \"\n", - " \"mount_path='/workspace'), InitContainerVolumeMount(name='init', \"\n", - " \"mount_path='/opt/init/.init.sh', sub_path='init')])], manifests=None, \"\n", - " 'env_from_config_maps=None, env_from_secrets=None, secret_mounts=None), '\n", - " \"Profile(id='profile_jupyter_lab', groups=['group-c'], \"\n", - " \"definition=ProfileDefinition(display_name='Jupyter Lab', \"\n", - " \"description='Jupyter Lab with Python 3.11', slug='eoepca_jupyter_lab', \"\n", - " 'default=False, kubespawner_override=KubespawnerOverride(cpu_limit=2, '\n", - " \"cpu_guarantee=1, mem_limit='6G', mem_guarantee='4G', \"\n", - " \"image='jupyter/scipy-notebook', extra_resource_limits={}, \"\n", - " 'extra_resource_guarantees={})), config_maps=[], '\n", - " \"volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', \"\n", - " \"size='50Gi', storage_class='managed-nfs-storage', \"\n", - " \"access_modes=['ReadWriteOnce'], \"\n", - " \"volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), \"\n", - " \"persist=True)], pod_env_vars={'HOME': '/workspace', 'XDG_RUNTIME_DIR': \"\n", - " \"'/workspace/.local', 'XDG_CONFIG_HOME': '/workspace/.config'}, \"\n", - " 'default_url=None, node_selector={}, role_bindings=None, '\n", - " 'image_pull_secrets=[], init_containers=[], manifests=None, '\n", - " 'env_from_config_maps=None, env_from_secrets=None, secret_mounts=None), '\n", - " \"Profile(id='profile_jupyter_lab_2', groups=['group-c'], \"\n", - " \"definition=ProfileDefinition(display_name='Jupyter Lab - profile 2', \"\n", - " \"description='Jupyter Lab with Python 3.11 private image - demoes the use of \"\n", - " \"an image pull secret', slug='eoepca_jupyter_lab_2', default=False, \"\n", - " 'kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, '\n", - " \"mem_limit='6G', mem_guarantee='4G', image='eoepca/iat-jupyterlab:develop', \"\n", - " 'extra_resource_limits={}, extra_resource_guarantees={})), config_maps=[], '\n", - " \"volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', \"\n", - " \"size='50Gi', storage_class='managed-nfs-storage', \"\n", - " \"access_modes=['ReadWriteOnce'], \"\n", - " \"volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), \"\n", - " \"persist=True)], pod_env_vars={'HOME': '/workspace', 'XDG_RUNTIME_DIR': \"\n", - " \"'/workspace/.local', 'XDG_CONFIG_HOME': '/workspace/.config'}, \"\n", - " 'default_url=None, node_selector={}, role_bindings=None, '\n", - " \"image_pull_secrets=[ImagePullSecret(name='cr-config', persist=False, \"\n", - " \"data='ewogICAgImF1dGhzIjogewogICAgICAgICJjci50ZXJyYWR1ZS5jb20iOiB7CiAgICAgICAgICAgICJ1c2VybmFtZSI6ICJyb2JvdCRlb2VwY2EtcGx1cy1ybyIsCiAgICAgICAgICAgICJwYXNzd29yZCI6ICJQMlE4TnkyZ0lHODhkZkxveXlLN05QVUZVbHJOekFZSiIsCiAgICAgICAgICAgICJlbWFpbCI6ICJlb2VwY2EtcGx1c0B0ZXJyYWR1ZS5jb20iLAogICAgICAgICAgICAiYXV0aCI6ICJjbTlpYjNRa1pXOWxjR05oTFhCc2RYTXRjbTg2VURKUk9FNTVNbWRKUnpnNFpHWk1iM2w1U3pkT1VGVkdWV3h5VG5wQldVbz0iCiAgICAgICAgfQogICAgfQp9')], \"\n", - " 'init_containers=[], manifests=None, env_from_config_maps=None, '\n", - " 'env_from_secrets=None, secret_mounts=None), '\n", - " \"Profile(id='profile_studio_coder_stac', groups=['group-a', 'group-b'], \"\n", - " \"definition=ProfileDefinition(display_name='Understanding STAC for \"\n", - " \"input/output data modelling', description='Understand the role of STAC in \"\n", - " \"input/output data manifests in EO data processing workflows', \"\n", - " \"slug='eoepca_coder_slug_stac', default=False, \"\n", - " 'kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=1, '\n", - " \"mem_limit='6G', mem_guarantee='4G', \"\n", - " \"image='docker.io/eoepca/pde-code-server@sha256:f57a3d5eabcae667e0db6e84a57b0c07c692c88f0fb5c8f6900ab8d5e38fcd40', \"\n", - " 'extra_resource_limits={}, extra_resource_guarantees={})), '\n", - " \"config_maps=[ConfigMap(name='init', key='init', \"\n", - " \"mount_path='/opt/init/.init.sh', default_mode=None, readonly=True, \"\n", - " \"content='set -x \\\\n\\\\ncd /workspace\\\\n\\\\ngit clone \"\n", - " \"\\\\'https://github.com/eoap/stac-eoap.git\\\\'\\\\n\\\\ncode-server \"\n", - " '--install-extension ms-python.python \\\\ncode-server --install-extension '\n", - " 'redhat.vscode-yaml\\\\ncode-server --install-extension '\n", - " 'sbg-rabix.benten-cwl\\\\ncode-server --install-extension '\n", - " 'ms-toolsai.jupyter\\\\n\\\\nln -s /workspace/.local/share/code-server/extensions '\n", - " '/workspace/extensions\\\\n\\\\nmkdir -p /workspace/User/\\\\n\\\\necho '\n", - " '\\\\\\'{\"workbench.colorTheme\": \"Visual Studio Dark\"}\\\\\\' > '\n", - " '/workspace/User/settings.json\\\\n\\\\npython -m venv /workspace/.venv\\\\nsource '\n", - " '/workspace/.venv/bin/activate\\\\n/workspace/.venv/bin/python -m pip install '\n", - " '--no-cache-dir stactools rasterio requests stac-asset click-logging tabulate '\n", - " 'tqdm pystac-client ipykernel loguru scikit-image rio_stac '\n", - " 'boto3==1.35.23\\\\n\\\\n/workspace/.venv/bin/python -m pip install --index-url '\n", - " 'https://test.pypi.org/simple cwl-wrapper\\\\n\\\\n/workspace/.venv/bin/python -m '\n", - " 'ipykernel install --user --name stac_env --display-name \"Python '\n", - " '(STAC)\"\\\\n\\\\nexport AWS_DEFAULT_REGION=\"us-east-1\"\\\\nexport '\n", - " 'AWS_ACCESS_KEY_ID=\"test\"\\\\nexport AWS_SECRET_ACCESS_KEY=\"test\"\\\\naws s3 mb '\n", - " \"s3://results --endpoint-url=http://localstack:4566\\\\n\\\\nexit 0', \"\n", - " \"persist=False), ConfigMap(name='bash-rc', key='bash-rc', \"\n", - " \"mount_path='/workspace/.bashrc', default_mode=None, readonly=True, \"\n", - " 'content=\\'alias ll=\"ls -l\"\\\\nalias calrissian=\"/opt/conda/bin/calrissian '\n", - " '--pod-nodeselectors /etc/calrissian/pod-node-selector.yml --stdout '\n", - " '/calrissian/results.json --max-ram 16G --max-cores \"8\" --tmp-outdir-prefix '\n", - " '/calrissian/tmp/ --outdir /calrissian/\"\\\\nalias '\n", - " 'cwltool=\"/opt/conda/bin/cwltool --podman\"\\\\n. '\n", - " '/home/jovyan/.bashrc\\\\n\\\\n#alias aws=\"aws '\n", - " '--endpoint-url=http://localstack:4566\"\\\\n\\\\n# >>> conda initialize >>>\\\\n# '\n", - " \"!! Contents within this block are managed by \\\\'conda init\\\\' \"\n", - " '!!\\\\n__conda_setup=\"$(\\\\\\'/opt/conda/bin/conda\\\\\\' \\\\\\'shell.bash\\\\\\' '\n", - " '\\\\\\'hook\\\\\\' 2> /dev/null)\"\\\\nif [ $? -eq 0 ]; then\\\\n eval '\n", - " '\"$__conda_setup\"\\\\nelse\\\\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; '\n", - " 'then\\\\n . \"/opt/conda/etc/profile.d/conda.sh\"\\\\n else\\\\n '\n", - " 'export PATH=\"/srv/conda/bin:$PATH\"\\\\n fi\\\\nfi\\\\nunset '\n", - " '__conda_setup\\\\n\\\\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\\\\n '\n", - " '. \"/opt/conda/etc/profile.d/mamba.sh\"\\\\nfi\\\\n# <<< conda initialize '\n", - " '<<<\\\\n\\\\na={{spawner.user.name}}\\\\n\\\\nalias aws=\"aws '\n", - " '--endpoint-url=http://localstack-jupyter-{{spawner.user.name}}:4566\"\\', '\n", - " \"persist=False), ConfigMap(name='bash-login', key='bash-login', \"\n", - " \"mount_path='/workspace/.bash_login', default_mode=None, readonly=True, \"\n", - " \"content='source /workspace/.bashrc\\\\n', persist=False)], \"\n", - " \"volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', \"\n", - " \"size='50Gi', storage_class='managed-nfs-storage', \"\n", - " \"access_modes=['ReadWriteOnce'], \"\n", - " \"volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), \"\n", - " \"persist=True)], pod_env_vars={'HOME': '/workspace', 'CONDA_ENVS_PATH': \"\n", - " \"'/workspace/.envs', 'CONDARC': '/workspace/.condarc', 'XDG_RUNTIME_DIR': \"\n", - " \"'/workspace/.local', 'XDG_CONFIG_HOME': '/workspace/.local', \"\n", - " \"'XDG_DATA_HOME': '/workspace/.local/share/', 'CWLTOOL_OPTIONS': '--podman', \"\n", - " \"'CODE_SERVER_WS': '/workspace/stac-eoap', 'AWS_DEFAULT_REGION': 'us-east-1', \"\n", - " \"'AWS_ACCESS_KEY_ID': 'test', 'AWS_SECRET_ACCESS_KEY': 'test'}, \"\n", - " 'default_url=None, node_selector={}, role_bindings=[], '\n", - " \"image_pull_secrets=[ImagePullSecret(name='cr-config', persist=False, \"\n", - " \"data='ewogICAgImF1dGhzIjogewogICAgICAgICJjci50ZXJyYWR1ZS5jb20iOiB7CiAgICAgICAgICAgICJ1c2VybmFtZSI6ICJyb2JvdCRlb2VwY2EtcGx1cy1ybyIsCiAgICAgICAgICAgICJwYXNzd29yZCI6ICJQMlE4TnkyZ0lHODhkZkxveXlLN05QVUZVbHJOekFZSiIsCiAgICAgICAgICAgICJlbWFpbCI6ICJlb2VwY2EtcGx1c0B0ZXJyYWR1ZS5jb20iLAogICAgICAgICAgICAiYXV0aCI6ICJjbTlpYjNRa1pXOWxjR05oTFhCc2RYTXRjbTg2VURKUk9FNTVNbWRKUnpnNFpHWk1iM2w1U3pkT1VGVkdWV3h5VG5wQldVbz0iCiAgICAgICAgfQogICAgfQp9')], \"\n", - " \"init_containers=[InitContainer(name='init-file-on-volume', \"\n", - " \"image='eoepca/pde-code-server:develop', command=['sh', '-c', 'sh \"\n", - " \"/opt/init/.init.sh'], volume_mounts=[VolumeMount(name='workspace-volume', \"\n", - " \"mount_path='/workspace'), InitContainerVolumeMount(name='init', \"\n", - " \"mount_path='/opt/init/.init.sh', sub_path='init')])], \"\n", - " \"manifests=[Manifest(name='manifests', key='manifests', \"\n", - " \"content=[{'apiVersion': 'v1', 'kind': 'ServiceAccount', 'metadata': {'name': \"\n", - " \"'localstack'}}, {'apiVersion': 'rbac.authorization.k8s.io/v1', 'kind': \"\n", - " \"'Role', 'metadata': {'name': 'localstack'}, 'rules': [{'apiGroups': [''], \"\n", - " \"'resources': ['pods'], 'verbs': ['*']}, {'apiGroups': [''], 'resources': \"\n", - " \"['pods/log'], 'verbs': ['get']}, {'apiGroups': [''], 'resources': \"\n", - " \"['pods/exec'], 'verbs': ['get', 'create']}, {'apiGroups': [''], 'resources': \"\n", - " \"['services'], 'verbs': ['get', 'list']}]}, {'apiVersion': \"\n", - " \"'rbac.authorization.k8s.io/v1', 'kind': 'RoleBinding', 'metadata': {'name': \"\n", - " \"'localstack'}, 'subjects': [{'kind': 'ServiceAccount', 'name': \"\n", - " \"'localstack'}], 'roleRef': {'kind': 'Role', 'name': 'localstack', \"\n", - " \"'apiGroup': 'rbac.authorization.k8s.io'}}, {'apiVersion': 'v1', 'kind': \"\n", - " \"'Service', 'metadata': {'name': 'localstack'}, 'spec': {'type': 'ClusterIP', \"\n", - " \"'ports': [{'name': 'edge', 'port': 4566, 'targetPort': 4566}, {'name': \"\n", - " \"'external-service-port-4510', 'port': 4510, 'targetPort': 'ext-svc-4510'}, \"\n", - " \"{'name': 'external-service-port-4511', 'port': 4511, 'targetPort': \"\n", - " \"'ext-svc-4511'}, {'name': 'external-service-port-4512', 'port': 4512, \"\n", - " \"'targetPort': 'ext-svc-4512'}, {'name': 'external-service-port-4513', \"\n", - " \"'port': 4513, 'targetPort': 'ext-svc-4513'}, {'name': \"\n", - " \"'external-service-port-4514', 'port': 4514, 'targetPort': 'ext-svc-4514'}, \"\n", - " \"{'name': 'external-service-port-4515', 'port': 4515, 'targetPort': \"\n", - " \"'ext-svc-4515'}, {'name': 'external-service-port-4516', 'port': 4516, \"\n", - " \"'targetPort': 'ext-svc-4516'}, {'name': 'external-service-port-4517', \"\n", - " \"'port': 4517, 'targetPort': 'ext-svc-4517'}, {'name': \"\n", - " \"'external-service-port-4518', 'port': 4518, 'targetPort': 'ext-svc-4518'}, \"\n", - " \"{'name': 'external-service-port-4519', 'port': 4519, 'targetPort': \"\n", - " \"'ext-svc-4519'}, {'name': 'external-service-port-4520', 'port': 4520, \"\n", - " \"'targetPort': 'ext-svc-4520'}, {'name': 'external-service-port-4521', \"\n", - " \"'port': 4521, 'targetPort': 'ext-svc-4521'}, {'name': \"\n", - " \"'external-service-port-4522', 'port': 4522, 'targetPort': 'ext-svc-4522'}, \"\n", - " \"{'name': 'external-service-port-4523', 'port': 4523, 'targetPort': \"\n", - " \"'ext-svc-4523'}, {'name': 'external-service-port-4524', 'port': 4524, \"\n", - " \"'targetPort': 'ext-svc-4524'}, {'name': 'external-service-port-4525', \"\n", - " \"'port': 4525, 'targetPort': 'ext-svc-4525'}, {'name': \"\n", - " \"'external-service-port-4526', 'port': 4526, 'targetPort': 'ext-svc-4526'}, \"\n", - " \"{'name': 'external-service-port-4527', 'port': 4527, 'targetPort': \"\n", - " \"'ext-svc-4527'}, {'name': 'external-service-port-4528', 'port': 4528, \"\n", - " \"'targetPort': 'ext-svc-4528'}, {'name': 'external-service-port-4529', \"\n", - " \"'port': 4529, 'targetPort': 'ext-svc-4529'}, {'name': \"\n", - " \"'external-service-port-4530', 'port': 4530, 'targetPort': 'ext-svc-4530'}, \"\n", - " \"{'name': 'external-service-port-4531', 'port': 4531, 'targetPort': \"\n", - " \"'ext-svc-4531'}, {'name': 'external-service-port-4532', 'port': 4532, \"\n", - " \"'targetPort': 'ext-svc-4532'}, {'name': 'external-service-port-4533', \"\n", - " \"'port': 4533, 'targetPort': 'ext-svc-4533'}, {'name': \"\n", - " \"'external-service-port-4534', 'port': 4534, 'targetPort': 'ext-svc-4534'}, \"\n", - " \"{'name': 'external-service-port-4535', 'port': 4535, 'targetPort': \"\n", - " \"'ext-svc-4535'}, {'name': 'external-service-port-4536', 'port': 4536, \"\n", - " \"'targetPort': 'ext-svc-4536'}, {'name': 'external-service-port-4537', \"\n", - " \"'port': 4537, 'targetPort': 'ext-svc-4537'}, {'name': \"\n", - " \"'external-service-port-4538', 'port': 4538, 'targetPort': 'ext-svc-4538'}, \"\n", - " \"{'name': 'external-service-port-4539', 'port': 4539, 'targetPort': \"\n", - " \"'ext-svc-4539'}, {'name': 'external-service-port-4540', 'port': 4540, \"\n", - " \"'targetPort': 'ext-svc-4540'}, {'name': 'external-service-port-4541', \"\n", - " \"'port': 4541, 'targetPort': 'ext-svc-4541'}, {'name': \"\n", - " \"'external-service-port-4542', 'port': 4542, 'targetPort': 'ext-svc-4542'}, \"\n", - " \"{'name': 'external-service-port-4543', 'port': 4543, 'targetPort': \"\n", - " \"'ext-svc-4543'}, {'name': 'external-service-port-4544', 'port': 4544, \"\n", - " \"'targetPort': 'ext-svc-4544'}, {'name': 'external-service-port-4545', \"\n", - " \"'port': 4545, 'targetPort': 'ext-svc-4545'}, {'name': \"\n", - " \"'external-service-port-4546', 'port': 4546, 'targetPort': 'ext-svc-4546'}, \"\n", - " \"{'name': 'external-service-port-4547', 'port': 4547, 'targetPort': \"\n", - " \"'ext-svc-4547'}, {'name': 'external-service-port-4548', 'port': 4548, \"\n", - " \"'targetPort': 'ext-svc-4548'}, {'name': 'external-service-port-4549', \"\n", - " \"'port': 4549, 'targetPort': 'ext-svc-4549'}, {'name': \"\n", - " \"'external-service-port-4550', 'port': 4550, 'targetPort': 'ext-svc-4550'}, \"\n", - " \"{'name': 'external-service-port-4551', 'port': 4551, 'targetPort': \"\n", - " \"'ext-svc-4551'}, {'name': 'external-service-port-4552', 'port': 4552, \"\n", - " \"'targetPort': 'ext-svc-4552'}, {'name': 'external-service-port-4553', \"\n", - " \"'port': 4553, 'targetPort': 'ext-svc-4553'}, {'name': \"\n", - " \"'external-service-port-4554', 'port': 4554, 'targetPort': 'ext-svc-4554'}, \"\n", - " \"{'name': 'external-service-port-4555', 'port': 4555, 'targetPort': \"\n", - " \"'ext-svc-4555'}, {'name': 'external-service-port-4556', 'port': 4556, \"\n", - " \"'targetPort': 'ext-svc-4556'}, {'name': 'external-service-port-4557', \"\n", - " \"'port': 4557, 'targetPort': 'ext-svc-4557'}, {'name': \"\n", - " \"'external-service-port-4558', 'port': 4558, 'targetPort': 'ext-svc-4558'}, \"\n", - " \"{'name': 'external-service-port-4559', 'port': 4559, 'targetPort': \"\n", - " \"'ext-svc-4559'}], 'selector': {'app.kubernetes.io/name': 'localstack', \"\n", - " \"'app.kubernetes.io/instance': 'localstack'}}}, {'apiVersion': 'apps/v1', \"\n", - " \"'kind': 'Deployment', 'metadata': {'name': 'localstack'}, 'spec': \"\n", - " \"{'replicas': 1, 'strategy': {'type': 'RollingUpdate'}, 'selector': \"\n", - " \"{'matchLabels': {'app.kubernetes.io/name': 'localstack', \"\n", - " \"'app.kubernetes.io/instance': 'localstack'}}, 'template': {'metadata': \"\n", - " \"{'labels': {'app': 'localstack-{{ spawner.user.name }}', \"\n", - " \"'app.kubernetes.io/name': 'localstack', 'app.kubernetes.io/instance': \"\n", - " \"'localstack'}}, 'spec': {'serviceAccountName': 'localstack', \"\n", - " \"'securityContext': {}, 'containers': [{'name': 'localstack', \"\n", - " \"'securityContext': {}, 'image': 'localstack/localstack:latest', \"\n", - " \"'imagePullPolicy': 'IfNotPresent', 'ports': [{'name': 'edge', \"\n", - " \"'containerPort': 4566, 'protocol': 'TCP'}, {'name': 'ext-svc-4510', \"\n", - " \"'containerPort': 4510, 'protocol': 'TCP'}, {'name': 'ext-svc-4511', \"\n", - " \"'containerPort': 4511, 'protocol': 'TCP'}, {'name': 'ext-svc-4512', \"\n", - " \"'containerPort': 4512, 'protocol': 'TCP'}, {'name': 'ext-svc-4513', \"\n", - " \"'containerPort': 4513, 'protocol': 'TCP'}, {'name': 'ext-svc-4514', \"\n", - " \"'containerPort': 4514, 'protocol': 'TCP'}, {'name': 'ext-svc-4515', \"\n", - " \"'containerPort': 4515, 'protocol': 'TCP'}, {'name': 'ext-svc-4516', \"\n", - " \"'containerPort': 4516, 'protocol': 'TCP'}, {'name': 'ext-svc-4517', \"\n", - " \"'containerPort': 4517, 'protocol': 'TCP'}, {'name': 'ext-svc-4518', \"\n", - " \"'containerPort': 4518, 'protocol': 'TCP'}, {'name': 'ext-svc-4519', \"\n", - " \"'containerPort': 4519, 'protocol': 'TCP'}, {'name': 'ext-svc-4520', \"\n", - " \"'containerPort': 4520, 'protocol': 'TCP'}, {'name': 'ext-svc-4521', \"\n", - " \"'containerPort': 4521, 'protocol': 'TCP'}, {'name': 'ext-svc-4522', \"\n", - " \"'containerPort': 4522, 'protocol': 'TCP'}, {'name': 'ext-svc-4523', \"\n", - " \"'containerPort': 4523, 'protocol': 'TCP'}, {'name': 'ext-svc-4524', \"\n", - " \"'containerPort': 4524, 'protocol': 'TCP'}, {'name': 'ext-svc-4525', \"\n", - " \"'containerPort': 4525, 'protocol': 'TCP'}, {'name': 'ext-svc-4526', \"\n", - " \"'containerPort': 4526, 'protocol': 'TCP'}, {'name': 'ext-svc-4527', \"\n", - " \"'containerPort': 4527, 'protocol': 'TCP'}, {'name': 'ext-svc-4528', \"\n", - " \"'containerPort': 4528, 'protocol': 'TCP'}, {'name': 'ext-svc-4529', \"\n", - " \"'containerPort': 4529, 'protocol': 'TCP'}, {'name': 'ext-svc-4530', \"\n", - " \"'containerPort': 4530, 'protocol': 'TCP'}, {'name': 'ext-svc-4531', \"\n", - " \"'containerPort': 4531, 'protocol': 'TCP'}, {'name': 'ext-svc-4532', \"\n", - " \"'containerPort': 4532, 'protocol': 'TCP'}, {'name': 'ext-svc-4533', \"\n", - " \"'containerPort': 4533, 'protocol': 'TCP'}, {'name': 'ext-svc-4534', \"\n", - " \"'containerPort': 4534, 'protocol': 'TCP'}, {'name': 'ext-svc-4535', \"\n", - " \"'containerPort': 4535, 'protocol': 'TCP'}, {'name': 'ext-svc-4536', \"\n", - " \"'containerPort': 4536, 'protocol': 'TCP'}, {'name': 'ext-svc-4537', \"\n", - " \"'containerPort': 4537, 'protocol': 'TCP'}, {'name': 'ext-svc-4538', \"\n", - " \"'containerPort': 4538, 'protocol': 'TCP'}, {'name': 'ext-svc-4539', \"\n", - " \"'containerPort': 4539, 'protocol': 'TCP'}, {'name': 'ext-svc-4540', \"\n", - " \"'containerPort': 4540, 'protocol': 'TCP'}, {'name': 'ext-svc-4541', \"\n", - " \"'containerPort': 4541, 'protocol': 'TCP'}, {'name': 'ext-svc-4542', \"\n", - " \"'containerPort': 4542, 'protocol': 'TCP'}, {'name': 'ext-svc-4543', \"\n", - " \"'containerPort': 4543, 'protocol': 'TCP'}, {'name': 'ext-svc-4544', \"\n", - " \"'containerPort': 4544, 'protocol': 'TCP'}, {'name': 'ext-svc-4545', \"\n", - " \"'containerPort': 4545, 'protocol': 'TCP'}, {'name': 'ext-svc-4546', \"\n", - " \"'containerPort': 4546, 'protocol': 'TCP'}, {'name': 'ext-svc-4547', \"\n", - " \"'containerPort': 4547, 'protocol': 'TCP'}, {'name': 'ext-svc-4548', \"\n", - " \"'containerPort': 4548, 'protocol': 'TCP'}, {'name': 'ext-svc-4549', \"\n", - " \"'containerPort': 4549, 'protocol': 'TCP'}, {'name': 'ext-svc-4550', \"\n", - " \"'containerPort': 4550, 'protocol': 'TCP'}, {'name': 'ext-svc-4551', \"\n", - " \"'containerPort': 4551, 'protocol': 'TCP'}, {'name': 'ext-svc-4552', \"\n", - " \"'containerPort': 4552, 'protocol': 'TCP'}, {'name': 'ext-svc-4553', \"\n", - " \"'containerPort': 4553, 'protocol': 'TCP'}, {'name': 'ext-svc-4554', \"\n", - " \"'containerPort': 4554, 'protocol': 'TCP'}, {'name': 'ext-svc-4555', \"\n", - " \"'containerPort': 4555, 'protocol': 'TCP'}, {'name': 'ext-svc-4556', \"\n", - " \"'containerPort': 4556, 'protocol': 'TCP'}, {'name': 'ext-svc-4557', \"\n", - " \"'containerPort': 4557, 'protocol': 'TCP'}, {'name': 'ext-svc-4558', \"\n", - " \"'containerPort': 4558, 'protocol': 'TCP'}, {'name': 'ext-svc-4559', \"\n", - " \"'containerPort': 4559, 'protocol': 'TCP'}], 'livenessProbe': \"\n", - " \"{'failureThreshold': 3, 'initialDelaySeconds': 0, 'periodSeconds': 10, \"\n", - " \"'successThreshold': 1, 'timeoutSeconds': 1, 'httpGet': {'path': \"\n", - " \"'/_localstack/health', 'port': 'edge'}}, 'readinessProbe': \"\n", - " \"{'failureThreshold': 3, 'initialDelaySeconds': 0, 'periodSeconds': 10, \"\n", - " \"'successThreshold': 1, 'timeoutSeconds': 1, 'httpGet': {'path': \"\n", - " \"'/_localstack/health', 'port': 'edge'}}, 'resources': {}, 'env': [{'name': \"\n", - " \"'DEBUG', 'value': '0'}, {'name': 'EXTERNAL_SERVICE_PORTS_START', 'value': \"\n", - " \"'4510'}, {'name': 'EXTERNAL_SERVICE_PORTS_END', 'value': '4560'}, {'name': \"\n", - " \"'LOCALSTACK_K8S_SERVICE_NAME', 'value': 'localstack'}, {'name': \"\n", - " \"'LOCALSTACK_K8S_NAMESPACE', 'valueFrom': {'fieldRef': {'fieldPath': \"\n", - " \"'metadata.namespace'}}}, {'name': 'LAMBDA_RUNTIME_EXECUTOR', 'value': \"\n", - " \"'docker'}, {'name': 'LAMBDA_K8S_IMAGE_PREFIX', 'value': \"\n", - " \"'localstack/lambda-'}, {'name': 'LAMBDA_RUNTIME_ENVIRONMENT_TIMEOUT', \"\n", - " \"'value': '60'}, {'name': 'OVERRIDE_IN_DOCKER', 'value': '1'}]}], 'volumes': \"\n", - " \"[]}}}}, {'apiVersion': 'v1', 'kind': 'ConfigMap', 'metadata': {'name': \"\n", - " \"'my-config'}, 'data': {'ENV_VAR1': 'value1', 'ENV_VAR2': 'value2'}}, \"\n", - " \"{'apiVersion': 'v1', 'kind': 'Secret', 'metadata': {'name': 'my-secret'}, \"\n", - " \"'type': 'Opaque', 'data': {'SECRET_KEY1': 'dmFsdWUx', 'SECRET_KEY2': \"\n", - " \"'dmFsdWUy'}}, {'apiVersion': 'v1', 'kind': 'Secret', 'metadata': {'name': \"\n", - " \"'aws-credentials-{{ spawner.user.name }}'}, 'type': 'Opaque', 'data': \"\n", - " \"{'credentials': \"\n", - " \"'W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg='}}, \"\n", - " \"{'apiVersion': 'external-secrets.io/v1beta1', 'kind': 'ExternalSecret', \"\n", - " \"'metadata': {'name': 'data-by-name', 'namespace': 'jupyter-{{ \"\n", - " \"spawner.user.name }}'}, 'spec': {'refreshInterval': '15s', 'secretStoreRef': \"\n", - " \"{'kind': 'ClusterSecretStore', 'name': 'k8s-secret-store'}, 'target': \"\n", - " \"{'name': 'data-by-name', 'creationPolicy': 'Owner'}, 'data': [{'secretKey': \"\n", - " \"'secret-value', 'remoteRef': {'key': 'secret-one', 'property': \"\n", - " \"'the-key'}}]}}, {'apiVersion': 'external-secrets.io/v1beta1', 'kind': \"\n", - " \"'ExternalSecret', 'metadata': {'name': 'eoepca-plus-secret-ro', 'namespace': \"\n", - " \"'jupyter-{{ spawner.user.name }}'}, 'spec': {'refreshInterval': '15s', \"\n", - " \"'secretStoreRef': {'kind': 'ClusterSecretStore', 'name': \"\n", - " \"'k8s-secret-store'}, 'target': {'name': 'eoepca-plus-secret-ro', \"\n", - " \"'creationPolicy': 'Owner'}, 'data': [{'secretKey': '.dockerconfigjson', \"\n", - " \"'remoteRef': {'key': 'eoepca-plus-secret-ro', 'property': \"\n", - " \"'.dockerconfigjson'}}]}}], persist=False)], env_from_config_maps=None, \"\n", - " 'env_from_secrets=None, secret_mounts=None), '\n", - " \"Profile(id='profile_studio_desktop_qgis', groups=['group-a', 'group-b'], \"\n", - " \"definition=ProfileDefinition(display_name='QGIS on a Remote Desktop', \"\n", - " \"description='Spatial visualization and decision-making tools for everyone', \"\n", - " \"slug='eoepca_desktop_qgis', default=False, \"\n", - " 'kubespawner_override=KubespawnerOverride(cpu_limit=2, cpu_guarantee=None, '\n", - " \"mem_limit='2G', mem_guarantee=None, \"\n", - " \"image='eoepca/iga-remote-desktop-qgis:1.1.3', extra_resource_limits={}, \"\n", - " \"extra_resource_guarantees={})), config_maps=[ConfigMap(name='bash-rc', \"\n", - " \"key='bash-rc', mount_path='/workspace/.bashrc', default_mode=None, \"\n", - " 'readonly=True, content=\\'alias ll=\"ls -l\"\\\\nalias '\n", - " 'calrissian=\"/opt/conda/bin/calrissian --pod-nodeselectors '\n", - " '/etc/calrissian/pod-node-selector.yml --stdout /calrissian/results.json '\n", - " '--max-ram 16G --max-cores \"8\" --tmp-outdir-prefix /calrissian/tmp/ --outdir '\n", - " '/calrissian/\"\\\\nalias cwltool=\"/opt/conda/bin/cwltool --podman\"\\\\n. '\n", - " '/home/jovyan/.bashrc\\\\n\\\\n#alias aws=\"aws '\n", - " '--endpoint-url=http://localstack:4566\"\\\\n\\\\n# >>> conda initialize >>>\\\\n# '\n", - " \"!! Contents within this block are managed by \\\\'conda init\\\\' \"\n", - " '!!\\\\n__conda_setup=\"$(\\\\\\'/opt/conda/bin/conda\\\\\\' \\\\\\'shell.bash\\\\\\' '\n", - " '\\\\\\'hook\\\\\\' 2> /dev/null)\"\\\\nif [ $? -eq 0 ]; then\\\\n eval '\n", - " '\"$__conda_setup\"\\\\nelse\\\\n if [ -f \"/opt/conda/etc/profile.d/conda.sh\" ]; '\n", - " 'then\\\\n . \"/opt/conda/etc/profile.d/conda.sh\"\\\\n else\\\\n '\n", - " 'export PATH=\"/srv/conda/bin:$PATH\"\\\\n fi\\\\nfi\\\\nunset '\n", - " '__conda_setup\\\\n\\\\nif [ -f \"/opt/conda/etc/profile.d/mamba.sh\" ]; then\\\\n '\n", - " '. \"/opt/conda/etc/profile.d/mamba.sh\"\\\\nfi\\\\n# <<< conda initialize '\n", - " '<<<\\\\n\\\\na={{spawner.user.name}}\\\\n\\\\nalias aws=\"aws '\n", - " '--endpoint-url=http://localstack-jupyter-{{spawner.user.name}}:4566\"\\', '\n", - " \"persist=False), ConfigMap(name='bash-login', key='bash-login', \"\n", - " \"mount_path='/workspace/.bash_login', default_mode=None, readonly=True, \"\n", - " \"content='source /workspace/.bashrc\\\\n', persist=False)], \"\n", - " \"volumes=[Volume(name='workspace-volume', claim_name='workspace-claim', \"\n", - " \"size='50Gi', storage_class='managed-nfs-storage', \"\n", - " \"access_modes=['ReadWriteOnce'], \"\n", - " \"volume_mount=VolumeMount(name='workspace-volume', mount_path='/workspace'), \"\n", - " \"persist=True)], pod_env_vars={'HOME': '/workspace'}, default_url='desktop', \"\n", - " 'node_selector={}, role_bindings=None, image_pull_secrets=[], '\n", - " 'init_containers=[], manifests=None, env_from_config_maps=None, '\n", - " 'env_from_secrets=None, secret_mounts=None)]')\n" - ] - } - ], - "source": [ - "pprint(profiles)\n", - "config = Config(profiles=profiles) # type: ignore\n", - "pprint(f'config: {config}')\n", - "eoepca_demo_config_path = str(Path(current_dir).parent.parent / 'eoepca-demo' / 'config.yml')\n", - "with open(eoepca_demo_config_path, \"w\") as file:\n", - " yaml.dump(config.model_dump(), file)" - ] - } - ], - "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.5" - }, - "orig_nbformat": 4 - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/apphub-configurator/examples/generate-config.sh b/apphub-configurator/examples/generate-config.sh deleted file mode 100755 index ecc0894..0000000 --- a/apphub-configurator/examples/generate-config.sh +++ /dev/null @@ -1,4 +0,0 @@ -set -x -export PYTHONPATH=$PWD/`dirname $0`/. - -python3 -m generate_config \ No newline at end of file diff --git a/apphub-configurator/examples/generate_config.py b/apphub-configurator/examples/generate_config.py deleted file mode 100644 index 73086fd..0000000 --- a/apphub-configurator/examples/generate_config.py +++ /dev/null @@ -1,168 +0,0 @@ -from apphub_configurator.models import * -import yaml -from pathlib import Path -import os -from loguru import logger -from apphub_configurator.helpers import ( - load_config_map, - load_manifests, - create_init_container, - load_init_script, -) -import click - -logger.info("Generating config file...") - -storage_class_rwo = "standard" -storage_class_rwx = "standard" -profiles = [] -workspace_volume_size = "50Gi" -calrissian_volume_size = "50Gi" -image = "ghcr.io/eoepca/pde-code-server:latest-dev" -node_selector = {} - -# get the current directory -current_dir = Path(os.path.dirname(os.path.realpath(__file__))) -parent_dir = current_dir.parent - -# load the manifests -localstack_manifest_path = os.path.join(parent_dir, "manifests/manifest.yaml") -# localstack_manifest -localstack_manifest = load_manifests( - name="localstack", - key="localstack", - file_path=localstack_manifest_path, -) - -dask_gateway_manifest_path = os.path.join(parent_dir, "manifests/dask-gateway.yaml") -# Dask Gateway manifest -dask_gateway_manifest = load_manifests( - name="dask-gateway", - key="dask-gateway", - file_path=dask_gateway_manifest_path, -) -kaniko_manifest_path = os.path.join(parent_dir, "manifests/kaniko.yaml") -kaniko_manifest = load_manifests( - name="kaniko", - key="kaniko", - file_path=kaniko_manifest_path, -) -# 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_file_path = os.path.join(parent_dir, "config-maps/bash-login") -bash_login_cm = load_config_map( - name="bash-login", - key="bash-login", - file_name=bash_login_file_path, - mount_path="/etc/profile.d/bash-login.sh", -) - -bash_rc_cm_file_path = os.path.join(parent_dir, "config-maps/bash-rc") -bash_rc_cm = load_config_map( - name="bash-rc", - key="bash-rc", - file_name=bash_rc_cm_file_path, - mount_path="/workspace/.bashrc", -) -init_cm_file_path = os.path.join(parent_dir, "config-maps/init.sh") -init_cm = load_init_script(init_cm_file_path) - -init_container = create_init_container( - image=image, - volume=workspace_volume, - mount_path="/workspace", -) - -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, kaniko_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) -config_file_path = str(Path(current_dir).parent.parent / 'files' / 'hub' / 'config.yml') - - -with open(config_file_path, "w") as file: - yaml.dump(config.dict(), file, width=200) - -logger.success( - f"Config file generated successfully at {config_file_path}" -) \ No newline at end of file diff --git a/apphub-configurator/manifests/dask-gateway.yaml b/apphub-configurator/manifests/dask-gateway.yaml deleted file mode 100644 index 53c8697..0000000 --- a/apphub-configurator/manifests/dask-gateway.yaml +++ /dev/null @@ -1,109 +0,0 @@ -apiVersion: helm.crossplane.io/v1beta1 -kind: Release -metadata: - name: dask-gw-jupyter-{{ spawner.user.name }} - namespace: "{{ namespace }}" -spec: - forProvider: - chart: - name: dask-gateway - version: "2024.1.0" - repository: https://helm.dask.org - namespace: "{{ namespace }}" - 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.{namespace}-{{ 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.{namespace}-{{ 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/apphub-configurator/manifests/kaniko.yaml b/apphub-configurator/manifests/kaniko.yaml deleted file mode 100644 index f86df21..0000000 --- a/apphub-configurator/manifests/kaniko.yaml +++ /dev/null @@ -1,58 +0,0 @@ - apiVersion: v1 - kind: Pod - metadata: - name: kaniko-build - namespace: "{{ namespace }}" - 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: "{{ namespace }}" -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: "{{ namespace }}" -subjects: - - kind: ServiceAccount - name: default - namespace: "{{ namespace }}" -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: pod-exec diff --git a/apphub-configurator/manifests/manifest.yaml b/apphub-configurator/manifests/manifest.yaml deleted file mode 100644 index 0e1b171..0000000 --- a/apphub-configurator/manifests/manifest.yaml +++ /dev/null @@ -1,505 +0,0 @@ -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: localstack-{{ spawner.user.name }} - 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: [] ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: my-config -data: - ENV_VAR1: value1 - 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" ---- -apiVersion: v1 -kind: Secret -metadata: - name: aws-credentials-{{ spawner.user.name }} -type: Opaque -data: - credentials: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg= -# --- -# apiVersion: helm.crossplane.io/v1beta1 -# kind: Release -# metadata: -# name: wordpress-{namespace}-{{ spawner.user.name }} -# namespace: {namespace}-{{ spawner.user.name }} -# spec: -# forProvider: -# chart: -# name: wordpress -# version: "24.1.4" -# repository: oci://registry-1.docker.io/bitnamicharts/ -# namespace: {namespace}-{{ spawner.user.name }} -# values: -# service: -# type: ClusterIP -# providerConfigRef: -# name: helm-provider ---- -apiVersion: external-secrets.io/v1beta1 -kind: ExternalSecret -metadata: - name: data-by-name - namespace: "{{ namespace }}" -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: "{{ namespace }}" -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 diff --git a/apphub-configurator/pyproject.toml b/apphub-configurator/pyproject.toml deleted file mode 100644 index 123a89f..0000000 --- a/apphub-configurator/pyproject.toml +++ /dev/null @@ -1,77 +0,0 @@ -[build-system] -requires = ["hatchling"] -build-backend = "hatchling.build" - -[project] -name = "apphub-configurator" -dynamic = ["version"] -description = "apphub-configurator" -readme = "../docs/README.md" -requires-python = ">=3.10" -license = "Apache-2.0" -keywords = ["kubernetes", "configuration", "EOEPCA"] -authors = [ - { name = "Terradue", email = "info@terradue.com" } -] -classifiers = [ - "Development Status :: 4 - Beta", - "Programming Language :: Python", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", -] - -dependencies = [] - -[project.urls] -Documentation = "https://github.com/EOEPCA/application-hub-context/apphub-configurator/apphub-configurator#readme" -Issues = "https://github.com/EOEPCA/application-hub-context/issues" -Source = "https://github.com/EOEPCA/application-hub-context/apphub-configurator/apphub-configurator" -Examples = "https://github.com/EOEPCA/application-hub-context/apphub-configurator/examples" - -[tool.hatch.version] -path = "src/apphub_configurator/__about__.py" - -[tool.hatch.metadata] -allow-direct-references = true - -[tool.hatch.build] -packages = ["src/apphub_configurator"] -examples = ["examples"] - - -[tool.hatch.envs.default] -path = "hatch/envs/apphub_configurator" -[tool.hatch.envs.prod] -path = "hatch/envs/apphub_configurator" -dependencies = ["pytest", - "pytest-cov", - "pydantic<2.0.0", - "pyyaml", - "ipykernel", - "jupyter", - "loguru", - "click", - ] - -[[tool.hatch.envs.test.matrix]] -python = ["3.10", "3.11", "3.12"] - -# Coverage settings -[tool.coverage.run] -source_pkgs = ["apphub_configurator"] -branch = true -parallel = true -omit = [ - "src/apphub_configurator/__about__.py", -] - -[tool.coverage.paths] -apphub_configurator = ["src/apphub_configurator", "*/apphub_configurator/src/apphub_configurator"] - -[tool.coverage.report] -exclude_lines = [ - "no cov", - "if __name__ == .__main__.:", - "if TYPE_CHECKING:", -] diff --git a/apphub-configurator/src/apphub_configurator/__about__.py b/apphub-configurator/src/apphub_configurator/__about__.py deleted file mode 100644 index 9d35080..0000000 --- a/apphub-configurator/src/apphub_configurator/__about__.py +++ /dev/null @@ -1,4 +0,0 @@ -# SPDX-FileCopyrightText: 2025-present pmembari -# -# SPDX-License-Identifier: MIT -__version__ = "0.1.0" diff --git a/apphub-configurator/src/apphub_configurator/__init__.py b/apphub-configurator/src/apphub_configurator/__init__.py deleted file mode 100644 index ba59794..0000000 --- a/apphub-configurator/src/apphub_configurator/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-FileCopyrightText: 2025-present pmembari -# -# SPDX-License-Identifier: MIT diff --git a/apphub-configurator/src/apphub_configurator/helpers.py b/apphub-configurator/src/apphub_configurator/helpers.py deleted file mode 100644 index 765a421..0000000 --- a/apphub-configurator/src/apphub_configurator/helpers.py +++ /dev/null @@ -1,61 +0,0 @@ -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", - ) diff --git a/apphub-configurator/src/apphub_configurator/models.py b/apphub-configurator/src/apphub_configurator/models.py deleted file mode 100644 index 7f7aa8f..0000000 --- a/apphub-configurator/src/apphub_configurator/models.py +++ /dev/null @@ -1,157 +0,0 @@ -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 - annotations: Optional[Dict[str, str]] = None - -class Manifest(BaseModel): - name: str - key: str - content: Optional[List[Dict]] = 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 SecretMount(BaseModel): - name: str - mount_path: str - sub_path: 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 - 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""" - - profiles: List[Profile] diff --git a/apphub-configurator/tests/__init__.py b/apphub-configurator/tests/__init__.py deleted file mode 100644 index ba59794..0000000 --- a/apphub-configurator/tests/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# SPDX-FileCopyrightText: 2025-present pmembari -# -# SPDX-License-Identifier: MIT diff --git a/application_hub_context/app_hub_context.py b/application_hub_context/app_hub_context.py index e60c92e..025d3ee 100644 --- a/application_hub_context/app_hub_context.py +++ b/application_hub_context/app_hub_context.py @@ -377,7 +377,7 @@ def delete_role_binding(self, role_binding: RoleBinding): f"Exception deleting role {role_binding.role.name}: {e}\n" ) - def create_role_binding(self, name: str, subjects: [Subject], role: Role): + def create_role_binding(self, name: str, subjects: list[Subject], role: Role): if self.is_role_binding_created(name=name): return self.rbac_authorization_v1_api.read_namespaced_role_binding( name=name, namespace=self.namespace @@ -385,16 +385,15 @@ def create_role_binding(self, name: str, subjects: [Subject], role: Role): metadata = client.V1ObjectMeta(name=name, namespace=self.namespace) - role_ref = client.V1RoleRef(api_group="", kind="Role", name=role.name) + role_ref = client.V1RoleRef(api_group="rbac.authorization.k8s.io", kind="Role", name=role.name) subject_list = [] for subject in subjects: - subject = client.models.V1Subject( - api_group="", - kind=subject.kind.value, - name=subject.name, - namespace=self.namespace, - ) + subject = { + "kind": subject.kind.value, + "name": subject.name, + "namespace": self.namespace, + } subject_list.append(subject) body = client.V1RoleBinding( @@ -1024,7 +1023,7 @@ def initialise(self): ) self.spawner.log.info(f"env_from_secrets {env_from_secrets}") if env_from_secrets: - if self.spawner.extra_container_config["env_from"] is None: + if self.spawner.extra_container_config.get("env_from") is None: self.spawner.extra_container_config["env_from"] = [] for env_from_secret in env_from_secrets: diff --git a/application_hub_context/parser.py b/application_hub_context/parser.py index 64e37c9..0f63dac 100644 --- a/application_hub_context/parser.py +++ b/application_hub_context/parser.py @@ -11,26 +11,47 @@ def __init__(self, config_data, user_groups): self.config = Config(**config_data) self.user_groups = user_groups + @classmethod + def _render_templates(cls, obj, context): + """Recursively render Jinja templates in strings only.""" + if isinstance(obj, str): + if "{{" in obj: + return Template(obj).render(**context) + return obj + + if isinstance(obj, list): + return [cls._render_templates(i, context) for i in obj] + + if isinstance(obj, dict): + return { + k: cls._render_templates(v, context) + for k, v in obj.items() + } + + return obj + @classmethod def read_file(cls, config_path, user_groups, spawner, namespace): - """reads a config file encoded in YAML""" with open(config_path, "r") as stream: - try: - # 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, - namespace=namespace) - - # Parse the rendered content as YAML - config_data = yaml.safe_load(rendered_content) - - except yaml.YAMLError as exc: - print(f"YAML Error: {exc}") - except Exception as e: - print(f"Error: {e}") + raw_content = stream.read() + + try: + # Parse YAML first (SAFE) + config_data = yaml.safe_load(raw_content) + + # Render templates selectively + context = { + "spawner": spawner, + "namespace": namespace, + } + config_data = cls._render_templates(config_data, context) + + except yaml.YAMLError as exc: + raise RuntimeError(f"Invalid YAML in {config_path}: {exc}") from exc + except Exception as exc: + raise RuntimeError( + f"Error processing config file {config_path}: {exc}" + ) from exc return cls(config_data=config_data, user_groups=user_groups) diff --git a/custom-config.yml b/custom-config.yml index 39c08ad..eaba7ee 100644 --- a/custom-config.yml +++ b/custom-config.yml @@ -1,75 +1,37 @@ profiles: - 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 - - ' + - content: source /workspace/.bashrc default_mode: '0660' - key: init - mount_path: /opt/init/.init.sh - 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 + mount_path: /workspace/.bash_login name: bash-login - persist: true + 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\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\"\n\nexport PATH=/workspace/.local/bin:$PATH" - default_mode: null + - content: "alias ll=\"ls -l\"\n\n. /home/jovyan/.bashrc\n\n# Cache Git HTTPS credentials in memory (8 hours)\ngit config --global credential.helper 'cache --timeout=28800'\n\nalias aws=\"aws --endpoint-url=http://localstack-jupyter-alice:4566\"" + default_mode: '0660' key: bash-rc mount_path: /workspace/.bashrc name: bash-rc - persist: true + persist: false + readonly: true + - content: echo "Initializing environment..." + default_mode: '0660' + key: init + mount_path: /opt/init/.init.sh + name: init + persist: false + readonly: true + - content: "#!/bin/bash\n\n# Function to display help\nshow_help () {\n echo \"Usage: $0 [options]\"\n echo -e \"\\nThis utility copies your local image pull secrets to your kubernetes namespace\\n\"\n echo \"Options:\"\n echo \" --help Display this help message\"\n exit 1\n}\n\n# Check for the --help option\n[ \"$1\" == \"--help\" ] && show_help\n\n# Check if kubectl is installed\n[ ! command -v kubectl &> /dev/null ] && {\n echo \"kubectl not found. Please install kubectl.\"\n exit 1\n}\n\n# Function to check if a file exists\nfile_exists () {\n if [ ! -f \"$1\" ]; then\n echo \"$1 not found\"\n exit 1\n fi\n}\n\n# Check if the XDG_RUNTIME_DIR environment variable is set\nif [ -z \"$XDG_RUNTIME_DIR\" ]; then\n echo \"XDG_RUNTIME_DIR environment variable is not set\"\n exit 1\nfi\n\nAUTH_JSON_PATH=\"$XDG_RUNTIME_DIR/containers/auth.json\"\n\nfile_exists ${AUTH_JSON_PATH}\n\nSECRET_NAME=\"incluster-cr-secret\"\n\nkubectl delete secret \"${SECRET_NAME}\" &> /dev/null\n\nkubectl create secret generic \"${SECRET_NAME}\" --from-file=.dockerconfigjson=${AUTH_JSON_PATH} --type=kubernetes.io/dockerconfigjson &> /dev/null\n\nres=$?\n\n[ ${res} == 0 ] && { \n echo \"Image pull secrets updated\" \n} || { \n echo \"\"\n exit 1 \n}" + default_mode: '755' + key: copy-secrets + mount_path: /usr/bin/copy-secrets + name: copy-secrets + 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 profile + description: Code Server for development + display_name: Code Server kubespawner_override: cpu_guarantee: 1 cpu_limit: 2 @@ -78,19 +40,19 @@ profiles: image: ghcr.io/eoepca/pde-code-server:latest-dev mem_guarantee: 4G mem_limit: 6G - slug: profile_1 - env_from_config_maps: - - my-config + slug: coder_app + env_from_config_maps: [] env_from_secrets: - - my-secret + - localstack-s3-secret groups: - group-a - group-b - id: profile_1 + - group-c + id: profile_coder_app image_pull_secrets: - - data: null - name: eoepca-plus-secret-ro - persist: true + - data: ewoJImF1dGhzIjogewoJCSJjci50ZXJyYWR1ZS5jb20iOiB7CgkJCSJhdXRoIjogIlptSnlhWFJ2T21ZNVZFNUNaVTlIVEE9PSIKCQl9Cgl9Cn0= + name: incluster-cr-secret + persist: false init_containers: - command: - sh @@ -99,6 +61,8 @@ profiles: image: ghcr.io/eoepca/pde-code-server:latest-dev name: init-file-on-volume volume_mounts: + - mount_path: /calrissian + name: calrissian-volume - mount_path: /workspace name: workspace-volume - mount_path: /opt/init/.init.sh @@ -106,497 +70,558 @@ profiles: 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 + - apiVersion: helm.crossplane.io/v1beta1 + kind: Release metadata: - name: localstack + name: localstack-jupyter-{{ spawner.user.name }} + namespace: '{{ namespace }}' 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 + forProvider: + chart: + name: localstack + repository: https://localstack.github.io/helm-charts + version: 0.6.20 + namespace: '{{ namespace }}' + skipCreateNamespace: true + values: + service: + type: ClusterIP + serviceAccount: + create: true + providerConfigRef: + name: helm-provider - apiVersion: v1 data: - credentials: W2RlZmF1bHRdCmF3c19hY2Nlc3Nfa2V5X2lkPUFTSUFJT1NGT0ROTjdFWEFNUExFCmF3c19zZWNyZXRfYWNjZXNzX2tleT13SmFsclhVdG5GRU1JL0s3TURFTkcvYlB4UmZpQ1lFWEFNUExFS0VZCmF3c19zZXNzaW9uX3Rva2VuPUlRb0piM2pySmdCV05FTE5Hb2xHSkxFT3RTVEFOR1k0TFlPNUk0SzVOUlZFS1pPTkNTTk1HRlNUS1FNSUxXUjJPUzAwRklDRTExSlg= + AWS_ACCESS_KEY_ID: dGVzdAo= + AWS_DEFAULT_REGION: dXMtZWFzdC0xCg== + AWS_SECRET_ACCESS_KEY: dGVzdAo= kind: Secret metadata: - name: aws-credentials-{{ spawner.user.name }} - type: Opaque - - apiVersion: external-secrets.io/v1beta1 - kind: ExternalSecret - metadata: - name: data-by-name - namespace: '{{ namespace }}' - 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 + name: localstack-s3-secret namespace: '{{ namespace }}' - 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: localstack - name: localstack + type: Opaque + key: local-stack + name: local-stack + persist: false + node_selector: {} + pod_env_vars: + CWLTOOL_OPTIONS: --podman + HOME: /workspace + NAMESPACE: '{{ namespace }}' + 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 + - get + subjects: + - kind: ServiceAccount + name: default + - name: pod-exec-role-binding + persist: false + role: + api_groups: + - '' + name: pod-exec-role + resources: + - pods/exec + verbs: + - create + 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 + - 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 + secret_mounts: [] + volumes: + - access_modes: + - ReadWriteMany + claim_name: calrissian-claim + name: calrissian-volume + persist: false + size: 25Gi + storage_class: standard + volume_mount: + mount_path: /calrissian + name: calrissian-volume + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: true + size: 10Gi + storage_class: standard + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: + - content: source /workspace/.bashrc + default_mode: '0660' + key: bash-login + mount_path: /workspace/.bash_login + name: bash-login + persist: false + readonly: true + - content: "alias ll=\"ls -l\"\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 <<<" + default_mode: '0660' + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: false + readonly: true + - content: echo "Initializing environment..." + default_mode: '0660' + key: init + mount_path: /opt/init/.init.sh + name: init + persist: false + readonly: true + - content: "#!/bin/bash\n\n# Function to display help\nshow_help () {\n echo \"Usage: $0 [options]\"\n echo -e \"\\nThis utility copies your local image pull secrets to your kubernetes namespace\\n\"\n echo \"Options:\"\n echo \" --help Display this help message\"\n exit 1\n}\n\n# Check for the --help option\n[ \"$1\" == \"--help\" ] && show_help\n\n# Check if kubectl is installed\n[ ! command -v kubectl &> /dev/null ] && {\n echo \"kubectl not found. Please install kubectl.\"\n exit 1\n}\n\n# Function to check if a file exists\nfile_exists () {\n if [ ! -f \"$1\" ]; then\n echo \"$1 not found\"\n exit 1\n fi\n}\n\n# Check if the XDG_RUNTIME_DIR environment variable is set\nif [ -z \"$XDG_RUNTIME_DIR\" ]; then\n echo \"XDG_RUNTIME_DIR environment variable is not set\"\n exit 1\nfi\n\nAUTH_JSON_PATH=\"$XDG_RUNTIME_DIR/containers/auth.json\"\n\nfile_exists ${AUTH_JSON_PATH}\n\nSECRET_NAME=\"incluster-cr-secret\"\n\nkubectl delete secret \"${SECRET_NAME}\" &> /dev/null\n\nkubectl create secret generic \"${SECRET_NAME}\" --from-file=.dockerconfigjson=${AUTH_JSON_PATH} --type=kubernetes.io/dockerconfigjson &> /dev/null\n\nres=$?\n\n[ ${res} == 0 ] && { \n echo \"Image pull secrets updated\" \n} || { \n echo \"\"\n exit 1 \n}" + default_mode: '755' + key: copy-secrets + mount_path: /usr/bin/copy-secrets + name: copy-secrets + persist: false + readonly: true + definition: + default: false + description: Code Server with GPU acceleration + display_name: GPU Code Server + kubespawner_override: + cpu_guarantee: 4 + cpu_limit: 8 + extra_resource_guarantees: {} + extra_resource_limits: + nvidia.com/gpu: 1 + image: ghcr.io/eoepca/pde-code-server:latest-dev + mem_guarantee: 16G + mem_limit: 32G + slug: gpu_coder_app + env_from_config_maps: [] + env_from_secrets: [] + groups: + - group-a + - group-b + - group-c + id: profile_gpu_coder_app + image_pull_secrets: + - data: ewoJImF1dGhzIjogewoJCSJjci50ZXJyYWR1ZS5jb20iOiB7CgkJCSJhdXRoIjogIlptSnlhWFJ2T21ZNVZFNUNaVTlIVEE9PSIKCQl9Cgl9Cn0= + name: incluster-cr-secret + persist: false + init_containers: + - command: + - sh + - -c + - sh /opt/init/.init.sh + image: ghcr.io/eoepca/pde-code-server:latest-dev + name: init-file-on-volume + volume_mounts: + - mount_path: /calrissian + name: calrissian-volume + - mount_path: /workspace + name: workspace-volume + - mount_path: /opt/init/.init.sh + name: init + sub_path: init + manifests: [] + node_selector: + nvidia.com/gpu.present: 'true' + pod_env_vars: + CWLTOOL_OPTIONS: --podman + HOME: /workspace + NAMESPACE: '{{ namespace }}' + 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 + - get + subjects: + - kind: ServiceAccount + name: default + - name: pod-exec-role-binding + persist: false + role: + api_groups: + - '' + name: pod-exec-role + resources: + - pods/exec + verbs: + - create + 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 + - 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 + secret_mounts: [] + 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: 20Gi + storage_class: standard + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: + - content: "source /workspace/.bashrc\n\n\naaaa" + default_mode: '0660' + key: bash-login + mount_path: /workspace/.bash_login + name: bash-login + persist: false + readonly: true + - content: "alias ll=\"ls -l\"\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 <<<" + default_mode: '0660' + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: false + readonly: true + - content: echo "Initializing remote desktop environment..." + default_mode: '0660' + key: init + mount_path: /opt/init/.init.sh + name: init + persist: false + readonly: true + default_url: /desktop + definition: + default: false + description: Linux desktop environment via web browser (noVNC) + display_name: Remote Desktop + kubespawner_override: + cpu_guarantee: 1 + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: ghcr.io/eoepca/iga-remote-desktop:1.2.0 + mem_guarantee: 4G + mem_limit: 6G + slug: remote_desktop + env_from_config_maps: [] + env_from_secrets: [] + groups: + - group-a + - group-b + - group-c + id: profile_remote_desktop + image_pull_secrets: [] + init_containers: + - command: + - sh + - -c + - sh /opt/init/.init.sh + image: ghcr.io/eoepca/iga-remote-desktop:1.2.0 + 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: [] + node_selector: {} + pod_env_vars: + 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: [] + secret_mounts: [] + volumes: + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: true + size: 20Gi + storage_class: standard + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: + - content: "source /workspace/.bashrc\n\n\naaaa" + default_mode: '0660' + key: bash-login + mount_path: /workspace/.bash_login + name: bash-login + persist: false + readonly: true + - content: "alias ll=\"ls -l\"\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 <<<" + default_mode: '0660' + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: false + readonly: true + - content: echo "Initializing remote desktop environment..." + default_mode: '0660' + key: init + mount_path: /opt/init/.init.sh + name: init + persist: false + readonly: true + default_url: /desktop + definition: + default: false + description: Linux desktop environment with QGIS via web browser + display_name: QGIS Remote Desktop + kubespawner_override: + cpu_guarantee: 1 + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: ghcr.io/eoepca/iga-remote-desktop-qgis:latest + mem_guarantee: 4G + mem_limit: 6G + slug: qgis_remote_desktop + env_from_config_maps: [] + env_from_secrets: [] + groups: + - group-a + - group-b + - group-c + id: profile_qgis_remote_desktop + image_pull_secrets: [] + init_containers: + - command: + - sh + - -c + - sh /opt/init/.init.sh + image: ghcr.io/eoepca/iga-remote-desktop-qgis:latest + 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: [] + node_selector: {} + pod_env_vars: + 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: [] + secret_mounts: [] + volumes: + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: true + size: 20Gi + storage_class: standard + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: + - content: source /workspace/.bashrc + default_mode: '0660' + key: bash-login + mount_path: /workspace/.bash_login + name: bash-login + persist: false + readonly: true + - content: "alias ll=\"ls -l\"\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 <<<" + default_mode: '0660' + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: false + readonly: true + - content: "echo \"Initializing environment...\"\n\ncd /workspace\n\ngit clone 'https://github.com/eoap/dask-app-package.git'\n\ncode-server --install-extension ms-python.python --install-extension redhat.vscode-yaml --install-extension sbg-rabix.benten-cwl --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\n# eopf-sentinel-2 environment\npython -m venv /workspace/.venv-eopf\nsource /workspace/.venv-eopf/bin/activate\n/workspace/.venv-eopf/bin/pip install --upgrade uv\n/workspace/.venv-eopf/bin/python -m uv pip install numpy==1.26.3 dask==2025.4.0 distributed==2025.4.0 dask-gateway==2025.4.0 pystac bokeh rioxarray==0.18.1 loguru==0.7.3 odc-stac[botocore]==0.3.10 stackstac==0.5.1 pystac-client==0.8.6 planetary-computer==1.0.0 xarray-spatial==0.4.0 \"xarray[complete]==2025.4.0\" ipykernel rio-color==2.0.1\nres=$?\nif [ $res -ne 0 ]; then\n echo \"Failed to install packages\"\n exit $res\nfi\n\ncd /workspace/dask-app-package/eopf-sentinel-2\n/workspace/.venv-eopf/bin/python -m uv pip install -e .\n\n/workspace/.venv-eopf/bin/python -m ipykernel install --user --name dask-gateway-eopf --display-name \"Python (EOPF Dask Application Package)\"" + default_mode: '0660' + key: init + mount_path: /opt/init/.init.sh + name: init + persist: false + readonly: true + - content: "#!/bin/bash\n\n# Function to display help\nshow_help () {\n echo \"Usage: $0 [options]\"\n echo -e \"\\nThis utility copies your local image pull secrets to your kubernetes namespace\\n\"\n echo \"Options:\"\n echo \" --help Display this help message\"\n exit 1\n}\n\n# Check for the --help option\n[ \"$1\" == \"--help\" ] && show_help\n\n# Check if kubectl is installed\n[ ! command -v kubectl &> /dev/null ] && {\n echo \"kubectl not found. Please install kubectl.\"\n exit 1\n}\n\n# Function to check if a file exists\nfile_exists () {\n if [ ! -f \"$1\" ]; then\n echo \"$1 not found\"\n exit 1\n fi\n}\n\n# Check if the XDG_RUNTIME_DIR environment variable is set\nif [ -z \"$XDG_RUNTIME_DIR\" ]; then\n echo \"XDG_RUNTIME_DIR environment variable is not set\"\n exit 1\nfi\n\nAUTH_JSON_PATH=\"$XDG_RUNTIME_DIR/containers/auth.json\"\n\nfile_exists ${AUTH_JSON_PATH}\n\nSECRET_NAME=\"incluster-cr-secret\"\n\nkubectl delete secret \"${SECRET_NAME}\" &> /dev/null\n\nkubectl create secret generic \"${SECRET_NAME}\" --from-file=.dockerconfigjson=${AUTH_JSON_PATH} --type=kubernetes.io/dockerconfigjson &> /dev/null\n\nres=$?\n\n[ ${res} == 0 ] && { \n echo \"Image pull secrets updated\" \n} || { \n echo \"\"\n exit 1 \n}" + default_mode: '755' + key: copy-secrets + mount_path: /usr/bin/copy-secrets + name: copy-secrets + persist: false + readonly: true + default_url: /workspace/dask-app-package + definition: + default: false + description: Code Server with Dask Gateway for distributed computing + display_name: Code Server with Dask Gateway + kubespawner_override: + cpu_guarantee: 1 + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: ghcr.io/eoepca/pde-code-server:latest-dev + mem_guarantee: 4G + mem_limit: 6G + slug: coder_dask_gateway_app + env_from_config_maps: [] + env_from_secrets: [] + groups: + - group-a + - group-b + - group-c + id: profile_coder_dask_gateway_app + image_pull_secrets: + - data: ewoJImF1dGhzIjogewoJCSJjci50ZXJyYWR1ZS5jb20iOiB7CgkJCSJhdXRoIjogIlptSnlhWFJ2T21ZNVZFNUNaVTlIVEE9PSIKCQl9Cgl9Cn0= + name: incluster-cr-secret + persist: false + init_containers: + - command: + - sh + - -c + - sh /opt/init/.init.sh + image: ghcr.io/eoepca/pde-code-server:latest-dev + name: init-file-on-volume + volume_mounts: + - mount_path: /calrissian + name: calrissian-volume + - mount_path: /workspace + name: workspace-volume + - mount_path: /opt/init/.init.sh + name: init + sub_path: init + manifests: - content: - apiVersion: helm.crossplane.io/v1beta1 kind: Release @@ -617,9 +642,7 @@ profiles: 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" + 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 @@ -627,15 +650,13 @@ profiles: name: helm-provider - apiVersion: v1 data: - gateway: "gateway:\n address: http://traefik-dask-gw-jupyter-{{ spawner.user.name }}-dask-gateway.{namespace}-{{ 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" + gateway: "gateway:\n address: http://traefik-dask-gw-jupyter-{{ spawner.user.name }}-dask-gateway.{{ namespace }}.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.{namespace}-{{ 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" + gateway: "gateway:\n address: http://traefik-dask-gw-jupyter-{{ spawner.user.name }}-dask-gateway.{{ namespace }}-{{ 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 @@ -681,98 +702,293 @@ profiles: key: dask-gateway name: dask-gateway persist: false - - content: - - apiVersion: v1 - kind: Pod - metadata: - name: kaniko-build - namespace: '{{ namespace }}' - 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: '{{ namespace }}' - 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: '{{ namespace }}' - roleRef: - apiGroup: rbac.authorization.k8s.io - kind: Role - name: pod-exec - subjects: - - kind: ServiceAccount - name: default - namespace: '{{ namespace }}' - key: kaniko - name: kaniko + node_selector: {} + pod_env_vars: + CODE_SERVER_WS: /workspace/dask-app-package + CWLTOOL_OPTIONS: --podman + DASK_GATEWAY_ADDRESS: http://traefik-dask-gw-jupyter-{{ spawner.user.name }}-dask-gateway.{{ namespace }}.svc.cluster.local:80 + HOME: /workspace + NAMESPACE: '{{ namespace }}' + 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 + - get + subjects: + - kind: ServiceAccount + name: default + - name: pod-exec-role-binding persist: false + role: + api_groups: + - '' + name: pod-exec-role + resources: + - pods/exec + verbs: + - create + 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 + - 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 + secret_mounts: [] + volumes: + - access_modes: + - ReadWriteMany + claim_name: calrissian-claim + name: calrissian-volume + persist: false + size: 30Gi + storage_class: standard + volume_mount: + mount_path: /calrissian + name: calrissian-volume + - access_modes: + - ReadWriteOnce + claim_name: workspace-claim + name: workspace-volume + persist: true + size: 15Gi + storage_class: standard + volume_mount: + mount_path: /workspace + name: workspace-volume +- config_maps: + - content: source /workspace/.bashrc + default_mode: '0660' + key: bash-login + mount_path: /workspace/.bash_login + name: bash-login + persist: false + readonly: true + - content: "alias ll=\"ls -l\"\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 <<<" + default_mode: '0660' + key: bash-rc + mount_path: /workspace/.bashrc + name: bash-rc + persist: false + readonly: true + - content: "echo \"Initializing environment...\"\n\ncd /workspace\n\ngit clone 'https://github.com/eoap/how-to.git'\n\ncode-server --install-extension ms-python.python --install-extension redhat.vscode-yaml --install-extension sbg-rabix.benten-cwl --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 calrissian tomlq\n\n/workspace/.venv/bin/python -m ipykernel install --user --name cwl_how_to_env --display-name \"Python (CWL How-To's)\"" + default_mode: '0660' + key: init + mount_path: /opt/init/.init.sh + name: init + persist: false + readonly: true + - content: "#!/bin/bash\n\n# Function to display help\nshow_help () {\n echo \"Usage: $0 [options]\"\n echo -e \"\\nThis utility copies your local image pull secrets to your kubernetes namespace\\n\"\n echo \"Options:\"\n echo \" --help Display this help message\"\n exit 1\n}\n\n# Check for the --help option\n[ \"$1\" == \"--help\" ] && show_help\n\n# Check if kubectl is installed\n[ ! command -v kubectl &> /dev/null ] && {\n echo \"kubectl not found. Please install kubectl.\"\n exit 1\n}\n\n# Function to check if a file exists\nfile_exists () {\n if [ ! -f \"$1\" ]; then\n echo \"$1 not found\"\n exit 1\n fi\n}\n\n# Check if the XDG_RUNTIME_DIR environment variable is set\nif [ -z \"$XDG_RUNTIME_DIR\" ]; then\n echo \"XDG_RUNTIME_DIR environment variable is not set\"\n exit 1\nfi\n\nAUTH_JSON_PATH=\"$XDG_RUNTIME_DIR/containers/auth.json\"\n\nfile_exists ${AUTH_JSON_PATH}\n\nSECRET_NAME=\"incluster-cr-secret\"\n\nkubectl delete secret \"${SECRET_NAME}\" &> /dev/null\n\nkubectl create secret generic \"${SECRET_NAME}\" --from-file=.dockerconfigjson=${AUTH_JSON_PATH} --type=kubernetes.io/dockerconfigjson &> /dev/null\n\nres=$?\n\n[ ${res} == 0 ] && { \n echo \"Image pull secrets updated\" \n} || { \n echo \"\"\n exit 1 \n}" + default_mode: '755' + key: copy-secrets + mount_path: /usr/bin/copy-secrets + name: copy-secrets + persist: false + readonly: true + default_url: /workspace/how-to + definition: + default: false + description: Code Server configured for running the How-To's of the Application Packages using CWL + display_name: Application Package CWL How-To's + kubespawner_override: + cpu_guarantee: 1 + cpu_limit: 2 + extra_resource_guarantees: {} + extra_resource_limits: {} + image: ghcr.io/eoepca/pde-code-server:latest-dev + mem_guarantee: 4G + mem_limit: 6G + slug: training_how_to_app + env_from_config_maps: [] + env_from_secrets: [] + groups: + - group-a + - group-b + - group-c + id: profile_training_how_to_app + image_pull_secrets: + - data: ewoJImF1dGhzIjogewoJCSJjci50ZXJyYWR1ZS5jb20iOiB7CgkJCSJhdXRoIjogIlptSnlhWFJ2T21ZNVZFNUNaVTlIVEE9PSIKCQl9Cgl9Cn0= + name: incluster-cr-secret + persist: false + init_containers: + - command: + - sh + - -c + - sh /opt/init/.init.sh + image: ghcr.io/eoepca/pde-code-server:latest-dev + name: init-file-on-volume + volume_mounts: + - mount_path: /calrissian + name: calrissian-volume + - mount_path: /workspace + name: workspace-volume + - mount_path: /opt/init/.init.sh + name: init + sub_path: init + manifests: [] 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 + CODE_SERVER_WS: /workspace/how-to + CWLTOOL_OPTIONS: --podman HOME: /workspace + NAMESPACE: '{{ namespace }}' + XDG_CONFIG_HOME: /workspace/.local + XDG_DATA_HOME: /workspace/.local/share/ XDG_RUNTIME_DIR: /workspace/.local - role_bindings: null - secret_mounts: - - mount_path: /workspace/.aws - name: aws-credentials-{{ spawner.user.name }} - sub_path: 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 + - get + subjects: + - kind: ServiceAccount + name: default + - name: pod-exec-role-binding + persist: false + role: + api_groups: + - '' + name: pod-exec-role + resources: + - pods/exec + verbs: + - create + 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 + - 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 + secret_mounts: [] volumes: - access_modes: - ReadWriteMany - annotations: null claim_name: calrissian-claim name: calrissian-volume persist: false - size: 50Gi + size: 30Gi storage_class: standard volume_mount: mount_path: /calrissian name: calrissian-volume - access_modes: - ReadWriteOnce - annotations: null claim_name: workspace-claim name: workspace-volume persist: true - size: 50Gi + size: 15Gi storage_class: standard volume_mount: mount_path: /workspace diff --git a/docs/README.md b/docs/README.md index 3214d58..87667ae 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,7 +1,7 @@ -# apphub-configurator +# app-hub-configurator -[![PyPI - Version](https://img.shields.io/pypi/v/apphub-configurator.svg)](https://pypi.org/project/apphub-configurator) -[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/apphub-configurator.svg)](https://pypi.org/project/apphub-configurator) +[![PyPI - Version](https://img.shields.io/pypi/v/app-hub-configurator.svg)](https://pypi.org/project/app-hub-configurator/) +[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/app-hub-configurator.svg)](https://pypi.org/project/app-hub-configurator/) ----- @@ -15,18 +15,18 @@ ## Installation ```console -pip install apphub-configurator +pip install app-hub-configurator ``` ## Overview This package contains a notebook and the python modules to support the generation of ApplicationHub configurations for a minikube cluster. For more information about ApplicationHub please check this [link](https://github.com/EOEPCA/application-hub-context) ## Examples: -Find more examples if you need from this [link](https://github.com/EOEPCA/application-hub-context/tree/ESAEOEPCA-236/config-generator/apphub-configurator/examples) +Find more examples in the official documentation website: [https://eoepca.github.io/app-hub-configurator/](https://eoepca.github.io/app-hub-configurator/) ### Step 1: Setup the environment -To begin using the `apphub-configurator` package, import the required functions from the package: +To begin using the `app-hub-configurator` package, import the required functions from the package: ```python from apphub_configurator.helpers import load_config_map, load_manifests, create_init_container, load_init_script @@ -119,4 +119,4 @@ Here is an overview of the functions and how to use them to generate configurati ## License -`apphub-configurator` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. \ No newline at end of file +`app-hub-configurator` is distributed under the terms of the [MIT](https://spdx.org/licenses/MIT.html) license. diff --git a/docs/configuration.md b/docs/configuration.md index 7224018..555e98a 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -7,9 +7,19 @@ In the Application Hub, configuration is vital for defining Kubernetes objects t Developing Kubernetes manifests or configuring the cluster using YAML files can be challenging and prone to errors. To simplify this process, a Python-based configuration generator can be used to produce clean, well-structured YAML files. This reduces the risk of misconfiguration and improves maintainability. Users can programmatically define and customize various Kubernetes resources(e.g.**Pods**, **Volumes**, and **ConfigMaps**), well as handling the **External Secrets Operator**, define **profiles**, and manage **Helm releases**. This approach offers flexibility and empowers users to tailor deployments to their specific requirements. -## Apphub-configurator +## App-hub-configurator The Configuration Generator is a Python module designed to facilitate the creation of configuration files for Application Hub deployments. It offers various utilities to streamline the configuration process. + +### Project location and compatibility + +The configurator package is managed outside this repository: + +- PyPI: [https://pypi.org/project/app-hub-configurator/](https://pypi.org/project/app-hub-configurator/) +- Docs and examples: [https://eoepca.github.io/app-hub-configurator/](https://eoepca.github.io/app-hub-configurator/) + +When upgrading, validate compatibility between your `application-hub-context` release and the generated `config.yaml` structure before deploying to shared environments. + ### Python utilities: - **`load_config_map(name, key, file_name, mount_path)`**: Reads a specified file and returns a `ConfigMap` object with the file's content, intended for use within Kubernetes configurations. @@ -66,7 +76,7 @@ The module defines several Pydantic-based data classes representing Kubernetes o These data classes provide a structured approach to defining Kubernetes resources. ### Examples: -The user can follow the examples provided to setup different profiles on Application Hub through the provided [notebook](./config-generator.ipynb) under example folder. In the notebook, the user is able to configure different profile including: +The user can follow examples to setup different profiles on Application Hub in the dedicated documentation: [https://eoepca.github.io/app-hub-configurator/](https://eoepca.github.io/app-hub-configurator/). The examples cover profiles including: * Coder @@ -82,7 +92,7 @@ The user can follow the examples provided to setup different profiles on Applica ### Output format: -The `apphub-configurator` package generates a `config.yaml` file to define various profiles for your application deployment. Each profile includes several key attributes that need to be configured: +The `app-hub-configurator` package generates a `config.yaml` file to define various profiles for your application deployment. Each profile includes several key attributes that need to be configured: - **id**: the profile identifier of your app - **groups**: the group list containing the users groups that can use the declared app diff --git a/docs/hands-on.md b/docs/hands-on.md index 0af261a..8e0f0de 100644 --- a/docs/hands-on.md +++ b/docs/hands-on.md @@ -1,6 +1,33 @@ # Hands-on -- Config generator: -Open the example [notebook](./config-generator.ipynb) to generate a `config.yaml` with your desired profile configuration -- Add `eoepca/application-hub` from helm repositories. -- Deploy the ApplicationHub on a remote cluster using skaffold. \ No newline at end of file +## 1. Generate your config + +Install and use the external configurator project: + +- PyPI: [https://pypi.org/project/app-hub-configurator/](https://pypi.org/project/app-hub-configurator/) +- Docs: [https://eoepca.github.io/app-hub-configurator/](https://eoepca.github.io/app-hub-configurator/) + +Produce a `config.yaml` file containing your target profiles. + +## 2. Place the generated config in this repository + +Copy or rename the generated file to `custom-config.yml` in the repository root: + +```bash +cp config.yaml custom-config.yml +``` + +The `skaffold` `custom` profile is already configured to use this file. + +## 3. Deploy with skaffold + +```bash +skaffold run -p custom +``` + +## 4. Verify deployment + +```bash +kubectl get pods -n jupyter +kubectl get configmap -n jupyter +``` diff --git a/docs/index.md b/docs/index.md index 4e1cc1b..58f6120 100644 --- a/docs/index.md +++ b/docs/index.md @@ -2,6 +2,13 @@ The Application Hub provides a unified Cloud infrastructure to manage and deliver work environments and tools for a variety of user tasks. These tasks include developing, hosting, executing, and performing exploratory analysis of Earth Observation (EO) applications. The hub simplifies the management of these tasks within a single, cohesive platform. +## Configurator Migration + +The configurator is no longer maintained in this repository. Use the external project: + +- Package: [https://pypi.org/project/app-hub-configurator/](https://pypi.org/project/app-hub-configurator/) +- Documentation: [https://eoepca.github.io/app-hub-configurator/](https://eoepca.github.io/app-hub-configurator/) + ## Configuration Overview There are many approach for deployment and one of them is using python based approach. Indeed, the user can generate, and apply the kubernetes objects using python instead of old-fashion approaches. In this tutorial, the user will configure The Application Hub using a Python script, [`jupyterhub_config.py`](../files/hub/jupyterhub_config.py). It customizes Application Hub's behavior by defining hooks, authentication, spawner settings, and other configurations. diff --git a/mkdocs.yml b/mkdocs.yml index 2483cfc..1f8e159 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -3,8 +3,8 @@ site_description: 'Application-Hub context' # Repository repo_name: 'eoepca/application-hub-context' -repo_url: 'https://github.com/EOEPCA/zapplication-hub-context' -edit_uri: 'blob/main/docs/src/' +repo_url: 'https://github.com/EOEPCA/application-hub-context' +edit_uri: 'edit/main/docs/' site_url: 'https://eoepca.github.io/application-hub-context/' @@ -25,5 +25,6 @@ nav: - Introduction: 'index.md' - Kubernetes: 'k8s.md' - Configuration: 'configuration.md' + - Configurator Reference: 'README.md' - JupyterHub API: 'jupyterhub-api.md' - Hands-on: 'hands-on.md' diff --git a/release.yaml b/release.yaml index b88c61f..2b900aa 100644 --- a/release.yaml +++ b/release.yaml @@ -1,4 +1,4 @@ image_name: application-hub image_prefix: eoepca -image_version: 1.5.1 +image_version: 1.5.2 image_registry: ghcr.io \ No newline at end of file diff --git a/setup.cfg b/setup.cfg index 135c20e..bdd13ef 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,4 +1,4 @@ [metadata] name = application-context-hub -version = 1.3.3 +version = 1.5.2 diff --git a/skaffold.yaml b/skaffold.yaml index 08a8bab..d630d49 100644 --- a/skaffold.yaml +++ b/skaffold.yaml @@ -27,11 +27,6 @@ deploy: setFiles: configYml: ./files/hub/config.yml jupyterConfig: ./files/hub/jupyterhub_config.py - hooks: - before: - - host: - command: ["sh", "-c", "apphub-configurator/examples/generate-config.sh"] - os: [darwin, linux] manifests: rawYaml: - sk-k8s/cluster-role-binding.yaml