From 2253fad75659986b018b9035fb5d1fffa8a3124b Mon Sep 17 00:00:00 2001 From: Masaki Yatsu Date: Mon, 8 Sep 2025 20:44:12 +0900 Subject: [PATCH] chore(jupyterhub): Improve SecretStore error messages --- jupyterhub/justfile | 2 +- python-package/buunstack/secrets.py | 81 ++++++++++------------------- 2 files changed, 28 insertions(+), 55 deletions(-) diff --git a/jupyterhub/justfile b/jupyterhub/justfile index b8b064a..bb125cb 100644 --- a/jupyterhub/justfile +++ b/jupyterhub/justfile @@ -8,7 +8,7 @@ export JUPYTERHUB_OIDC_CLIENT_SESSION_MAX := env("JUPYTERHUB_OIDC_CLIENT_SESSION export JUPYTERHUB_NFS_PV_ENABLED := env("JUPYTERHUB_NFS_PV_ENABLED", "") export JUPYTERHUB_STORAGE_CLASS := env("JUPYTERHUB_STORAGE_CLASS", "") export JUPYTERHUB_VAULT_INTEGRATION_ENABLED := env("JUPYTERHUB_VAULT_INTEGRATION_ENABLED", "") -export JUPYTER_PYTHON_KERNEL_TAG := env("JUPYTER_PYTHON_KERNEL_TAG", "python-3.12-30") +export JUPYTER_PYTHON_KERNEL_TAG := env("JUPYTER_PYTHON_KERNEL_TAG", "python-3.12-31") export KERNEL_IMAGE_BUUN_STACK_REPOSITORY := env("KERNEL_IMAGE_BUUN_STACK_REPOSITORY", "buun-stack-notebook") export KERNEL_IMAGE_BUUN_STACK_CUDA_REPOSITORY := env("KERNEL_IMAGE_BUUN_STACK_CUDA_REPOSITORY", "buun-stack-cuda-notebook") export JUPYTER_PROFILE_MINIMAL_ENABLED := env("JUPYTER_PROFILE_MINIMAL_ENABLED", "false") diff --git a/python-package/buunstack/secrets.py b/python-package/buunstack/secrets.py index ec627b9..b42e078 100644 --- a/python-package/buunstack/secrets.py +++ b/python-package/buunstack/secrets.py @@ -141,69 +141,42 @@ class SecretStore: except Exception: pass - # Token expired or invalid - provide detailed expiry information + # Token expired or invalid - provide helpful error message token_ttl = os.getenv("NOTEBOOK_VAULT_TOKEN_TTL", "24h") token_max_ttl = os.getenv("NOTEBOOK_VAULT_TOKEN_MAX_TTL", "168h") - # Try to get actual token information for better error message - token_info = None + # Try to get token info for more specific error message try: token_info = self.client.auth.token.lookup_self() - except Exception: - pass - - if token_info: data = token_info.get("data", {}) ttl = data.get("ttl", 0) - creation_time = data.get("creation_time", 0) - expire_time = data.get("expire_time", "unknown") renewable = data.get("renewable", False) + creation_time = data.get("creation_time", 0) - if ttl <= 0 and renewable: - # Token expired but was renewable - likely hit Max TTL + # Token expired but was renewable - likely hit Max TTL + if ttl <= 0 and renewable and creation_time: import datetime - try: - current_time = datetime.datetime.now() - if creation_time: - created_at = datetime.datetime.fromtimestamp(creation_time) - age_hours = (current_time - created_at).total_seconds() / 3600 - error_msg = ( - f"Vault Token Expired\n\n" - f"Your notebook's Vault token has reached its maximum lifetime and cannot be renewed.\n\n" - f"Token Details:\n" - f"• Created: {created_at.strftime('%Y-%m-%d %H:%M:%S')} ({age_hours:.1f}h ago)\n" - f"• TTL (renewal period): {token_ttl}\n" - f"• Max TTL (maximum lifetime): {token_max_ttl}\n" - f"• Expired at: {expire_time}\n\n" - f"How Token Renewal Works:\n" - f"• Your token is automatically renewed every time you use SecretStore\n" - f"• Each renewal extends the token for another {token_ttl}\n" - f"• However, tokens cannot be renewed beyond {token_max_ttl} from creation\n" - f"• Regular usage (within {token_ttl} intervals) keeps your token alive for up to {token_max_ttl}\n\n" - f"Solution:\n" - f"Please restart your notebook server to get a fresh token with a new {token_max_ttl} lifetime." - ) - else: - error_msg = ( - f"Vault Token Expired\n\n" - f"Your notebook's Vault token has expired and cannot be renewed.\n\n" - f"Token Settings:\n" - f"• TTL (renewal period): {token_ttl}\n" - f"• Max TTL (maximum lifetime): {token_max_ttl}\n\n" - f"Tip: Regular usage (within {token_ttl} intervals) keeps your token alive for up to {token_max_ttl}.\n\n" - f"Solution: Please restart your notebook server to get a fresh token." - ) - except Exception: - error_msg = ( - f"Vault Token Expired\n\n" - f"Your notebook's Vault token has expired and cannot be renewed.\n\n" - f"Token Settings:\n" - f"• TTL (renewal period): {token_ttl}\n" - f"• Max TTL (maximum lifetime): {token_max_ttl}\n\n" - f"Tip: Regular usage (within {token_ttl} intervals) keeps your token alive for up to {token_max_ttl}.\n\n" - f"Solution: Please restart your notebook server to get a fresh token." - ) + created_at = datetime.datetime.fromtimestamp(creation_time) + age_hours = ( + datetime.datetime.now() - created_at + ).total_seconds() / 3600 + + error_msg = ( + f"Vault Token Expired\n\n" + f"Your notebook's Vault token has reached its maximum lifetime and cannot be renewed.\n\n" + f"Token Details:\n" + f"• Created: {created_at.strftime('%Y-%m-%d %H:%M:%S')} ({age_hours:.1f}h ago)\n" + f"• TTL (renewal period): {token_ttl}\n" + f"• Max TTL (maximum lifetime): {token_max_ttl}\n\n" + f"How Token Renewal Works:\n" + f"• Your token is automatically renewed every time you use SecretStore\n" + f"• Each renewal extends the token for another {token_ttl}\n" + f"• However, tokens cannot be renewed beyond {token_max_ttl} from creation\n" + f"• Regular usage (within {token_ttl} intervals) keeps your token alive for up to {token_max_ttl}\n\n" + f"Solution:\n" + f"Please restart your notebook server to get a fresh token with a new {token_max_ttl} lifetime." + ) else: # Token invalid for other reasons error_msg = ( @@ -211,8 +184,8 @@ class SecretStore: "Your notebook's Vault token is invalid or corrupted.\n\n" "Solution: Please restart your notebook server to get a fresh token." ) - else: - # Cannot retrieve token info - generic message + except Exception: + # Cannot get token info - use generic message error_msg = ( f"Vault Authentication Failed\n\n" f"Your notebook's Vault token is invalid or has expired.\n\n"