Files
scripts/migrate.sh
2025-11-21 04:08:16 +00:00

107 lines
3.9 KiB
Bash

#!/bin/bash
# Usage: curl -sL https://url/migrate.sh | bash -s -- <LOCAL_ID_OR_VOL> <USER@REMOTE_IP> <REMOTE_ID_OR_VOL> [SSH_KEY_PATH]
LOCAL_SEARCH="$1"
DEST_CONN="$2"
REMOTE_SEARCH="$3"
SSH_KEY_PATH="$4"
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'
# --- 1. SSH SETUP ---
SSH_BASE_OPTS="-o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=no"
if [ -n "$SSH_KEY_PATH" ]; then
SSH_CMD="ssh -i $SSH_KEY_PATH $SSH_BASE_OPTS"
RSYNC_SH="ssh -i $SSH_KEY_PATH $SSH_BASE_OPTS"
else
SSH_CMD="ssh $SSH_BASE_OPTS"
RSYNC_SH="ssh $SSH_BASE_OPTS"
fi
# --- 2. DETECT MODE (Container vs Volume) ---
MODE="container"
# Check if input is a valid volume
if docker volume inspect "$LOCAL_SEARCH" >/dev/null 2>&1; then
MODE="volume"
echo -e "${YELLOW}Info: Detected input as Volume Name, not Container.${NC}"
fi
# --- 3. EXECUTE MIGRATION ---
if [ "$MODE" == "volume" ]; then
# === VOLUME MODE ===
L_VOL="$LOCAL_SEARCH"
R_VOL="$REMOTE_SEARCH"
echo -e " Source Volume: ${GREEN}$L_VOL${NC}"
echo -e " Target Volume: ${GREEN}$R_VOL${NC}"
# Get Paths
REAL_L_PATH=$(docker volume inspect --format '{{.Mountpoint}}' "$L_VOL")
REAL_R_PATH=$($SSH_CMD "$DEST_CONN" "docker volume inspect --format '{{.Mountpoint}}' $R_VOL")
if [ -z "$REAL_R_PATH" ]; then
echo -e "${RED}Error: Remote volume '$R_VOL' not found on Server B.${NC}"
exit 1
fi
# Attempt to stop any containers using these volumes (Safety)
echo -e "\n🛑 Stopping any containers using these volumes..."
# Local
L_CONTAINER=$(docker ps -q --filter volume="$L_VOL")
[ -n "$L_CONTAINER" ] && docker stop $L_CONTAINER
# Remote
R_CONTAINER=$($SSH_CMD "$DEST_CONN" "docker ps -q --filter volume='$R_VOL'")
[ -n "$R_CONTAINER" ] && $SSH_CMD "$DEST_CONN" "docker stop $R_CONTAINER"
# Sync
echo -e "🚀 Transferring data..."
rsync -az --info=progress2 -e "$RSYNC_SH" "${REAL_L_PATH}/" "$DEST_CONN:${REAL_R_PATH}/"
echo -e "\n✅ Volume Transfer Complete!"
[ -n "$L_CONTAINER" ] && docker start $L_CONTAINER
[ -n "$R_CONTAINER" ] && $SSH_CMD "$DEST_CONN" "docker start $R_CONTAINER"
else
# === CONTAINER MODE (Original Logic) ===
L_ID=$(docker ps -aq --filter "name=${LOCAL_SEARCH}" | head -n 1)
R_ID=$($SSH_CMD "$DEST_CONN" "docker ps -aq --filter 'name=${REMOTE_SEARCH}' | head -n 1")
if [ -z "$L_ID" ]; then echo -e "${RED}Error: Container/Volume '$LOCAL_SEARCH' not found.${NC}"; exit 1; fi
if [ -z "$R_ID" ]; then echo -e "${RED}Error: Remote container '$REMOTE_SEARCH' not found.${NC}"; exit 1; fi
echo -e "🛑 Stopping containers..."
docker stop "$L_ID"
$SSH_CMD "$DEST_CONN" "docker stop $R_ID"
echo -e "📦 Syncing volumes..."
L_MOUNTS=$(docker inspect -f '{{range .Mounts}}{{.Type}}|{{.Source}}|{{.Destination}} {{end}}' "$L_ID")
for MOUNT in $L_MOUNTS; do
TYPE=$(echo "$MOUNT" | cut -d'|' -f1)
L_PATH=$(echo "$MOUNT" | cut -d'|' -f2)
TARGET=$(echo "$MOUNT" | cut -d'|' -f3)
if [[ "$TARGET" == *".sock" ]] || [[ "$TARGET" == *".conf" ]]; then continue; fi
R_PATH_RAW=$($SSH_CMD "$DEST_CONN" "docker inspect -f '{{range .Mounts}}{{if eq .Destination \"$TARGET\"}}{{.Source}}{{end}}{{end}}' $R_ID")
if [ -n "$R_PATH_RAW" ]; then
if [ "$TYPE" == "volume" ]; then
REAL_L=$(docker volume inspect --format '{{.Mountpoint}}' "$L_PATH")
REAL_R=$($SSH_CMD "$DEST_CONN" "docker volume inspect --format '{{.Mountpoint}}' $R_PATH_RAW" 2>/dev/null || echo $R_PATH_RAW)
else
REAL_L="$L_PATH"; REAL_R="$R_PATH_RAW"
fi
rsync -az --info=progress2 -e "$RSYNC_SH" "${REAL_L}/" "$DEST_CONN:${REAL_R}/"
fi
done
echo -e "✅ Container Transfer Complete!"
docker start "$L_ID"
$SSH_CMD "$DEST_CONN" "docker start $R_ID"
fi