diff --git a/.github/workflows/docker-jupyterlab.yml b/.github/workflows/docker-jupyterlab.yml new file mode 100644 index 00000000..73165226 --- /dev/null +++ b/.github/workflows/docker-jupyterlab.yml @@ -0,0 +1,25 @@ +name: Docker Image Build Jupyterlab + +on: + push: + branches: [ master, dev*, feature/docker* ] + pull_request: + branches: [ master, dev*, feature/docker* ] + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Build the Docker image + run: | + cd applications/jupyterlab + docker build -t myjlab -f Dockerfile --no-cache . + + - name: Info on Docker image sizes + run: | + docker images diff --git a/.github/workflows/docker-netpyne.yml b/.github/workflows/docker-netpyne.yml new file mode 100644 index 00000000..d1601ede --- /dev/null +++ b/.github/workflows/docker-netpyne.yml @@ -0,0 +1,25 @@ +name: Docker Image Build NetPyNE-UI + +on: + push: + branches: [ master, dev*, feature/docker* ] + pull_request: + branches: [ master, dev*, feature/docker* ] + +jobs: + + build: + + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v3 + + - name: Build the Docker image + run: | + cd applications/netpyne + docker build -t mynpui -f Dockerfile --no-cache . + + - name: Info on Docker image sizes + run: | + docker images diff --git a/.github/workflows/docker-nwbe.yml b/.github/workflows/docker-nwbe.yml new file mode 100644 index 00000000..e779fa3a --- /dev/null +++ b/.github/workflows/docker-nwbe.yml @@ -0,0 +1,27 @@ +name: Docker Image Build NWB Explorer + +on: + push: + branches: [ master, dev*, feature/docker* ] + pull_request: + branches: [ master, dev*, feature/docker* ] + +jobs: + + build: + + runs-on: ubuntu-20.04 + + steps: + - uses: actions/checkout@v3 + + - name: Build the Docker image + run: | + cd applications/nwb-explorer + docker build -t mynwbosb -f Dockerfile --no-cache . + + - name: Info on Docker image sizes + run: | + docker images + + diff --git a/applications/accounts-api/backend/accounts_api/services/user_service.py b/applications/accounts-api/backend/accounts_api/services/user_service.py index 92cb38d0..a5b3dcb3 100644 --- a/applications/accounts-api/backend/accounts_api/services/user_service.py +++ b/applications/accounts-api/backend/accounts_api/services/user_service.py @@ -4,13 +4,11 @@ from cloudharness.auth.quota import get_user_quotas from keycloak.exceptions import KeycloakGetError, KeycloakError from accounts_api.models import User -from cloudharness.auth import AuthClient +from cloudharness.auth import AuthClient, UserNotFound from cloudharness import log import typing # from cloudharness.models import User as CHUser # Cloudharness 2.0.0 -class UserNotFound(Exception): pass - class UserNotAuthorized(Exception): pass @@ -20,7 +18,8 @@ def get_user(username_or_id: str) -> User: try: kc_user = client.get_user(username_or_id) - + except UserNotFound: + raise except KeycloakGetError as e: if e.response_code == 404: raise UserNotFound(username_or_id) diff --git a/applications/accounts/themes/custom/login/resources/css/login.css b/applications/accounts/themes/custom/login/resources/css/login.css index 0a535bc2..68c32809 100644 --- a/applications/accounts/themes/custom/login/resources/css/login.css +++ b/applications/accounts/themes/custom/login/resources/css/login.css @@ -19,7 +19,7 @@ body { } .login-pf-logo { - background: url(../img/osblogo.png) no-repeat; + background: url(../img/osb-logo.png) no-repeat; background-size: contain; width: 132px; height: 20px; diff --git a/applications/accounts/themes/custom/login/resources/img/favicon.svg b/applications/accounts/themes/custom/login/resources/img/favicon.svg new file mode 100644 index 00000000..38a46e08 --- /dev/null +++ b/applications/accounts/themes/custom/login/resources/img/favicon.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/applications/accounts/themes/custom/login/resources/img/osb-logo.png b/applications/accounts/themes/custom/login/resources/img/osb-logo.png index c83ae2c3..14c454e7 100644 Binary files a/applications/accounts/themes/custom/login/resources/img/osb-logo.png and b/applications/accounts/themes/custom/login/resources/img/osb-logo.png differ diff --git a/applications/accounts/themes/custom/login/resources/img/osblogo.png b/applications/accounts/themes/custom/login/resources/img/osblogo.png deleted file mode 100644 index c1fd863e..00000000 Binary files a/applications/accounts/themes/custom/login/resources/img/osblogo.png and /dev/null differ diff --git a/applications/accounts/themes/custom/login/template.ftl b/applications/accounts/themes/custom/login/template.ftl index 2e23ec0e..9287584b 100644 --- a/applications/accounts/themes/custom/login/template.ftl +++ b/applications/accounts/themes/custom/login/template.ftl @@ -13,7 +13,7 @@ ${msg("loginTitle",(realm.displayName!''))} - + <#if properties.stylesCommon?has_content> <#list properties.stylesCommon?split(' ') as style> diff --git a/applications/jupyterhub/.dockerignore b/applications/jupyterhub/.dockerignore new file mode 100644 index 00000000..b8cad24a --- /dev/null +++ b/applications/jupyterhub/.dockerignore @@ -0,0 +1 @@ +/deploy \ No newline at end of file diff --git a/applications/jupyterhub/Dockerfile b/applications/jupyterhub/Dockerfile index 4ad8f20c..3c419293 100644 --- a/applications/jupyterhub/Dockerfile +++ b/applications/jupyterhub/Dockerfile @@ -36,8 +36,6 @@ COPY --chown=1000:1000 theming/spawn_pending.html /usr/local/share/jupyterhub/te COPY --chown=1000:1000 theming/hot_fix_for_eventsource.js /usr/local/share/jupyterhub/static/hot_fix_for_eventsource.js COPY --chown=1000:1000 theming/arrow-dropdown.svg /usr/local/share/jupyterhub/static/arrow-dropdown.svg -COPY hub/jupyter_notebook_config.py /etc/jupyter/jupyter_notebook_config.py - RUN chmod 777 /usr/src/app -R USER jovyan diff --git a/applications/jupyterhub/hub/jupyter_notebook_config.py b/applications/jupyterhub/deploy/resources/applications/jupyter_notebook_config.py old mode 100755 new mode 100644 similarity index 95% rename from applications/jupyterhub/hub/jupyter_notebook_config.py rename to applications/jupyterhub/deploy/resources/applications/jupyter_notebook_config.py index 748f6bca..34ba9b8b --- a/applications/jupyterhub/hub/jupyter_notebook_config.py +++ b/applications/jupyterhub/deploy/resources/applications/jupyter_notebook_config.py @@ -20,7 +20,7 @@ print(c.NotebookApp.tornado_settings) c.NotebookApp.tornado_settings = { 'headers': { - 'Content-Security-Policy': "frame-ancestors 'self' localhost:3000 localhost:* localhost *.osb.local *.opensourcebrain.org *.metacell.us", + 'Content-Security-Policy': "frame-ancestors 'self' localhost:3000 localhost:* localhost:3000 *.osb.local *.opensourcebrain.org *.metacell.us", } } print(c.NotebookApp.tornado_settings) diff --git a/applications/jupyterhub/deploy/templates/configmap.yaml b/applications/jupyterhub/deploy/templates/configmap.yaml new file mode 100644 index 00000000..ee05f573 --- /dev/null +++ b/applications/jupyterhub/deploy/templates/configmap.yaml @@ -0,0 +1,9 @@ +kind: ConfigMap +apiVersion: v1 +metadata: + name: "jupyterhub-notebook-config" + labels: + app: jupytehub +data: + {{- (.Files.Glob "resources/jupyterhub/applications/*").AsConfig | nindent 2 }} +--- diff --git a/applications/jupyterhub/deploy/values-minimal.yaml b/applications/jupyterhub/deploy/values-minimal.yaml index 9054ed69..a76dee1f 100644 --- a/applications/jupyterhub/deploy/values-minimal.yaml +++ b/applications/jupyterhub/deploy/values-minimal.yaml @@ -2,3 +2,4 @@ harness: dependencies: soft: [nfsserver, accounts] hard: [] +nfs_volumes: false \ No newline at end of file diff --git a/applications/jupyterhub/deploy/values.yaml b/applications/jupyterhub/deploy/values.yaml index 9f35a43d..e8a8e8f0 100755 --- a/applications/jupyterhub/deploy/values.yaml +++ b/applications/jupyterhub/deploy/values.yaml @@ -28,7 +28,7 @@ harness: quota-ws-maxmem: 1 # sets the storage dedicated to the user data in Gb units (float) quota-storage-max : 1.25 - +nfs_volumes: false hub: config: JupyterHub: diff --git a/applications/jupyterhub/kubespawner b/applications/jupyterhub/kubespawner index 8942af43..812d309f 160000 --- a/applications/jupyterhub/kubespawner +++ b/applications/jupyterhub/kubespawner @@ -1 +1 @@ -Subproject commit 8942af43f3a5eb951b41c3012c52b301d9ed6655 +Subproject commit 812d309f2bf5cec7e107f2ea85883fc677289d16 diff --git a/applications/jupyterhub/src/chauthenticator/chauthenticator/auth.py b/applications/jupyterhub/src/chauthenticator/chauthenticator/auth.py index cea91077..9eccfc8a 100644 --- a/applications/jupyterhub/src/chauthenticator/chauthenticator/auth.py +++ b/applications/jupyterhub/src/chauthenticator/chauthenticator/auth.py @@ -27,8 +27,19 @@ def initialize(self, force_new_server, process_user): @gen.coroutine def get(self): + if 'open=' in self.request.uri: + url = self.request.uri.split('open=').pop() + self.request.cookies.set("loadurl", bytes( + url, 'utf-8'), encrypted=False, httponly=False) + + # legacy nwb explorer support + elif 'nwbfile=' in self.request.uri: + print("Nwb file found") + url = self.request.uri.split('nwbfile=').pop().split("&")[0] + print("NWB URL", url) + self._set_cookie("nwbloadurl", bytes( + url, 'utf-8'), encrypted=False, httponly=False) self.clear_login_cookie() - try: accessToken = self.request.cookies.get( @@ -50,7 +61,9 @@ def get(self): self.request.cookies.clear() raw_user = self.get_anonymous_user() - print("JH user: ", raw_user.__dict__) + + + # print("JH user: ", raw_user.__dict__) self.set_login_cookie(raw_user) user = yield gen.maybe_future(self.process_user(raw_user, self)) self.redirect(self.get_next_url(user)) @@ -99,7 +112,10 @@ def get_handlers(self, app): 'process_user': self.process_user } return [ - ('/chkclogin', CloudHarnessAuthenticateHandler, extra_settings) + ('/chkclogin', CloudHarnessAuthenticateHandler, extra_settings), + ('/nwbfile=.*', CloudHarnessAuthenticateHandler, extra_settings), + ('/open=.*', CloudHarnessAuthenticateHandler, extra_settings), + ('/query?.*', CloudHarnessAuthenticateHandler, extra_settings), ] def login_url(self, base_url): diff --git a/applications/jupyterhub/src/osb_jupyter/osb_jupyter/jupyterhub.py b/applications/jupyterhub/src/osb_jupyter/osb_jupyter/jupyterhub.py index 414e94b9..02b10934 100755 --- a/applications/jupyterhub/src/osb_jupyter/osb_jupyter/jupyterhub.py +++ b/applications/jupyterhub/src/osb_jupyter/osb_jupyter/jupyterhub.py @@ -1,9 +1,15 @@ +import re from jupyterhub.user import User from kubespawner.spawner import KubeSpawner from cloudharness.auth import AuthClient from cloudharness import log from cloudharness import applications +from cloudharness.auth.exceptions import UserNotFound +from urllib.parse import parse_qs, urlparse + +allowed_chars = set( + "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") def affinity_spec(key, value): @@ -22,9 +28,11 @@ def affinity_spec(key, value): 'topologyKey': 'kubernetes.io/hostname' } + class CookieNotFound(Exception): pass + def change_pod_manifest(self: KubeSpawner): """ Application Hook to change the manifest of the notebook image @@ -47,7 +55,8 @@ def get_from_cookie(cookie_name): return cookie.value def user_volume_is_legacy(user_id): - print("User id", user_id, "max", self.config['apps']['jupyterhub'].get('legacyusermax', 0)) + print("User id", user_id, "max", + self.config['apps']['jupyterhub'].get('legacyusermax', 0)) return int(user_id) < self.config['apps']['jupyterhub'].get('legacyusermax', 0) def workspace_volume_is_legacy(workspace_id): @@ -75,21 +84,20 @@ def workspace_volume_is_legacy(workspace_id): self.volumes.append(ws_pvc) app_user = get_app_user(self.user) - # Add labels to use for affinity - clean_username = "".join(c for c in app_user.username if c.isalnum()) + clean_username = "".join( + c for c in app_user.username if c in allowed_chars) labels = { 'workspace': str(workspace_id), 'username': clean_username, 'user': str(self.user.id), } - - self.common_labels = labels self.extra_labels = labels - self.storage_class = f'{self.config["namespace"]}-nfs-client' + if self.config['apps']['jupyterhub'].get('nfs_volumes', False): + self.storage_class = f'{self.config["namespace"]}-nfs-client' if not user_volume_is_legacy(self.user.id): # User pod affinity is by default added by cloudharness @@ -98,10 +106,11 @@ def workspace_volume_is_legacy(workspace_id): workspace = get_workspace(workspace_id, get_from_cookie("accessToken")) write_access = has_user_write_access( workspace, self.user, app_user=app_user) - + if workspace_volume_is_legacy(workspace_id): # Pods with write access must be on the same node - self.pod_affinity_required.append(affinity_spec('workspace', workspace_id)) + self.pod_affinity_required.append( + affinity_spec('workspace', workspace_id)) from pprint import pprint pprint(self.volumes) self.pod_name = f'ws-{clean_username}-{workspace_id}-{appname}' @@ -111,7 +120,16 @@ def workspace_volume_is_legacy(workspace_id): 'mountPath': '/opt/workspace', 'readOnly': not write_access }) - except CookieNotFound: + if "image=" in self.handler.request.uri and is_user_trusted(self.user): + print("Image override") + image = parse_qs(urlparse(self.handler.request.uri).query)[ + 'image'][0] + print("Image is", image) + self.image = image + self.pod_name = f"{self.pod_name}--{re.sub('[^0-9a-zA-Z]+', '-', image)}" + # open external resources + + except (CookieNotFound, UserNotFound): # Setup a readonly default session self.pod_name = f'anonymous-{self.user.name}-{appname}' self.storage_pvc_ensure = False @@ -122,29 +140,55 @@ def workspace_volume_is_legacy(workspace_id): except Exception as e: log.error('Change pod manifest failed due to an error.', exc_info=True) + # Add customized config map for jupyter notebook config + self.volumes.append({ + 'name': 'jupyterhub-notebook-config', + 'configMap': {'name': 'jupyterhub-notebook-config'} + }) + self.volume_mounts.append({ + 'name': 'jupyterhub-notebook-config', + 'subPath': 'jupyter_notebook_config.py', + 'mountPath': '/etc/jupyter/jupyter_notebook_config.py', + 'readOnly': True + }) + self.volume_mounts.append({ + 'name': 'jupyterhub-notebook-config', + 'subPath': 'jupyter_notebook_config.py', + 'mountPath': '/opt/conda/etc/jupyter/nbconfig/jupyter_notebook_config.py', + 'readOnly': True + }) + + + def get_app_user(user: User): auth_client = AuthClient() kc_user = auth_client.get_user(user.name) return kc_user + def has_user_write_access(workspace, user: User, app_user=None): print('Checking access, name:', user.name, "workspace:", workspace["id"]) - - workspace_owner = workspace["user"]["id"] - print("Workspace owner", workspace_owner, "-", workspace["user"]["username"]) - - if workspace_owner == user.name: - return True + print("Workspace owner", workspace_owner, + "-", workspace["user"]["username"]) + return workspace_owner == user.name or is_user_trusted(user) + + +def is_user_trusted(user: User): auth_client = AuthClient() - return auth_client.user_has_realm_role(app_user.id, 'administrator') + kc_user = auth_client.get_user(user.name) + return auth_client.user_has_realm_role(kc_user.id, 'administrator') or\ + auth_client.user_has_realm_role(kc_user.id, 'trusted') + def get_workspace(workspace_id, token, workspace_base_url=None): if workspace_base_url is None: - workspace_conf: applications.ApplicationConfiguration = applications.get_configuration('workspaces') + workspace_conf: applications.ApplicationConfiguration = applications.get_configuration( + 'workspaces') workspace_base_url = workspace_conf.get_service_address() import requests - workspace = requests.get(f"{workspace_base_url}/api/workspace/{workspace_id}", headers={"Authorization": f"Bearer {token}"}).json() - - return workspace \ No newline at end of file + workspace = requests.get(f"{workspace_base_url}/api/workspace/{workspace_id}", + headers={"Authorization": f"Bearer {token}"}).json() + + return workspace diff --git a/applications/jupyterlab-minimal/Dockerfile b/applications/jupyterlab-minimal/Dockerfile index 0c0770c9..b3c82306 100644 --- a/applications/jupyterlab-minimal/Dockerfile +++ b/applications/jupyterlab-minimal/Dockerfile @@ -1,6 +1,5 @@ -FROM jupyter/base-notebook:hub-1.4.2 +FROM jupyter/base-notebook:hub-1.5.0 -COPY hub/jupyter_notebook_config.py /etc/jupyter/jupyter_notebook_config.py USER root RUN mkdir /opt/workspace RUN chown -R jovyan:users /opt/workspace diff --git a/applications/jupyterlab-minimal/hub/jupyter_notebook_config.py b/applications/jupyterlab-minimal/hub/jupyter_notebook_config.py deleted file mode 100644 index a23e00ad..00000000 --- a/applications/jupyterlab-minimal/hub/jupyter_notebook_config.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) Jupyter Development Team. -# Distributed under the terms of the Modified BSD License. - -from jupyter_core.paths import jupyter_data_dir -import subprocess -import os -import errno -import stat - -c = get_config() -c.NotebookApp.ip = '0.0.0.0' -c.NotebookApp.port = 8888 -c.NotebookApp.open_browser = False - -# https://github.com/jupyter/notebook/issues/3130 -c.FileContentsManager.delete_to_trash = False - -print('*'*80) -import notebook -print(c.NotebookApp.tornado_settings) -c.NotebookApp.tornado_settings = { - 'headers': { - 'Content-Security-Policy': "frame-ancestors *", - } -} -print(c.NotebookApp.tornado_settings) -print('*'*80) - -# Generate a self-signed certificate -if 'GEN_CERT' in os.environ: - dir_name = jupyter_data_dir() - pem_file = os.path.join(dir_name, 'notebook.pem') - try: - os.makedirs(dir_name) - except OSError as exc: # Python >2.5 - if exc.errno == errno.EEXIST and os.path.isdir(dir_name): - pass - else: - raise - - # Generate an openssl.cnf file to set the distinguished name - cnf_file = os.path.join(os.getenv('CONDA_DIR', '/usr/lib'), 'ssl', 'openssl.cnf') - if not os.path.isfile(cnf_file): - with open(cnf_file, 'w') as fh: - fh.write('''\ -[req] -distinguished_name = req_distinguished_name -[req_distinguished_name] -''') - - # Generate a certificate if one doesn't exist on disk - subprocess.check_call(['openssl', 'req', '-new', - '-newkey', 'rsa:2048', - '-days', '365', - '-nodes', '-x509', - '-subj', '/C=XX/ST=XX/L=XX/O=generated/CN=generated', - '-keyout', pem_file, - '-out', pem_file]) - # Restrict access to the file - os.chmod(pem_file, stat.S_IRUSR | stat.S_IWUSR) - c.NotebookApp.certfile = pem_file - -# Change default umask for all subprocesses of the notebook server if set in -# the environment -if 'NB_UMASK' in os.environ: - os.umask(int(os.environ['NB_UMASK'], 8)) diff --git a/applications/jupyterlab/.dockerignore b/applications/jupyterlab/.dockerignore new file mode 100644 index 00000000..b8cad24a --- /dev/null +++ b/applications/jupyterlab/.dockerignore @@ -0,0 +1 @@ +/deploy \ No newline at end of file diff --git a/applications/jupyterlab/Dockerfile b/applications/jupyterlab/Dockerfile index 7f8f4a16..151473ad 100644 --- a/applications/jupyterlab/Dockerfile +++ b/applications/jupyterlab/Dockerfile @@ -51,8 +51,6 @@ RUN cat ~/.bashrc USER root -COPY hub/jupyter_notebook_config.py /etc/jupyter/jupyter_notebook_config.py - # RUN chown jovyan /opt # RUN chown -R jovyan /opt/conda # give user permission to update existing packages @@ -91,6 +89,8 @@ USER jovyan RUN pip install -r requirements.txt --upgrade --no-cache-dir ######################################################################### +# fix for https://github.com/jupyter/notebook/issues/7048 +RUN pip install traitlets==5.9.0 COPY --chown=jovyan:users overrides/* /opt/conda/share/jupyter/lab/static/ WORKDIR /opt/workspace diff --git a/applications/jupyterlab/deploy/values.yaml b/applications/jupyterlab/deploy/values.yaml index d5264ee5..838cbce4 100755 --- a/applications/jupyterlab/deploy/values.yaml +++ b/applications/jupyterlab/deploy/values.yaml @@ -20,5 +20,5 @@ harness: c.Spawner.http_timeout = 300 c.Spawner.start_timeout = 300 - c.JupyterHub.tornado_settings = { "headers": { "Content-Security-Policy": "frame-ancestors 'self' localhost:3000 *.osb.local osb.local localhost *.metacell.us *.opensourcebrain.org"}} + c.JupyterHub.tornado_settings = { "headers": { "Content-Security-Policy": "frame-ancestors 'self' localhost:3000 *.osb.local osb.local localhost *.metacell.us *.opensourcebrain.org "}} diff --git a/applications/jupyterlab/hub/jupyter_notebook_config.py b/applications/jupyterlab/hub/jupyter_notebook_config.py deleted file mode 100755 index 4e25c2ae..00000000 --- a/applications/jupyterlab/hub/jupyter_notebook_config.py +++ /dev/null @@ -1,66 +0,0 @@ -# Copyright (c) Jupyter Development Team. -# Distributed under the terms of the Modified BSD License. - -from jupyter_core.paths import jupyter_data_dir -import subprocess -import os -import errno -import stat - -c = get_config() -c.NotebookApp.ip = '0.0.0.0' -c.NotebookApp.port = 8888 -c.NotebookApp.open_browser = False - -# https://github.com/jupyter/notebook/issues/3130 -c.FileContentsManager.delete_to_trash = False - -print('*'*80) -import notebook -print(c.NotebookApp.tornado_settings) -c.NotebookApp.tornado_settings = { - 'headers': { - 'Content-Security-Policy': "frame-ancestors 'self' localhost:3000 localhost *.osb.local *.metacell.us *.opensourcebrain.org", - } -} -print(c.NotebookApp.tornado_settings) -print('*'*80) - -# Generate a self-signed certificate -if 'GEN_CERT' in os.environ: - dir_name = jupyter_data_dir() - pem_file = os.path.join(dir_name, 'notebook.pem') - try: - os.makedirs(dir_name) - except OSError as exc: # Python >2.5 - if exc.errno == errno.EEXIST and os.path.isdir(dir_name): - pass - else: - raise - - # Generate an openssl.cnf file to set the distinguished name - cnf_file = os.path.join(os.getenv('CONDA_DIR', '/usr/lib'), 'ssl', 'openssl.cnf') - if not os.path.isfile(cnf_file): - with open(cnf_file, 'w') as fh: - fh.write('''\ -[req] -distinguished_name = req_distinguished_name -[req_distinguished_name] -''') - - # Generate a certificate if one doesn't exist on disk - subprocess.check_call(['openssl', 'req', '-new', - '-newkey', 'rsa:2048', - '-days', '365', - '-nodes', '-x509', - '-subj', '/C=XX/ST=XX/L=XX/O=generated/CN=generated', - '-keyout', pem_file, - '-out', pem_file]) - # Restrict access to the file - os.chmod(pem_file, stat.S_IRUSR | stat.S_IWUSR) - c.NotebookApp.certfile = pem_file - -# Change default umask for all subprocesses of the notebook server if set in -# the environment -if 'NB_UMASK' in os.environ: - os.umask(int(os.environ['NB_UMASK'], 8)) diff --git a/applications/jupyterlab/overrides/index.html b/applications/jupyterlab/overrides/index.html index bf3a6746..f8fee6e8 100644 --- a/applications/jupyterlab/overrides/index.html +++ b/applications/jupyterlab/overrides/index.html @@ -1,18 +1,7 @@ - - - JupyterLab - {# Copy so we do not modify the page_config with - updates. #} {% set page_config_full = page_config.copy() %} {# Set a dummy variable - we just want the side effect - of the update. #} {% set _ = page_config_full.update(baseUrl=base_url, wsUrl=ws_url) %} - {% block favicon %} - - - {% endblock %} - - +JupyterLab{# Copy so we do not modify the page_config with updates. #} {% set page_config_full = page_config.copy() %} {# Set a dummy variable - we just want the side effect of the update. #} {% set _ = page_config_full.update(baseUrl=base_url, wsUrl=ws_url) %}{% block favicon %}{% endblock %}