106 lines
5.0 KiB
Bash
Executable File
106 lines
5.0 KiB
Bash
Executable File
#!/bin/bash
|
|
# WEVIA Snapshot Archiver v2 — Bash (robust, no Python deps)
|
|
# Usage: bash wevia-snap-archiver.sh <snap_id> <tag> <release_name>
|
|
# set -e
|
|
SNAP_ID="$1"
|
|
TAG="$2"
|
|
REL_NAME="$3"
|
|
HZ="xUcbvWMjkMgetuTU0llazUgB85jc7aQBLMhQ79NZ1Yf7j2TRF598DfNxoVrMnVOj"
|
|
GH="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"
|
|
LOG="/tmp/snap-$TAG.log"
|
|
|
|
log() { echo "[$(date +%H:%M:%S)] $1" | tee -a "$LOG"; }
|
|
|
|
log "=== ARCHIVING SNAPSHOT $SNAP_ID → $TAG ==="
|
|
|
|
# 1. Create temp server
|
|
log "Creating temp server..."
|
|
RESULT=$(curl -s -X POST "https://api.hetzner.cloud/v1/servers" \
|
|
-H "Authorization: Bearer $HZ" -H "Content-Type: application/json" \
|
|
-d "{\"name\":\"temp-$TAG\",\"server_type\":\"ccx23\",\"image\":$SNAP_ID,\"location\":\"hel1\",\"start_after_create\":true,\"ssh_keys\":[109134687],\"networks\":[12033255]}")
|
|
SRV_ID=$(echo "$RESULT" | python3 -c "import sys,json;print(json.load(sys.stdin)['server']['id'])" 2>/dev/null)
|
|
PUB_IP=$(echo "$RESULT" | python3 -c "import sys,json;print(json.load(sys.stdin)['server']['public_net']['ipv4']['ip'])" 2>/dev/null)
|
|
log "Server $SRV_ID IP=$PUB_IP"
|
|
|
|
# 2. Wait for creation (max 20 min)
|
|
log "Waiting for server creation..."
|
|
for i in $(seq 1 60); do
|
|
sleep 20
|
|
STATUS=$(curl -s "https://api.hetzner.cloud/v1/servers/$SRV_ID" -H "Authorization: Bearer $HZ" | python3 -c "import sys,json;print(json.load(sys.stdin)['server']['status'])" 2>/dev/null)
|
|
PROGRESS=$(curl -s "https://api.hetzner.cloud/v1/servers/$SRV_ID/actions?per_page=3" -H "Authorization: Bearer $HZ" | python3 -c "import sys,json;acts=json.load(sys.stdin)['actions'];print(all(a['status']=='success' for a in acts))" 2>/dev/null)
|
|
log " [$i] status=$STATUS done=$PROGRESS"
|
|
[ "$PROGRESS" = "True" ] && break
|
|
done
|
|
|
|
# 3. Rescue mode
|
|
log "Poweroff..."
|
|
curl -s -X POST "https://api.hetzner.cloud/v1/servers/$SRV_ID/actions/poweroff" -H "Authorization: Bearer $HZ" > /dev/null
|
|
sleep 25
|
|
|
|
log "Enable rescue..."
|
|
RESCUE_PW=$(curl -s -X POST "https://api.hetzner.cloud/v1/servers/$SRV_ID/actions/enable_rescue" \
|
|
-H "Authorization: Bearer $HZ" -H "Content-Type: application/json" \
|
|
-d '{"type":"linux64","ssh_keys":[109134687]}' | python3 -c "import sys,json;print(json.load(sys.stdin)['root_password'])" 2>/dev/null)
|
|
log "Rescue PW: $RESCUE_PW"
|
|
sleep 10
|
|
|
|
log "Poweron..."
|
|
curl -s -X POST "https://api.hetzner.cloud/v1/servers/$SRV_ID/actions/poweron" -H "Authorization: Bearer $HZ" > /dev/null
|
|
sleep 90
|
|
|
|
# 4. SSH to rescue
|
|
log "Connecting to rescue..."
|
|
CONNECTED=$(sshpass -p "$RESCUE_PW" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=20 root@$PUB_IP 'echo OK' 2>/dev/null)
|
|
if [ "$CONNECTED" != "OK" ]; then
|
|
log "RESCUE SSH FAILED — retrying in 60s..."
|
|
sleep 60
|
|
CONNECTED=$(sshpass -p "$RESCUE_PW" ssh -o StrictHostKeyChecking=no -o ConnectTimeout=20 root@$PUB_IP 'echo OK' 2>/dev/null)
|
|
fi
|
|
|
|
if [ "$CONNECTED" != "OK" ]; then
|
|
log "FATAL: Cannot connect to rescue. Cleaning up."
|
|
curl -s -X DELETE "https://api.hetzner.cloud/v1/servers/$SRV_ID" -H "Authorization: Bearer $HZ" > /dev/null
|
|
exit 1
|
|
fi
|
|
|
|
log "CONNECTED! Mounting disk..."
|
|
sshpass -p "$RESCUE_PW" ssh -o StrictHostKeyChecking=no root@$PUB_IP 'mkdir -p /mnt/data && mount /dev/sda1 /mnt/data' 2>/dev/null
|
|
|
|
# 5. Scan + archive
|
|
log "Scanning..."
|
|
CONTENT=$(sshpass -p "$RESCUE_PW" ssh -o StrictHostKeyChecking=no root@$PUB_IP 'du -sh /mnt/data/opt/* /mnt/data/var/www/* 2>/dev/null | sort -rh | head -20' 2>/dev/null)
|
|
log "$CONTENT"
|
|
|
|
VAULT="/opt/wevads/vault/$TAG"
|
|
mkdir -p "$VAULT"
|
|
|
|
for ARCH in "opt-all:opt/" "www-all:var/www/" "configs:var/spool/cron/ etc/ssh/ root/" "postgresql:var/lib/postgresql/"; do
|
|
NAME="${ARCH%%:*}"
|
|
PATHS="${ARCH#*:}"
|
|
log "Creating $NAME..."
|
|
sshpass -p "$RESCUE_PW" ssh -o StrictHostKeyChecking=no root@$PUB_IP "cd /mnt/data && tar czf /tmp/$NAME.tar.gz --exclude='node_modules' --exclude='.git' --exclude='vendor' --exclude='cache' --exclude='.cache' --exclude='.cursor-server' $PATHS 2>/dev/null; ls -lh /tmp/$NAME.tar.gz" 2>/dev/null
|
|
log "SCP $NAME to S204..."
|
|
sshpass -p "$RESCUE_PW" scp -o StrictHostKeyChecking=no "root@$PUB_IP:/tmp/$NAME.tar.gz" "$VAULT/" 2>/dev/null && log "$NAME OK" || log "$NAME FAILED"
|
|
done
|
|
|
|
log "Archives: $(ls -lh $VAULT/)"
|
|
|
|
# 6. GitHub release + upload
|
|
log "Creating GitHub release $TAG..."
|
|
REL_ID=$(curl -s -X POST "https://api.github.com/repos/Yacineutt/weval-archive/releases" \
|
|
-H "Authorization: token $GH" -H "Content-Type: application/json" \
|
|
-d "{\"tag_name\":\"$TAG\",\"name\":\"$REL_NAME\",\"body\":\"Hetzner snapshot $SNAP_ID\"}" | python3 -c "import sys,json;print(json.load(sys.stdin).get('id',''))" 2>/dev/null)
|
|
UPLOAD="https://uploads.github.com/repos/Yacineutt/weval-archive/releases/$REL_ID/assets"
|
|
|
|
for f in $VAULT/*.tar.gz; do
|
|
FNAME=$(basename "$f")
|
|
log "Uploading $FNAME..."
|
|
curl -s -H "Authorization: token $GH" -H "Content-Type: application/gzip" --data-binary @"$f" "$UPLOAD?name=$FNAME" > /dev/null 2>&1
|
|
log "$FNAME uploaded"
|
|
done
|
|
|
|
# 7. Cleanup
|
|
log "Deleting temp server $SRV_ID..."
|
|
curl -s -X DELETE "https://api.hetzner.cloud/v1/servers/$SRV_ID" -H "Authorization: Bearer $HZ" > /dev/null
|
|
log "=== SNAPSHOT $SNAP_ID ARCHIVED → $TAG ==="
|