mirror of
https://github.com/stellarshenson/stellars-jupyterhub-ds.git
synced 2026-03-07 21:50:28 +00:00
feat: add optional volume descriptions for UI
Added VOLUME_DESCRIPTIONS config dict allowing optional user-friendly
descriptions for volumes shown in management UI.
Changes:
- Added VOLUME_DESCRIPTIONS dict in jupyterhub_config.py (optional, co-defined
with DOCKER_SPAWNER_VOLUMES)
- Exposed volume_descriptions via c.JupyterHub.template_vars
- Updated home.html to conditionally display descriptions if defined
- If volume not in VOLUME_DESCRIPTIONS, no description shown (UI remains agnostic)
Configuration example:
VOLUME_DESCRIPTIONS = {
'home': 'User home directory files, configurations',
'workspace': 'Project files, notebooks, code',
'cache': 'Temporary files, pip cache, conda cache'
}
Documentation:
- Updated .claude/CLAUDE.md with Manage Volumes implementation details
- Simplified Restart Server section for consistency
- Added template variables to doc/ui-template-customization.md
UI now shows volume name, Docker volume path, and optional description
(if configured). Fully backward compatible - descriptions are optional.
This commit is contained in:
@@ -258,52 +258,52 @@ The startup script will automatically read this list and create missing groups.
|
||||
|
||||
## User Self-Service Features
|
||||
|
||||
The platform provides two self-service features accessible from the user control panel (`/hub/home`):
|
||||
### Manage Volumes
|
||||
|
||||
### Reset Home Volume
|
||||
|
||||
**Purpose**: Allows users to delete their home directory volume and start fresh with a clean environment.
|
||||
Selective volume reset allowing users to delete chosen persistent volumes (home, workspace, cache).
|
||||
|
||||
**Requirements**:
|
||||
- User's JupyterLab server must be stopped
|
||||
- Volume `jupyterlab-{username}_home` must exist
|
||||
- Volumes dynamically read from `DOCKER_SPAWNER_VOLUMES` config
|
||||
|
||||
**Implementation**:
|
||||
- API Endpoint: `DELETE /hub/api/users/{username}/reset-home-volume`
|
||||
- Handler: `services/jupyterhub/conf/bin/custom_handlers.py::ResetHomeVolumeHandler`
|
||||
- Uses Docker API to safely remove the volume
|
||||
- Only affects home volume - workspace and cache volumes are preserved
|
||||
- API Endpoint: `DELETE /hub/api/users/{username}/manage-volumes`
|
||||
- Handler: `services/jupyterhub/conf/bin/custom_handlers.py::ManageVolumesHandler`
|
||||
- Template: `services/jupyterhub/templates/home.html` with dynamic checkbox generation
|
||||
- Volume list extracted via `get_user_volume_suffixes(DOCKER_SPAWNER_VOLUMES)`
|
||||
- Optional descriptions from `VOLUME_DESCRIPTIONS` dict (config file)
|
||||
- Validates requested volumes against `USER_VOLUME_SUFFIXES`
|
||||
|
||||
**Permissions**:
|
||||
- Users can reset their own home volume
|
||||
- Admins can reset any user's home volume
|
||||
- Enforced via `@admin_or_self` decorator
|
||||
**Configuration** (`config/jupyterhub_config.py`):
|
||||
```python
|
||||
DOCKER_SPAWNER_VOLUMES = {
|
||||
"jupyterlab-{username}_home": "/home",
|
||||
"jupyterlab-{username}_workspace": DOCKER_NOTEBOOK_DIR,
|
||||
"jupyterlab-{username}_cache": "/home/lab/.cache",
|
||||
}
|
||||
|
||||
VOLUME_DESCRIPTIONS = {
|
||||
'home': 'User home directory files, configurations',
|
||||
'workspace': 'Project files, notebooks, code',
|
||||
'cache': 'Temporary files, pip cache, conda cache'
|
||||
}
|
||||
```
|
||||
|
||||
**Permissions**: Users can manage own volumes, admins can manage any user's volumes
|
||||
|
||||
### Restart Server
|
||||
|
||||
**Purpose**: Provides one-click Docker container restart without recreating the container.
|
||||
One-click Docker container restart without recreation. Preserves volumes and configuration.
|
||||
|
||||
**Requirements**:
|
||||
- User's JupyterLab server must be running
|
||||
- Container `jupyterlab-{username}` must exist
|
||||
**Requirements**: User's JupyterLab server must be running
|
||||
|
||||
**Implementation**:
|
||||
- API Endpoint: `POST /hub/api/users/{username}/restart-server`
|
||||
- Handler: `services/jupyterhub/conf/bin/custom_handlers.py::RestartServerHandler`
|
||||
- Uses Docker's native `container.restart(timeout=10)` method
|
||||
- Preserves container identity, volumes, and configuration
|
||||
- Does NOT recreate container (unlike JupyterHub's stop/spawn cycle)
|
||||
|
||||
**Permissions**:
|
||||
- Users can restart their own server
|
||||
- Admins can restart any user's server
|
||||
- Enforced via `@admin_or_self` decorator
|
||||
|
||||
**UI Location**:
|
||||
- Custom template: `services/jupyterhub/templates/home.html`
|
||||
- Reset button visible when server is stopped
|
||||
- Restart button visible when server is running
|
||||
- Both include confirmation modals with warnings
|
||||
**Permissions**: Users can restart own server, admins can restart any user's server
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
|
||||
@@ -120,6 +120,14 @@ DOCKER_SPAWNER_VOLUMES = {
|
||||
"jupyterhub_shared": "/mnt/shared" # shared drive across hub
|
||||
}
|
||||
|
||||
# Optional descriptions for user volumes (shown in UI)
|
||||
# If a volume suffix is not listed here, no description will be shown
|
||||
VOLUME_DESCRIPTIONS = {
|
||||
'home': 'User home directory files, configurations',
|
||||
'workspace': 'Project files, notebooks, code',
|
||||
'cache': 'Temporary files, pip cache, conda cache'
|
||||
}
|
||||
|
||||
# Helper function to extract user-specific volume suffixes
|
||||
def get_user_volume_suffixes(volumes_dict):
|
||||
"""Extract volume suffixes from volumes dict that follow jupyterlab-{username}_<suffix> pattern"""
|
||||
@@ -145,9 +153,10 @@ if c is not None:
|
||||
# Set volumes from constant
|
||||
c.DockerSpawner.volumes = DOCKER_SPAWNER_VOLUMES
|
||||
|
||||
# Make volume suffixes available to templates
|
||||
# Make volume suffixes and descriptions available to templates
|
||||
c.JupyterHub.template_vars = {
|
||||
'user_volume_suffixes': USER_VOLUME_SUFFIXES
|
||||
'user_volume_suffixes': USER_VOLUME_SUFFIXES,
|
||||
'volume_descriptions': VOLUME_DESCRIPTIONS
|
||||
}
|
||||
|
||||
# Built-in groups that cannot be deleted (auto-recreated if missing)
|
||||
|
||||
@@ -8,6 +8,10 @@ JupyterHub templates extended using Jinja2 to add custom UI features (server res
|
||||
- Changes require Docker rebuild with `--no-cache` flag
|
||||
- JupyterHub 5.4.2 uses Bootstrap 5 (not Bootstrap 4)
|
||||
|
||||
**Template Variables** (via `c.JupyterHub.template_vars`):
|
||||
- `user_volume_suffixes`: List of volume suffixes from `DOCKER_SPAWNER_VOLUMES`
|
||||
- `volume_descriptions`: Optional dict mapping suffixes to descriptions
|
||||
|
||||
**JavaScript Integration**:
|
||||
All custom JavaScript wrapped in RequireJS to ensure library loading:
|
||||
```javascript
|
||||
|
||||
@@ -146,6 +146,10 @@
|
||||
<strong>{{ volume_suffix }}</strong>
|
||||
<br>
|
||||
<code class="text-muted">jupyterlab-{{ user.name }}_{{ volume_suffix }}</code>
|
||||
{% if volume_descriptions and volume_suffix in volume_descriptions %}
|
||||
<br>
|
||||
<small class="text-muted">{{ volume_descriptions[volume_suffix] }}</small>
|
||||
{% endif %}
|
||||
</label>
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
Reference in New Issue
Block a user