mirror of
https://github.com/stellarshenson/stellars-jupyterhub-ds.git
synced 2026-03-07 21:50:28 +00:00
chore: use generic examples in volume-renamer help
This commit is contained in:
@@ -3,6 +3,6 @@ Additional tools and scripts that help with the hub maintenance
|
||||
- `certs-installer` - scripts for certificates installation in Root CA Truststore (Windows & Linux)
|
||||
- `docker_volume_backupper` - script that backs docker volumes (use regex for name)
|
||||
- `traefik-host-based-routing` - deployment template with local Traefik and self-signed certificates
|
||||
- `volume-migration` - scripts to rename volumes and migrate user data between usernames
|
||||
- `volume-renamer` - scripts to rename volumes and migrate user data between usernames
|
||||
|
||||
|
||||
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
# List all JupyterHub user volumes
|
||||
|
||||
echo "JupyterHub user volumes:"
|
||||
echo ""
|
||||
|
||||
docker volume ls --format '{{.Name}}' | grep -E '^jupyterlab-' | sort | while read -r VOL; do
|
||||
SIZE=$(docker run --rm -v "$VOL":/data alpine sh -c "du -sh /data 2>/dev/null | cut -f1" 2>/dev/null || echo "?")
|
||||
printf " %-50s %s\n" "$VOL" "$SIZE"
|
||||
done
|
||||
|
||||
echo ""
|
||||
@@ -1,76 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Migrate all JupyterHub user volumes from one username to another
|
||||
# Handles: home, workspace, cache volumes
|
||||
|
||||
set -e
|
||||
|
||||
SCRIPT_DIR="$(dirname "$0")"
|
||||
|
||||
if [ -z "$1" ] || [ -z "$2" ]; then
|
||||
echo "Usage: $0 <old-username> <new-username> [--delete-source]"
|
||||
echo ""
|
||||
echo "Migrates JupyterHub user volumes:"
|
||||
echo " - jupyterlab-{username}_home"
|
||||
echo " - jupyterlab-{username}_workspace"
|
||||
echo " - jupyterlab-{username}_cache"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --delete-source Delete source volumes after successful copy"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " $0 john john.doe"
|
||||
echo " $0 john john.doe --delete-source"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OLD_USER="$1"
|
||||
NEW_USER="$2"
|
||||
DELETE_FLAG=""
|
||||
|
||||
if [ "$3" = "--delete-source" ]; then
|
||||
DELETE_FLAG="--delete-source"
|
||||
fi
|
||||
|
||||
VOLUME_SUFFIXES="home workspace cache"
|
||||
|
||||
echo "Migrating volumes for user: $OLD_USER -> $NEW_USER"
|
||||
echo ""
|
||||
|
||||
# Check which volumes exist
|
||||
VOLUMES_TO_MIGRATE=""
|
||||
for SUFFIX in $VOLUME_SUFFIXES; do
|
||||
SOURCE="jupyterlab-${OLD_USER}_${SUFFIX}"
|
||||
if docker volume inspect "$SOURCE" >/dev/null 2>&1; then
|
||||
VOLUMES_TO_MIGRATE="$VOLUMES_TO_MIGRATE $SUFFIX"
|
||||
echo " Found: $SOURCE"
|
||||
else
|
||||
echo " Skip: $SOURCE (not found)"
|
||||
fi
|
||||
done
|
||||
|
||||
if [ -z "$VOLUMES_TO_MIGRATE" ]; then
|
||||
echo ""
|
||||
echo "No volumes found for user: $OLD_USER"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
read -p "Proceed with migration? [y/N] " CONFIRM
|
||||
if [ "$CONFIRM" != "y" ] && [ "$CONFIRM" != "Y" ]; then
|
||||
echo "Aborted"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Migrate each volume
|
||||
for SUFFIX in $VOLUMES_TO_MIGRATE; do
|
||||
SOURCE="jupyterlab-${OLD_USER}_${SUFFIX}"
|
||||
TARGET="jupyterlab-${NEW_USER}_${SUFFIX}"
|
||||
|
||||
echo "--- Migrating $SUFFIX volume ---"
|
||||
"$SCRIPT_DIR/rename-volume.sh" "$SOURCE" "$TARGET" $DELETE_FLAG
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo "User volume migration complete: $OLD_USER -> $NEW_USER"
|
||||
@@ -1,74 +0,0 @@
|
||||
#!/bin/bash
|
||||
# Rename a Docker volume by copying data to a new volume
|
||||
|
||||
set -e
|
||||
|
||||
if [ -z "$1" ] || [ -z "$2" ]; then
|
||||
echo "Usage: $0 <source-volume> <target-volume> [--delete-source]"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo " --delete-source Delete source volume after successful copy"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " $0 jupyterlab-olduser_home jupyterlab-newuser_home"
|
||||
echo " $0 jupyterlab-olduser_home jupyterlab-newuser_home --delete-source"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
SOURCE="$1"
|
||||
TARGET="$2"
|
||||
DELETE_SOURCE=false
|
||||
|
||||
if [ "$3" = "--delete-source" ]; then
|
||||
DELETE_SOURCE=true
|
||||
fi
|
||||
|
||||
# Check source volume exists
|
||||
if ! docker volume inspect "$SOURCE" >/dev/null 2>&1; then
|
||||
echo "ERROR: Source volume '$SOURCE' does not exist"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check target volume doesn't exist
|
||||
if docker volume inspect "$TARGET" >/dev/null 2>&1; then
|
||||
echo "ERROR: Target volume '$TARGET' already exists"
|
||||
echo "Delete it first or choose a different name"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Renaming volume:"
|
||||
echo " Source: $SOURCE"
|
||||
echo " Target: $TARGET"
|
||||
echo ""
|
||||
|
||||
# Create target volume
|
||||
echo "Creating target volume..."
|
||||
docker volume create "$TARGET"
|
||||
|
||||
# Copy data using alpine container
|
||||
echo "Copying data..."
|
||||
CONTAINER_NAME="volume_copy_$(date +%s)_$$"
|
||||
|
||||
if docker run --rm \
|
||||
--name "$CONTAINER_NAME" \
|
||||
-v "$SOURCE":/source:ro \
|
||||
-v "$TARGET":/target \
|
||||
alpine \
|
||||
sh -c "cp -a /source/. /target/"; then
|
||||
|
||||
echo "Data copied successfully"
|
||||
else
|
||||
echo "ERROR: Copy failed"
|
||||
docker volume rm "$TARGET" 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Delete source if requested
|
||||
if [ "$DELETE_SOURCE" = true ]; then
|
||||
echo "Deleting source volume..."
|
||||
docker volume rm "$SOURCE"
|
||||
echo "Source volume deleted"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Volume rename complete: $SOURCE -> $TARGET"
|
||||
148
extra/volume-renamer/rename-user-volumes.sh
Executable file
148
extra/volume-renamer/rename-user-volumes.sh
Executable file
@@ -0,0 +1,148 @@
|
||||
#!/bin/bash
|
||||
# Rename Docker volumes from one user pattern to another
|
||||
# Handles Docker's dot-to-hex encoding (. becomes -2e)
|
||||
|
||||
set -e
|
||||
|
||||
show_help() {
|
||||
cat << EOF
|
||||
Usage: $0 [--dry-run] [--keep-orig] <source-pattern> <target-username>
|
||||
|
||||
Rename Docker volumes from one user to another (. encoded as -2e).
|
||||
|
||||
--dry-run Show mappings without changes
|
||||
--keep-orig Keep original volumes
|
||||
|
||||
Example: $0 --dry-run oldnick first.last
|
||||
jupyterlab-oldnick_home -> jupyterlab-first-2elast_home
|
||||
EOF
|
||||
exit 0
|
||||
}
|
||||
|
||||
# Parse arguments
|
||||
DRY_RUN=false
|
||||
KEEP_ORIG=false
|
||||
POSITIONAL=()
|
||||
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
--dry-run)
|
||||
DRY_RUN=true
|
||||
shift
|
||||
;;
|
||||
--keep-orig)
|
||||
KEEP_ORIG=true
|
||||
shift
|
||||
;;
|
||||
-h|--help)
|
||||
show_help
|
||||
;;
|
||||
-*)
|
||||
echo "Unknown option: $1"
|
||||
echo "Use --help for usage"
|
||||
exit 1
|
||||
;;
|
||||
*)
|
||||
POSITIONAL+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Check required arguments
|
||||
if [ ${#POSITIONAL[@]} -lt 2 ]; then
|
||||
show_help
|
||||
fi
|
||||
|
||||
SOURCE_PATTERN="${POSITIONAL[0]}"
|
||||
TARGET_USER="${POSITIONAL[1]}"
|
||||
|
||||
# Encode dots as -2e for Docker volume names
|
||||
TARGET_ENCODED=$(echo "$TARGET_USER" | sed 's/\./-2e/g')
|
||||
|
||||
# Find matching volumes
|
||||
VOLUMES=$(docker volume ls --format '{{.Name}}' | grep -E "jupyterlab-${SOURCE_PATTERN}[_-]" || true)
|
||||
|
||||
if [ -z "$VOLUMES" ]; then
|
||||
echo "No volumes found matching pattern: jupyterlab-${SOURCE_PATTERN}[_-]*"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Volume rename mappings:"
|
||||
echo ""
|
||||
|
||||
# Build mapping list
|
||||
declare -a SOURCES
|
||||
declare -a TARGETS
|
||||
|
||||
while IFS= read -r VOL; do
|
||||
# Replace source pattern with encoded target
|
||||
NEW_VOL=$(echo "$VOL" | sed "s/jupyterlab-${SOURCE_PATTERN}/jupyterlab-${TARGET_ENCODED}/")
|
||||
SOURCES+=("$VOL")
|
||||
TARGETS+=("$NEW_VOL")
|
||||
echo " $VOL"
|
||||
echo " -> $NEW_VOL"
|
||||
echo ""
|
||||
done <<< "$VOLUMES"
|
||||
|
||||
if [ "$DRY_RUN" = true ]; then
|
||||
echo "[DRY RUN] No changes made"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "---"
|
||||
if [ "$KEEP_ORIG" = true ]; then
|
||||
echo "Mode: copy (original volumes will be kept)"
|
||||
else
|
||||
echo "Mode: move (original volumes will be deleted)"
|
||||
fi
|
||||
echo ""
|
||||
|
||||
read -p "Proceed with rename? [y/N] " CONFIRM
|
||||
if [ "$CONFIRM" != "y" ] && [ "$CONFIRM" != "Y" ]; then
|
||||
echo "Aborted"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo ""
|
||||
|
||||
# Perform renames
|
||||
for i in "${!SOURCES[@]}"; do
|
||||
SOURCE="${SOURCES[$i]}"
|
||||
TARGET="${TARGETS[$i]}"
|
||||
|
||||
echo "Renaming: $SOURCE -> $TARGET"
|
||||
|
||||
# Check target doesn't exist
|
||||
if docker volume inspect "$TARGET" >/dev/null 2>&1; then
|
||||
echo " ERROR: Target volume already exists, skipping"
|
||||
continue
|
||||
fi
|
||||
|
||||
# Create target volume
|
||||
docker volume create "$TARGET" >/dev/null
|
||||
|
||||
# Copy data
|
||||
CONTAINER_NAME="vol_copy_$(date +%s)_$$_$i"
|
||||
if docker run --rm \
|
||||
--name "$CONTAINER_NAME" \
|
||||
-v "$SOURCE":/source:ro \
|
||||
-v "$TARGET":/target \
|
||||
alpine \
|
||||
sh -c "cp -a /source/. /target/" 2>/dev/null; then
|
||||
|
||||
echo " Copied successfully"
|
||||
|
||||
# Remove original if not keeping
|
||||
if [ "$KEEP_ORIG" = false ]; then
|
||||
docker volume rm "$SOURCE" >/dev/null
|
||||
echo " Original removed"
|
||||
fi
|
||||
else
|
||||
echo " ERROR: Copy failed"
|
||||
docker volume rm "$TARGET" 2>/dev/null || true
|
||||
fi
|
||||
done
|
||||
|
||||
echo ""
|
||||
echo "Done"
|
||||
Reference in New Issue
Block a user