Update migrate.sh
This commit is contained in:
152
migrate.sh
152
migrate.sh
@@ -1,5 +1,5 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# Usage: curl -sL https://url/migrate.sh | bash -s -- <LOCAL_ID> <USER@REMOTE_IP> <REMOTE_ID> [SSH_KEY_PATH]
|
# 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"
|
LOCAL_SEARCH="$1"
|
||||||
DEST_CONN="$2"
|
DEST_CONN="$2"
|
||||||
@@ -11,102 +11,96 @@ GREEN='\033[0;32m'
|
|||||||
YELLOW='\033[1;33m'
|
YELLOW='\033[1;33m'
|
||||||
NC='\033[0m'
|
NC='\033[0m'
|
||||||
|
|
||||||
# --- 1. CONFIGURE SSH ---
|
# --- 1. SSH SETUP ---
|
||||||
# Base options to avoid "Are you sure?" prompts and timeouts
|
|
||||||
SSH_BASE_OPTS="-o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=no"
|
SSH_BASE_OPTS="-o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=no"
|
||||||
|
|
||||||
if [ -n "$SSH_KEY_PATH" ]; then
|
if [ -n "$SSH_KEY_PATH" ]; then
|
||||||
echo -e "🔑 Using Identity File: ${YELLOW}$SSH_KEY_PATH${NC}"
|
|
||||||
if [ ! -f "$SSH_KEY_PATH" ]; then
|
|
||||||
echo -e "${RED}Error: Key file not found at $SSH_KEY_PATH${NC}"
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
# Create the SSH command with the key
|
|
||||||
SSH_CMD="ssh -i $SSH_KEY_PATH $SSH_BASE_OPTS"
|
SSH_CMD="ssh -i $SSH_KEY_PATH $SSH_BASE_OPTS"
|
||||||
# Create the RSYNC generic shell command
|
|
||||||
RSYNC_SH="ssh -i $SSH_KEY_PATH $SSH_BASE_OPTS"
|
RSYNC_SH="ssh -i $SSH_KEY_PATH $SSH_BASE_OPTS"
|
||||||
else
|
else
|
||||||
SSH_CMD="ssh $SSH_BASE_OPTS"
|
SSH_CMD="ssh $SSH_BASE_OPTS"
|
||||||
RSYNC_SH="ssh $SSH_BASE_OPTS"
|
RSYNC_SH="ssh $SSH_BASE_OPTS"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# --- 2. PRE-FLIGHT CHECKS ---
|
# --- 2. DETECT MODE (Container vs Volume) ---
|
||||||
if [ -z "$REMOTE_SEARCH" ]; then
|
MODE="container"
|
||||||
echo -e "${RED}Usage: $0 <Local_ID> <User@Remote_IP> <Remote_ID> [Key_Path]${NC}"
|
# Check if input is a valid volume
|
||||||
exit 1
|
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
|
fi
|
||||||
|
|
||||||
echo -e "📡 Connecting to $DEST_CONN..."
|
# --- 3. EXECUTE MIGRATION ---
|
||||||
if ! $SSH_CMD "$DEST_CONN" echo "SSH Connection OK" >/dev/null 2>&1; then
|
|
||||||
echo -e "${RED}Error: Cannot connect to $DEST_CONN.${NC}"
|
|
||||||
echo -e " Check your IP, User, or SSH Key permissions."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
# --- 3. IDENTIFY CONTAINERS ---
|
if [ "$MODE" == "volume" ]; then
|
||||||
echo -e "\n🔍 Identifying Containers..."
|
# === VOLUME MODE ===
|
||||||
|
L_VOL="$LOCAL_SEARCH"
|
||||||
|
R_VOL="$REMOTE_SEARCH"
|
||||||
|
|
||||||
# Local
|
echo -e " Source Volume: ${GREEN}$L_VOL${NC}"
|
||||||
L_ID=$(docker ps -aq --filter "name=${LOCAL_SEARCH}" | head -n 1)
|
echo -e " Target Volume: ${GREEN}$R_VOL${NC}"
|
||||||
L_NAME=$(docker ps -a --filter "name=${LOCAL_SEARCH}" --format "{{.Names}}" | head -n 1)
|
|
||||||
|
|
||||||
# Remote (via SSH)
|
# Get Paths
|
||||||
R_ID=$($SSH_CMD "$DEST_CONN" "docker ps -aq --filter 'name=${REMOTE_SEARCH}' | head -n 1")
|
REAL_L_PATH=$(docker volume inspect --format '{{.Mountpoint}}' "$L_VOL")
|
||||||
R_NAME=$($SSH_CMD "$DEST_CONN" "docker ps -a --filter 'name=${REMOTE_SEARCH}' --format '{{.Names}}' | head -n 1")
|
REAL_R_PATH=$($SSH_CMD "$DEST_CONN" "docker volume inspect --format '{{.Mountpoint}}' $R_VOL")
|
||||||
|
|
||||||
if [ -z "$L_ID" ]; then echo -e "${RED}Local container '${LOCAL_SEARCH}' not found.${NC}"; exit 1; fi
|
if [ -z "$REAL_R_PATH" ]; then
|
||||||
if [ -z "$R_ID" ]; then echo -e "${RED}Remote container '${REMOTE_SEARCH}' not found on Server B.${NC}"; exit 1; fi
|
echo -e "${RED}Error: Remote volume '$R_VOL' not found on Server B.${NC}"
|
||||||
|
exit 1
|
||||||
echo -e " Source: ${GREEN}$L_NAME${NC} ($L_ID)"
|
|
||||||
echo -e " Target: ${GREEN}$R_NAME${NC} ($R_ID)"
|
|
||||||
|
|
||||||
# --- 4. STOP CONTAINERS ---
|
|
||||||
echo -e "\n🛑 ${RED}Stopping containers to freeze state...${NC}"
|
|
||||||
docker stop "$L_ID"
|
|
||||||
$SSH_CMD "$DEST_CONN" "docker stop $R_ID"
|
|
||||||
|
|
||||||
# --- 5. MIGRATE VOLUMES ---
|
|
||||||
echo -e "\n📦 Mapping & Syncing Volumes..."
|
|
||||||
|
|
||||||
# Get Mounts: Type|SourcePath|InternalPath
|
|
||||||
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)
|
|
||||||
INTERNAL_TARGET=$(echo "$MOUNT" | cut -d'|' -f3)
|
|
||||||
|
|
||||||
# Skip system mounts
|
|
||||||
if [[ "$INTERNAL_TARGET" == *".sock" ]] || [[ "$INTERNAL_TARGET" == *".conf" ]]; then continue; fi
|
|
||||||
|
|
||||||
echo -e "\n 🔄 Volume: ${YELLOW}$INTERNAL_TARGET${NC}"
|
|
||||||
|
|
||||||
# Find corresponding path on Remote
|
|
||||||
R_PATH_RAW=$($SSH_CMD "$DEST_CONN" "docker inspect -f '{{range .Mounts}}{{if eq .Destination \"$INTERNAL_TARGET\"}}{{.Source}}{{end}}{{end}}' $R_ID")
|
|
||||||
|
|
||||||
if [ -z "$R_PATH_RAW" ]; then
|
|
||||||
echo -e " ${RED}Skipping: Target has no matching volume.${NC}"
|
|
||||||
continue
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Resolve Real Paths
|
# Attempt to stop any containers using these volumes (Safety)
|
||||||
if [ "$TYPE" == "volume" ]; then
|
echo -e "\n🛑 Stopping any containers using these volumes..."
|
||||||
REAL_L_PATH=$(docker volume inspect --format '{{.Mountpoint}}' "$L_PATH")
|
# Local
|
||||||
REAL_R_PATH=$($SSH_CMD "$DEST_CONN" "docker volume inspect --format '{{.Mountpoint}}' $R_PATH_RAW 2>/dev/null || echo $R_PATH_RAW")
|
L_CONTAINER=$(docker ps -q --filter volume="$L_VOL")
|
||||||
else
|
[ -n "$L_CONTAINER" ] && docker stop $L_CONTAINER
|
||||||
REAL_L_PATH="$L_PATH"
|
# Remote
|
||||||
REAL_R_PATH="$R_PATH_RAW"
|
R_CONTAINER=$($SSH_CMD "$DEST_CONN" "docker ps -q --filter volume='$R_VOL'")
|
||||||
fi
|
[ -n "$R_CONTAINER" ] && $SSH_CMD "$DEST_CONN" "docker stop $R_CONTAINER"
|
||||||
|
|
||||||
# RSYNC EXECUTION
|
# Sync
|
||||||
echo " 🚀 Transferring data..."
|
echo -e "🚀 Transferring data..."
|
||||||
rsync -az --info=progress2 -e "$RSYNC_SH" "${REAL_L_PATH}/" "$DEST_CONN:${REAL_R_PATH}/"
|
rsync -az --info=progress2 -e "$RSYNC_SH" "${REAL_L_PATH}/" "$DEST_CONN:${REAL_R_PATH}/"
|
||||||
done
|
|
||||||
|
|
||||||
# --- 6. RESTART ---
|
echo -e "\n✅ Volume Transfer Complete!"
|
||||||
echo -e "\n✅ Transfer Complete."
|
[ -n "$L_CONTAINER" ] && docker start $L_CONTAINER
|
||||||
echo -e " Restarting containers..."
|
[ -n "$R_CONTAINER" ] && $SSH_CMD "$DEST_CONN" "docker start $R_CONTAINER"
|
||||||
$SSH_CMD "$DEST_CONN" "docker start $R_ID"
|
|
||||||
docker start "$L_ID"
|
|
||||||
|
|
||||||
echo -e "\n🎉 ${GREEN}Success!${NC}"
|
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
|
||||||
|
|||||||
Reference in New Issue
Block a user