This is an automated email from the ASF dual-hosted git repository.

lahirujayathilake pushed a commit to branch agent-framewok-refactoring
in repository https://gitbox.apache.org/repos/asf/airavata.git


The following commit(s) were added to refs/heads/agent-framewok-refactoring by 
this push:
     new f1c2f64441 multi server jupyterhub config
f1c2f64441 is described below

commit f1c2f644413d1601c484205fa0d5de3a3bad8ab1
Author: lahiruj <[email protected]>
AuthorDate: Wed Mar 19 10:35:51 2025 -0400

    multi server jupyterhub config
---
 .../jupyterhub/multi-server-jupyterhub_config.py   | 132 +++++++++++++++++++++
 1 file changed, 132 insertions(+)

diff --git a/dev-tools/deployment/jupyterhub/multi-server-jupyterhub_config.py 
b/dev-tools/deployment/jupyterhub/multi-server-jupyterhub_config.py
new file mode 100644
index 0000000000..87de4d30fe
--- /dev/null
+++ b/dev-tools/deployment/jupyterhub/multi-server-jupyterhub_config.py
@@ -0,0 +1,132 @@
+from oauthenticator.generic import GenericOAuthenticator
+from dockerspawner import DockerSpawner
+from tornado import web
+from docker.errors import NotFound
+import os
+import sys
+import re
+import docker
+
+# Authenticator Configuration
+c.JupyterHub.authenticator_class = GenericOAuthenticator
+c.GenericOAuthenticator.client_id = os.getenv('OAUTH_CLIENT_ID')
+c.GenericOAuthenticator.client_secret = os.getenv('OAUTH_CLIENT_SECRET')
+c.GenericOAuthenticator.oauth_callback_url = 
'https://hub.cybershuttle.org/hub/oauth_callback'
+c.GenericOAuthenticator.authorize_url = 
'https://auth.cybershuttle.org/realms/default/protocol/openid-connect/auth'
+c.GenericOAuthenticator.token_url = 
'https://auth.cybershuttle.org/realms/default/protocol/openid-connect/token'
+c.GenericOAuthenticator.userdata_url = 
'https://auth.cybershuttle.org/realms/default/protocol/openid-connect/userinfo'
+c.GenericOAuthenticator.scope = ['openid', 'profile', 'email']
+c.GenericOAuthenticator.username_claim = 'email'
+
+# User Permissions
+c.Authenticator.enable_auth_state = True
+c.GenericOAuthenticator.allow_all = True
+c.Authenticator.admin_users = {'[email protected]'}
+
+# Custom Spawner
+class CustomDockerSpawner(DockerSpawner):
+    allowed_images = {
+        "base": "cybershuttle/jupyterlab-base",
+        "v1":   "cybershuttle/jupyterlab-v1",
+        "bmtk": "cybershuttle/jupyterlab-bmtk",
+    }
+
+    def _options_form_default(self):
+        return ""
+
+    def options_from_form(self, formdata):
+        if hasattr(self, 'handler') and self.handler:
+            qs_args = self.handler.request.arguments  # {'image': [b'v1']}
+            if 'image' in qs_args:
+                image_param = qs_args['image'][0].decode('utf-8')
+                return {"image": image_param}
+
+        # fallback to formdata if no query param
+        return {"image": formdata.get("image", ["base"])[0]}
+
+    def get_image(self):
+        user_requested = self.user_options.get("image", "base")
+        default_image  = "cybershuttle/jupyterlab-base"
+        return self.allowed_images.get(user_requested, default_image)
+
+    def sanitize_name(self, name):
+        return re.sub(r"[^a-zA-Z0-9_.-]", "_", name)
+
+
+    async def start(self):
+        self.image = self.get_image()
+
+        # Create a unique volume name keyed by (username + servername + image).
+        # If the user spawns again with the same (servername + image), it will 
reuse the same volume.
+
+        safe_user  = self.sanitize_name(self.user.name)
+        safe_srv   = self.sanitize_name(self.name) or "default"
+        safe_img   = self.sanitize_name(self.image.split("/")[-1])
+
+        # Overwrite self.volumes each time, so DockerSpawner creates/attaches 
them
+        self.volumes = {
+            f"jupyterhub-vol-{safe_user}-{safe_srv}-{safe_img}": 
"/home/jovyan/work",
+            "/home/ubuntu/jupyterhub/mnt/shared_data": {
+                "bind": "/home/jovyan/shared_data_tmp",
+                "mode": "ro"
+            },
+        }
+
+        # Let DockerSpawner do the normal container creation or reuse
+        return await super().start()
+
+# Spawner Configuration
+c.JupyterHub.allow_named_servers = True
+c.JupyterHub.named_server_limit_per_user = 5
+c.JupyterHub.spawner_class = CustomDockerSpawner
+c.DockerSpawner.notebook_dir = '/home/jovyan/work'
+c.DockerSpawner.default_url = "/lab"
+
+c.DockerSpawner.environment = {
+    'CHOWN_HOME': 'no',
+    'CHOWN_HOME_OPTS': '',
+}
+c.DockerSpawner.extra_create_kwargs = {'user': 'root'}
+c.DockerSpawner.use_internal_ip = True
+c.DockerSpawner.network_name = os.getenv('DOCKER_NETWORK_NAME', 
'jupyterhub_network')
+
+# Hub Configuration
+c.JupyterHub.hub_ip = '0.0.0.0'
+c.JupyterHub.hub_port = 8081
+c.JupyterHub.hub_connect_ip = 'jupyterhub'
+
+# External URL
+c.JupyterHub.external_url = 'https://hub.cybershuttle.org'
+
+# Logging
+c.JupyterHub.log_level = 'DEBUG'
+
+# Terminate idle notebook containers
+c.JupyterHub.services = [
+    {
+        "name": "jupyterhub-idle-culler-service",
+        "admin": True,
+        "command": [sys.executable, "-m", "jupyterhub_idle_culler", 
"--timeout=3600"],
+    }
+]
+
+c.JupyterHub.load_roles = [
+    {
+        "name": "jupyterhub-idle-culler-role",
+        "scopes": [
+            "list:users",
+            "read:users:activity",
+            "read:servers",
+            "delete:servers",
+        ],
+        "services": ["jupyterhub-idle-culler-service"],
+    }
+]
+
+# SSL Termination
+c.JupyterHub.bind_url = 'http://:8000'
+c.JupyterHub.external_ssl = True
+
+# Custom templates - Login
+c.JupyterHub.template_paths = ['/srv/jupyterhub/custom_templates']
+c.OAuthenticator.login_service = "Sign in with Existing Institution 
Credentials"

Reply via email to