From 3ae8b3cd1dd7efdd3cdb42b7ad5638154d29dd37 Mon Sep 17 00:00:00 2001 From: Sophia Castellarin Date: Mon, 9 Dec 2024 14:04:13 -0800 Subject: [PATCH] Don't use `quansight/conda-store` docker image (#1004) --- .../conda-store/how-tos/install-docker.md | 2 +- .../assets/conda_store_config.py | 6 ++- .../assets/jupyterhub_config.py | 25 ++++++++--- .../docker-without-nfs/docker-compose.yaml | 45 ++++++------------- .../dockerfiles/jupyterhub.Dockerfile | 41 +++++++++++++++++ examples/docker/assets/conda_store_config.py | 6 ++- examples/docker/assets/jupyterhub_config.py | 17 ++++++- examples/docker/docker-compose.yaml | 32 +++---------- .../docker/dockerfiles/jupyterhub.Dockerfile | 41 +++++++++++++++++ 9 files changed, 146 insertions(+), 69 deletions(-) create mode 100644 examples/docker-without-nfs/dockerfiles/jupyterhub.Dockerfile create mode 100644 examples/docker/dockerfiles/jupyterhub.Dockerfile diff --git a/docusaurus-docs/conda-store/how-tos/install-docker.md b/docusaurus-docs/conda-store/how-tos/install-docker.md index 63c660c5e..db7622134 100644 --- a/docusaurus-docs/conda-store/how-tos/install-docker.md +++ b/docusaurus-docs/conda-store/how-tos/install-docker.md @@ -17,4 +17,4 @@ docker compose up --build On your web browser, visit: [https://conda-store.localhost/conda-store](https://conda-store.localhost/conda-store). -By default, you can log in with any username and use the password `password`, since we are using `DummyAuthenticator`/` +This example uses the JupyterHub OAuth Authenticator. You can log in with the test admin user `admin`, with password `test`. diff --git a/examples/docker-without-nfs/assets/conda_store_config.py b/examples/docker-without-nfs/assets/conda_store_config.py index e345ab094..8fdd6c220 100644 --- a/examples/docker-without-nfs/assets/conda_store_config.py +++ b/examples/docker-without-nfs/assets/conda_store_config.py @@ -12,7 +12,8 @@ # conda-store settings # ================================== c.CondaStore.storage_class = S3Storage -c.CondaStore.store_directory = "/opt/conda-store/conda-store" +c.CondaStore.store_directory = "/var/lib/conda-store/" +c.CondaStore.environment_directory = "/opt/conda-store/envs/{namespace}-{name}" # Also edit `conda-store-server/alembic.ini` accordingly for key sqlalchemy.url c.CondaStore.database_url = ( "postgresql+psycopg2://postgres:password@postgres/conda-store" @@ -57,11 +58,12 @@ # auth settings # ================================== c.CondaStoreServer.authentication_class = JupyterHubOAuthAuthentication -c.JupyterHubOAuthAuthentication.jupyterhub_url = "https://conda-store.localhost" +c.JupyterHubOAuthAuthentication.jupyterhub_url = "http://conda-store.localhost" c.JupyterHubOAuthAuthentication.client_id = "service-this-is-a-jupyterhub-client" c.JupyterHubOAuthAuthentication.client_secret = "this-is-a-jupyterhub-secret" c.JupyterHubOAuthAuthentication.oauth_callback_url = "/conda-store/oauth_callback/" c.JupyterHubOAuthAuthentication.tls_verify = False +c.JupyterHubOAuthAuthentication.access_scope = "conda-store-user" # ================================== # worker settings diff --git a/examples/docker-without-nfs/assets/jupyterhub_config.py b/examples/docker-without-nfs/assets/jupyterhub_config.py index f633ec978..28ec2b03c 100644 --- a/examples/docker-without-nfs/assets/jupyterhub_config.py +++ b/examples/docker-without-nfs/assets/jupyterhub_config.py @@ -56,9 +56,8 @@ async def start(self): def user_env(self, env): env = super().user_env(env) - env["CONDA_STORE_URL"] = "https://conda-store.localhost/conda-store" + env["CONDA_STORE_URL"] = "http://conda-store.localhost/conda-store" env["CONDA_STORE_AUTH"] = "token" - env["CONDA_STORE_NO_VERIFY"] = "true" env["CONDA_STORE_TOKEN"] = self.conda_store_token return env @@ -66,7 +65,7 @@ async def generate_token(self, username): async with CondaStoreAPI( conda_store_url=os.environ["CONDA_STORE_URL"], auth_type=os.environ["CONDA_STORE_AUTH"], - verify_ssl="CONDA_STORE_NO_VERIFY" not in os.environ, + verify_ssl=False, ) as conda_store: return await conda_store.create_token( primary_namespace=username, @@ -80,7 +79,7 @@ async def list_environments(self, token: str): async with CondaStoreAPI( conda_store_url=os.environ["CONDA_STORE_URL"], auth_type=os.environ["CONDA_STORE_AUTH"], - verify_ssl="CONDA_STORE_NO_VERIFY" not in os.environ, + verify_ssl=False, ) as conda_store: return await conda_store.list_environments( status="COMPLETED", @@ -101,9 +100,25 @@ async def list_environments(self, token: str): "name": "conda-store", "oauth_client_id": "service-this-is-a-jupyterhub-client", "admin": True, - "url": "https://conda-store.localhost/conda-store/", + "url": "http://conda-store.localhost/conda-store/", "api_token": "this-is-a-jupyterhub-secret", "oauth_redirect_uri": "/conda-store/oauth_callback/", "oauth_no_confirm": True, # allows no authorize yes/no button + "oauth_client_allowed_scopes": [ + "self", "inherit", "access:services", + ] + } +] + +c.JupyterHub.load_roles = [ + { + "name": "conda-store-user", + "scopes": [ + "self", 'access:services!service=conda-store', + ], + "groups": ["conda-store-user"], } ] + +# Users are able to log in with username/password combo admin/test +c.Authenticator.admin_users = {'admin'} diff --git a/examples/docker-without-nfs/docker-compose.yaml b/examples/docker-without-nfs/docker-compose.yaml index 3a2f751a8..4354de560 100644 --- a/examples/docker-without-nfs/docker-compose.yaml +++ b/examples/docker-without-nfs/docker-compose.yaml @@ -1,37 +1,29 @@ -version: "3.8" - volumes: conda_store_data: services: traefik: image: "traefik:v2.6" - container_name: "traefik" command: - "--api.insecure=true" - "--providers.docker=true" - - "--providers.docker.exposedbydefault=false" + - "--providers.docker.exposedbydefault=true" - "--entrypoints.web.address=:80" - - "--entrypoints.websecure.address=:443" - "--entrypoints.minio.address=:9080" - - "--entrypoints.websecure.http.tls=true" - - "--entrypoints.minio.http.tls=true" - - "--entrypoints.web.http.redirections.entryPoint.to=websecure" - - "--entrypoints.web.http.redirections.entryPoint.scheme=https" - - "--entrypoints.web.http.redirections.entrypoint.permanent=true" + - "--entrypoints.websecure.http.tls=false" + - "--entrypoints.minio.http.tls=false" + - "--entrypoints.web.http.redirections.entryPoint.scheme=http" ports: - "80:80" - "443:443" - - "8080:8080" - "9080:9080" volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" conda-store-worker: image: quansight/conda-store-server - user: 1000:1000 + user: 1000:100 volumes: - - conda_store_data:/opt/conda-store/ - ./assets/conda_store_config.py:/etc/conda-store/conda_store_config.py:ro - ./assets/environments:/opt/environments:ro depends_on: @@ -53,7 +45,6 @@ services: labels: - "traefik.enable=true" - "traefik.http.routers.conda-store.rule=Host(`conda-store.localhost`) && PathPrefix(`/conda-store`)" - - "traefik.http.routers.conda-store.entrypoints=websecure" - "traefik.port=8080" depends_on: postgres: @@ -81,15 +72,18 @@ services: - "8080:8080" jupyterhub: - build: quansight/conda-store + build: + context: dockerfiles + dockerfile: jupyterhub.Dockerfile + args: + python_version: 3.12 labels: - "traefik.enable=true" - "traefik.http.routers.jupyterhub.rule=Host(`conda-store.localhost`) && (Path(`/`) || PathPrefix(`/hub`) || PathPrefix(`/user`))" - - "traefik.http.routers.jupyterhub.entrypoints=websecure" - "traefik.port=8000" user: 1000:1000 environment: - CONDA_STORE_URL: https://conda-store.localhost/conda-store + CONDA_STORE_URL: http://conda-store.localhost/conda-store CONDA_STORE_AUTH: token CONDA_STORE_NO_VERIFY: "true" CONDA_STORE_TOKEN: this-is-a-jupyterhub-secret-token @@ -122,6 +116,8 @@ services: - "traefik.http.routers.minio.rule=Host(`conda-store.localhost`)" - "traefik.http.routers.minio.entrypoints=minio" - "traefik.port=9000" + links: + - "traefik:conda-store.localhost" healthcheck: test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"] interval: 10s @@ -148,21 +144,6 @@ services: POSTGRES_PASSWORD: password POSTGRES_DB: conda-store - mysql: - image: mysql:8.0 - ports: - - 3306:3306 - healthcheck: - test: ["CMD", "mysqladmin", "ping", "-h", "localhost"] - interval: 10s - timeout: 5s - retries: 5 - environment: - MYSQL_ROOT_PASSWORD: hbgY8bkoWPMM7EjtcD6wrlVX - MYSQL_USER: admin - MYSQL_PASSWORD: password - MYSQL_DATABASE: conda-store - redis: image: bitnami/redis healthcheck: diff --git a/examples/docker-without-nfs/dockerfiles/jupyterhub.Dockerfile b/examples/docker-without-nfs/dockerfiles/jupyterhub.Dockerfile new file mode 100644 index 000000000..1b1d4acc6 --- /dev/null +++ b/examples/docker-without-nfs/dockerfiles/jupyterhub.Dockerfile @@ -0,0 +1,41 @@ +# Copyright (c) conda-store development team. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +FROM condaforge/miniforge3:24.9.2-0 AS base + +LABEL org.opencontainers.image.authors="conda-store development team" + +# must be passed at build environment (should use .python-version-default) +ARG python_version +ARG conda_env_name="conda-store" +ARG user_no=1000 + +# ensure we are using the conda environment +ENV PATH=/opt/conda/condabin:/opt/conda/envs/conda-store/bin:/opt/conda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PATH} + +RUN apt-get update && \ + apt-get install -yq --no-install-recommends curl && \ + apt-get clean && \ + rm -rf /var/cache/apt/* &&\ + rm -rf /var/lib/apt/lists/* &&\ + rm -rf /tmp/* &&\ + groupadd -g ${user_no} conda-store &&\ + useradd -M -r -s /usr/sbin/nologin -u ${user_no} -g ${user_no} conda-store && \ + mkdir -p /opt/jupyterhub && \ + chown -R conda-store:conda-store /opt/jupyterhub + +RUN mamba create --name ${conda_env_name} \ + python=${python_version} nb_conda_kernels nodejs=18 yarn constructor conda-store \ + --channel conda-forge -y && \ + mamba clean --all -y && \ + conda clean --force-pkgs-dirs + +RUN python -m pip install jupyter-launcher-shortcuts jupyterlab-conda-store jupyterlab\>=4.0.0 jupyterhub\>3.1.1 + +RUN npm install -g configurable-http-proxy + +WORKDIR /opt/conda-store + +USER conda-store +WORKDIR /opt/jupyterhub diff --git a/examples/docker/assets/conda_store_config.py b/examples/docker/assets/conda_store_config.py index f4e1ccb86..c14a32ec0 100644 --- a/examples/docker/assets/conda_store_config.py +++ b/examples/docker/assets/conda_store_config.py @@ -12,7 +12,8 @@ # conda-store settings # ================================== c.CondaStore.storage_class = S3Storage -c.CondaStore.store_directory = "/opt/conda-store/conda-store" +c.CondaStore.store_directory = "/var/lib/conda-store/" +c.CondaStore.environment_directory = "/opt/conda-store/envs/{namespace}-{name}" # Also edit `conda-store-server/alembic.ini` accordingly for key sqlalchemy.url c.CondaStore.database_url = ( "postgresql+psycopg2://postgres:password@postgres/conda-store" @@ -51,11 +52,12 @@ # auth settings # ================================== c.CondaStoreServer.authentication_class = JupyterHubOAuthAuthentication -c.JupyterHubOAuthAuthentication.jupyterhub_url = "https://conda-store.localhost" +c.JupyterHubOAuthAuthentication.jupyterhub_url = "http://conda-store.localhost" c.JupyterHubOAuthAuthentication.client_id = "service-this-is-a-jupyterhub-client" c.JupyterHubOAuthAuthentication.client_secret = "this-is-a-jupyterhub-secret" c.JupyterHubOAuthAuthentication.oauth_callback_url = "/conda-store/oauth_callback/" c.JupyterHubOAuthAuthentication.tls_verify = False +c.JupyterHubOAuthAuthentication.access_scope = "conda-store-user" # ================================== # worker settings diff --git a/examples/docker/assets/jupyterhub_config.py b/examples/docker/assets/jupyterhub_config.py index b0a15cd8e..19e53010f 100644 --- a/examples/docker/assets/jupyterhub_config.py +++ b/examples/docker/assets/jupyterhub_config.py @@ -50,9 +50,24 @@ def preexec(): "name": "conda-store", "oauth_client_id": "service-this-is-a-jupyterhub-client", "admin": True, - "url": "https://conda-store.localhost/conda-store/", + "url": "http://conda-store.localhost/conda-store/", "api_token": "this-is-a-jupyterhub-secret", "oauth_redirect_uri": "/conda-store/oauth_callback/", "oauth_no_confirm": True, # allows no authorize yes/no button + "oauth_client_allowed_scopes": [ + "self", "inherit", "access:services", + ] } ] + +c.JupyterHub.load_roles = [ + { + "name": "conda-store-user", + "scopes": [ + "self", 'access:services!service=conda-store', + ], + "groups": ["conda-store-user"], + } +] + +c.Authenticator.admin_users = {'admin'} diff --git a/examples/docker/docker-compose.yaml b/examples/docker/docker-compose.yaml index 8f7164f0d..62efe7d52 100644 --- a/examples/docker/docker-compose.yaml +++ b/examples/docker/docker-compose.yaml @@ -1,5 +1,3 @@ -version: "3.8" - volumes: conda_store_data: redis: @@ -12,19 +10,15 @@ services: command: - "--api.insecure=true" - "--providers.docker=true" - - "--providers.docker.exposedbydefault=false" + - "--providers.docker.exposedbydefault=true" - "--entrypoints.web.address=:80" - "--entrypoints.websecure.address=:443" - "--entrypoints.minio.address=:9080" - "--entrypoints.websecure.http.tls=true" - "--entrypoints.minio.http.tls=true" - - "--entrypoints.web.http.redirections.entryPoint.to=websecure" - - "--entrypoints.web.http.redirections.entryPoint.scheme=https" - - "--entrypoints.web.http.redirections.entrypoint.permanent=true" ports: - "80:80" - "443:443" - - "8080:8080" - "9080:9080" volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" @@ -42,7 +36,6 @@ services: image: quansight/conda-store-server user: 1000:1000 volumes: - - conda_store_data:/opt/conda-store/ - ./assets/conda_store_config.py:/etc/conda-store/conda_store_config.py:ro depends_on: initializer: @@ -60,7 +53,6 @@ services: labels: - "traefik.enable=true" - "traefik.http.routers.conda-store.rule=Host(`conda-store.localhost`) && (PathPrefix(`/conda-store`) || PathPrefix(`/v2`))" - - "traefik.http.routers.conda-store.entrypoints=websecure" - "traefik.port=8080" depends_on: postgres: @@ -82,11 +74,14 @@ services: - "8080:8080" jupyterhub: - image: quansight/conda-store + build: + context: dockerfiles + dockerfile: jupyterhub.Dockerfile + args: + python_version: 3.12 labels: - "traefik.enable=true" - "traefik.http.routers.jupyterhub.rule=Host(`conda-store.localhost`) && (Path(`/`) || PathPrefix(`/hub`) || PathPrefix(`/user`))" - - "traefik.http.routers.jupyterhub.entrypoints=websecure" - "traefik.port=8000" user: 1000:1000 healthcheck: @@ -142,21 +137,6 @@ services: POSTGRES_PASSWORD: password POSTGRES_DB: conda-store - mysql: - image: mysql:8.0 - ports: - - 3306:3306 - healthcheck: - test: ["CMD", "mysqladmin" ,"ping", "-h", "localhost"] - interval: 10s - timeout: 5s - retries: 5 - environment: - MYSQL_ROOT_PASSWORD: hbgY8bkoWPMM7EjtcD6wrlVX - MYSQL_USER: admin - MYSQL_PASSWORD: password - MYSQL_DATABASE: conda-store - redis: image: bitnami/redis healthcheck: diff --git a/examples/docker/dockerfiles/jupyterhub.Dockerfile b/examples/docker/dockerfiles/jupyterhub.Dockerfile new file mode 100644 index 000000000..1b1d4acc6 --- /dev/null +++ b/examples/docker/dockerfiles/jupyterhub.Dockerfile @@ -0,0 +1,41 @@ +# Copyright (c) conda-store development team. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. + +FROM condaforge/miniforge3:24.9.2-0 AS base + +LABEL org.opencontainers.image.authors="conda-store development team" + +# must be passed at build environment (should use .python-version-default) +ARG python_version +ARG conda_env_name="conda-store" +ARG user_no=1000 + +# ensure we are using the conda environment +ENV PATH=/opt/conda/condabin:/opt/conda/envs/conda-store/bin:/opt/conda/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:${PATH} + +RUN apt-get update && \ + apt-get install -yq --no-install-recommends curl && \ + apt-get clean && \ + rm -rf /var/cache/apt/* &&\ + rm -rf /var/lib/apt/lists/* &&\ + rm -rf /tmp/* &&\ + groupadd -g ${user_no} conda-store &&\ + useradd -M -r -s /usr/sbin/nologin -u ${user_no} -g ${user_no} conda-store && \ + mkdir -p /opt/jupyterhub && \ + chown -R conda-store:conda-store /opt/jupyterhub + +RUN mamba create --name ${conda_env_name} \ + python=${python_version} nb_conda_kernels nodejs=18 yarn constructor conda-store \ + --channel conda-forge -y && \ + mamba clean --all -y && \ + conda clean --force-pkgs-dirs + +RUN python -m pip install jupyter-launcher-shortcuts jupyterlab-conda-store jupyterlab\>=4.0.0 jupyterhub\>3.1.1 + +RUN npm install -g configurable-http-proxy + +WORKDIR /opt/conda-store + +USER conda-store +WORKDIR /opt/jupyterhub