Open jvhaarst opened 2 months ago
This is related to the page_config_hook
we use to populate hub data into page_config. Notebook 7 didn't call it, but Lab does, meaning it would be correct once a Lab page was visited, but not when another page is.
I tried to track it down with a dev install of notebook
, but the issue was gone when I installed from main. I think this is already fixed by https://github.com/jupyter/notebook/pull/7387 which is not yet released.
I just patched our site-packages/notebook/app.py to add the lines from https://github.com/jupyter/notebook/pull/7387 , and indeed the menu item is now present ! Thanks for the fast response and solution, we'll just patch our deployment until the fix lands in the repositories.
Now that this works, is there an easy way to also get the button for the "Control Panel" back in notebook 7 ?
The button in the top bar, as opposed to the menu item? I'm sure it's possible, but I'm not sure how to add it. It would be a javascript Extension.
We show our users the tree view by default instead of jupyterlab, as they have been using that since we started with jupyterhub. In our old version (3.1.1) we see the button for access to the "Control Panel" in the top left corner. In our current version (5.0.0), that button is missing.
It also isn't in the menu on first startup. At first I thought it was missing completely, so we would have to instruct our users how to access the "Control Panel".
But.....
If I switch the view from tree to jupyterlab (in another window), the menu item to access the "Control Panel" suddenly appears in the tree view !
This sounds like a bug, not a feature..
How to reproduce
Set your jupyterhub to show the older tree view by default. (
c.Spawner.default_url = '/tree'
) See that the "Control Panel" option is missing from the menu. Duplicate the tab with the view Use the View tab to start the jupyterlab view. Refresh the tree view, and the menu item is there !See above
Expected behaviour
The menu item is there.
Actual behaviour
The menu item isn't there.
Your personal set up
Installed 5.0.0 from pip, like this:
Full environment
``` alembic==1.13.2 annotated-types==0.7.0 anyio==4.4.0 argon2-cffi==23.1.0 argon2-cffi-bindings==21.2.0 arrow==1.3.0 asttokens==2.4.1 async-lru==2.0.4 attrs==23.2.0 Babel==2.15.0 batchspawner==1.3.0 beautifulsoup4==4.12.3 bleach==6.1.0 certifi==2024.7.4 certipy==0.1.3 cffi==1.16.0 charset-normalizer==3.3.2 comm==0.2.2 cryptography==42.0.8 debugpy==1.8.2 decorator==5.1.1 defusedxml==0.7.1 executing==2.0.1 fastjsonschema==2.20.0 fqdn==1.5.1 greenlet==3.0.3 h11==0.14.0 httpcore==1.0.5 httpx==0.27.0 idna==3.7 ipydatawidgets==4.3.5 ipykernel==6.29.5 ipython==8.26.0 ipywidgets==8.1.3 isoduration==20.11.0 jedi==0.19.1 Jinja2==3.1.4 json5==0.9.25 jsonpointer==3.0.0 jsonschema==4.23.0 jsonschema-specifications==2023.12.1 jupyter-events==0.10.0 jupyter-lsp==2.2.5 jupyter_client==8.6.2 jupyter_core==5.7.2 jupyter_server==2.14.2 jupyter_server_terminals==0.5.3 jupyterhub==5.0.0 jupyterlab==4.2.4 jupyterlab_pygments==0.3.0 jupyterlab_server==2.27.3 jupyterlab_widgets==3.0.11 jupyterlmod==4.0.3 Mako==1.3.5 MarkupSafe==2.1.5 matplotlib-inline==0.1.7 mistune==3.0.2 nbclient==0.10.0 nbconvert==7.16.4 nbformat==5.10.4 nest-asyncio==1.6.0 notebook==7.2.1 notebook_shim==0.2.4 numpy==2.1.0 oauthenticator==16.3.1 oauthlib==3.2.2 overrides==7.7.0 packaging==24.1 pamela==1.1.0 pandocfilters==1.5.1 parso==0.8.4 pexpect==4.9.0 platformdirs==4.2.2 prometheus_client==0.20.0 prompt_toolkit==3.0.47 psutil==6.0.0 ptyprocess==0.7.0 pure-eval==0.2.2 pycparser==2.22 pycurl==7.45.3 pydantic==2.8.2 pydantic_core==2.20.1 Pygments==2.18.0 PyJWT==2.8.0 pyOpenSSL==24.1.0 python-dateutil==2.9.0.post0 python-json-logger==2.0.7 pythreejs==2.4.2 PyYAML==6.0.1 pyzmq==26.0.3 referencing==0.35.1 requests==2.32.3 rfc3339-validator==0.1.4 rfc3986-validator==0.1.1 rpds-py==0.19.0 ruamel.yaml==0.18.6 ruamel.yaml.clib==0.2.8 Send2Trash==1.8.3 setuptools==71.0.3 six==1.16.0 sniffio==1.3.1 soupsieve==2.5 SQLAlchemy==2.0.31 stack-data==0.6.3 sudospawner==0.5.2 terminado==0.18.1 tinycss2==1.3.0 tornado==6.4.1 traitlets==5.14.3 traittypes==0.2.1 types-python-dateutil==2.9.0.20240316 typing_extensions==4.12.2 uri-template==1.3.0 urllib3==2.2.2 wcwidth==0.2.13 webcolors==24.6.0 webencodings==0.5.1 websocket-client==1.8.0 widgetsnbextension==4.0.11 wrapspawner==1.0.1 ```Configuration
```python c = get_config() import os, socket CURRENT_BASE_DIR=os.path.realpath(os.path.dirname(__file__)) SLURM_DIR='/shared/apps/slurm/current' HOSTNAME=socket.gethostname() c.Application.log_level = 'DEBUG' import os os.environ['OAUTH2_TOKEN_URL'] = 'https://auth.anunna.wur.nl/realms/anunna/protocol/openid-connect/token' os.environ['OAUTH2_AUTHORIZE_URL'] = 'https://auth.anunna.wur.nl/realms/anunna/protocol/openid-connect/auth' os.environ['OAUTH2_USERDATA_URL'] = 'https://auth.anunna.wur.nl/realms/anunna/protocol/openid-connect/userinfo' from oauthenticator.generic import GenericOAuthenticator c.JupyterHub.authenticator_class = 'oauthenticator.generic.GenericOAuthenticator' c.GenericOAuthenticator.login_service = 'keycloak' c.OAuthenticator.client_id = 'jupyterhub-dev' c.OAuthenticator.client_secret = '' c.GenericOAuthenticator.oauth_callback_url = 'https://dev.notebook.anunna.wur.nl/hub/oauth_callback' c.GenericOAuthenticator.userdata_url = os.environ['OAUTH2_USERDATA_URL'] c.GenericOAuthenticator.token_url = os.environ['OAUTH2_TOKEN_URL'] c.GenericOAuthenticator.userdata_token_method = 'GET' c.GenericOAuthenticator.userdata_params = {"state": "state"} c.GenericOAuthenticator.username_key = "preferred_username" c.GenericOAuthenticator.claim_groups_key = 'realm_access.roles' c.GenericOAuthenticator.admin_groups = ['admin_unix_admin'] c.GenericOAuthenticator.scope = ['openid', 'profile', 'roles'] c.Authenticator.auto_login = True c.JupyterHub.bind_url = 'http://127.0.0.1:8941' c.JupyterHub.db_url = 'sqlite:///jupyterhub.sqlite' c.JupyterHub.hub_connect_url = f'http://{HOSTNAME}:8945' c.JupyterHub.last_activity_interval = 300 c.ConfigurableHTTPProxy.api_url = 'http://127.0.0.1:8008' from wrapspawner import ProfilesSpawner class FormSpawner(ProfilesSpawner): def options_from_form(self, formdata): #self.log.error("Formdata:--------------------------------------------------------------") #self.log.error(formdata) #self.log.error("Profile data before changes:-------------------------------------------") #self.log.error(self.profiles) opts = formdata.copy() if 'profile' in opts: del opts['profile'] self.select_profile("HPC") for opt in opts: val = opts.get(opt, ['']) self.child_config[opt]=val[0] # Default to first profile if somehow none is provided return dict(profile=formdata.get('profile', [self.profiles[0][1]])[0]) def construct_child(self): super().construct_child() c.JupyterHub.spawner_class = FormSpawner c.JupyterHub.template_vars = { 'announcement': 'You\'re on an upgraded version of Jupyterhub. All should work as it did before. If you encounter any issues, please open a ticket. You can fall back to the old version on old.notebook.anunna.wur.nl, but we will deprecate it in the near future', 'announcement_spawn' : 'You\'re on an upgraded version of Jupyterhub. All should work as it did before. If you encounter any issues, please open a ticket. You can fall back to the old version on old.notebook.anunna.wur.nl, but we will deprecate it in the near future', } c.SudoSpawner.sudospawner_path=f'{CURRENT_BASE_DIR}/bin/sudospawner' c.Spawner.cmd = f'{CURRENT_BASE_DIR}/bin/jupyterhub-singleuser' import batchspawner c.Spawner.start_timeout=60 c.BatchSpawnerBase.batchspawner_singleuser_cmd=f'{CURRENT_BASE_DIR}/bin/batchspawner-singleuser' c.BatchSpawnerBase.batch_submit_cmd = f'{SLURM_DIR}/bin/sbatch --parsable' c.BatchSpawnerBase.batch_query_cmd = f'{SLURM_DIR}/bin/squeue -h -j {{job_id}} -o "%T %B"' c.BatchSpawnerBase.batch_cancel_cmd = f'{SLURM_DIR}/bin/scancel {{job_id}}' c.SlurmSpawner.batch_script = '''#!/bin/bash {% if partition %}#SBATCH --partition={{partition}} {% endif %}{% if runtime %}#SBATCH --time={{runtime}} {% endif %}{% if memory %}#SBATCH --mem={{memory}} {% endif %}{% if gres %}#SBATCH --gres={{gres}} {% endif %}{% if nprocs %}#SBATCH --cpus-per-task={{nprocs}} {% endif %}{% if reservation%}#SBATCH --reservation={{reservation}} {% endif %}{% if options %}#SBATCH {{options}}{% endif %} set -euo pipefail unset XDG_RUNTIME_DIR trap 'echo SIGTERM received' TERM {{prologue}} {{cmd}} {{epilogue}} ''' c.Spawner.default_url = '/tree' c.Spawner.environment = { 'LMOD_CMD':'/usr/share/lmod/lmod/libexec/lmod', 'MODULEPATH':'/shared/modulefiles:/shared/eb_modules/all:/shared/modulefiles/SHARED' } c.Spawner.env_keep = ['LMOD_CMD', 'MODULEPATH'] c.ProfilesSpawner.profiles = [ ('On the cluster', 'HPC', 'batchspawner.SlurmSpawner', {'start_timeout':300,'http_timeout':300, 'req_partition':'main', 'req_memory':'2000'} ), ('Cluster - EZO23306 (test)' , 'EZO23306', 'batchspawner.SlurmSpawner', {'start_timeout':300,'http_timeout':300,'req_partition':'main','req_nprocs':'1','req_memory':'2000M','req_runtime':'4:00:00'} ), ('Cluster - Psi4 (test)' , 'Psi4', 'batchspawner.SlurmSpawner', {'start_timeout':300,'http_timeout':300,'req_partition':'main','req_nprocs':'2','req_memory':'4000M','req_runtime':'4:00:00'} ), ('Sandbox (constrained resources)', 'local', 'sudospawner.SudoSpawner', {'ip':'0.0.0.0'} ), ] c.ProfilesSpawner.form_template="""Jupyterhub on Anunna
Logs
``` # paste relevant logs here, if any ```