166 lines
5.5 KiB
Bash
166 lines
5.5 KiB
Bash
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
# Gitea restore helper — restores a gitea-dump zip into a running Gitea deployment
|
|
#
|
|
# Prerequisites:
|
|
# - Gitea deployed on the target cluster (Helm chart via ArgoCD)
|
|
# - kubectl context pointing to the target cluster
|
|
# - A gitea-dump zip file (from gitea-backup.sh download or CronJob)
|
|
#
|
|
# Usage:
|
|
# ./scripts/gitea-restore.sh <gitea-dump-file.zip>
|
|
#
|
|
# What it does:
|
|
# 1. Scales Gitea down to 0
|
|
# 2. Restores the PostgreSQL database from gitea-db.sql
|
|
# 3. Restores Git repositories to the data PVC
|
|
# 4. Scales Gitea back up
|
|
|
|
NAMESPACE="gitea"
|
|
DEPLOYMENT="gitea"
|
|
PG_STATEFULSET="gitea-postgresql"
|
|
PVC="gitea-shared-storage"
|
|
HELPER_POD="gitea-restore-helper"
|
|
HELPER_IMAGE="alpine:3.20"
|
|
PG_USER="gitea"
|
|
PG_DB="gitea"
|
|
|
|
DUMP_FILE="${1:?Usage: $0 <gitea-dump-file.zip>}"
|
|
|
|
if [ ! -f "$DUMP_FILE" ]; then
|
|
echo "Error: file not found: $DUMP_FILE"
|
|
exit 1
|
|
fi
|
|
|
|
echo "=== Gitea Restore ==="
|
|
echo "Dump file: $DUMP_FILE"
|
|
echo ""
|
|
|
|
# --- Safety prompt ---
|
|
read -r -p "This will OVERWRITE the Gitea database and repositories on the current cluster. Continue? [y/N] " confirm
|
|
if [[ ! "$confirm" =~ ^[Yy]$ ]]; then
|
|
echo "Aborted."
|
|
exit 0
|
|
fi
|
|
|
|
cleanup() {
|
|
kubectl -n "$NAMESPACE" delete pod "$HELPER_POD" --ignore-not-found --grace-period=0 > /dev/null 2>&1 || true
|
|
}
|
|
trap cleanup EXIT
|
|
|
|
# --- Step 1: Extract dump locally ---
|
|
RESTORE_DIR=$(mktemp -d)
|
|
echo ""
|
|
echo "[1/5] Extracting dump..."
|
|
unzip -q "$DUMP_FILE" -d "$RESTORE_DIR"
|
|
|
|
# Detect SQL dump file (gitea-db.sql or gitea-dump-*.sql)
|
|
SQL_FILE=$(find "$RESTORE_DIR" -maxdepth 1 -name '*.sql' | head -1)
|
|
if [ -z "$SQL_FILE" ]; then
|
|
echo "Error: no .sql file found in dump."
|
|
rm -rf "$RESTORE_DIR"
|
|
exit 1
|
|
fi
|
|
|
|
# Detect repos — either a "repos" folder or gitea-repo.zip
|
|
if [ -d "$RESTORE_DIR/repos" ]; then
|
|
REPOS_SOURCE="dir"
|
|
REPOS_PATH="$RESTORE_DIR/repos"
|
|
elif [ -f "$RESTORE_DIR/gitea-repo.zip" ]; then
|
|
REPOS_SOURCE="zip"
|
|
REPOS_PATH="$RESTORE_DIR/gitea-repo.zip"
|
|
else
|
|
echo "Error: no repos/ directory or gitea-repo.zip found in dump."
|
|
rm -rf "$RESTORE_DIR"
|
|
exit 1
|
|
fi
|
|
|
|
echo " Found: $(basename "$SQL_FILE") ($(du -h "$SQL_FILE" | cut -f1))"
|
|
echo " Found: repos ($REPOS_SOURCE)"
|
|
[ -d "$RESTORE_DIR/data" ] && echo " Found: data/"
|
|
[ -f "$RESTORE_DIR/app.ini" ] && echo " Found: app.ini (managed by Helm, skipping)"
|
|
|
|
# --- Step 2: Scale down Gitea ---
|
|
echo ""
|
|
echo "[2/5] Scaling down Gitea..."
|
|
kubectl -n "$NAMESPACE" scale deployment "$DEPLOYMENT" --replicas=0
|
|
kubectl -n "$NAMESPACE" rollout status deployment "$DEPLOYMENT" --timeout=60s 2>/dev/null || true
|
|
echo " Waiting for pods to terminate..."
|
|
kubectl -n "$NAMESPACE" wait --for=delete pod -l app.kubernetes.io/name=gitea --timeout=120s 2>/dev/null || true
|
|
echo " Gitea is down."
|
|
|
|
# --- Step 3: Restore database ---
|
|
echo ""
|
|
echo "[3/5] Restoring database..."
|
|
# Drop and recreate to ensure clean state
|
|
kubectl -n "$NAMESPACE" exec "${PG_STATEFULSET}-0" -- \
|
|
psql -U "$PG_USER" -d postgres -c "DROP DATABASE IF EXISTS ${PG_DB};" 2>/dev/null
|
|
kubectl -n "$NAMESPACE" exec "${PG_STATEFULSET}-0" -- \
|
|
psql -U "$PG_USER" -d postgres -c "CREATE DATABASE ${PG_DB} OWNER ${PG_USER};" 2>/dev/null
|
|
kubectl -n "$NAMESPACE" exec -i "${PG_STATEFULSET}-0" -- \
|
|
psql -U "$PG_USER" -d "$PG_DB" < "$SQL_FILE" > /dev/null
|
|
echo " Database restored."
|
|
|
|
# --- Step 4: Restore repositories ---
|
|
echo ""
|
|
echo "[4/5] Restoring repositories..."
|
|
|
|
# Need a helper pod on the same node as the PVC (RWO)
|
|
cleanup
|
|
kubectl -n "$NAMESPACE" run "$HELPER_POD" --restart=Never \
|
|
--image="$HELPER_IMAGE" \
|
|
--overrides="{
|
|
\"spec\":{
|
|
\"containers\":[{
|
|
\"name\":\"$HELPER_POD\",
|
|
\"image\":\"$HELPER_IMAGE\",
|
|
\"command\":[\"sleep\",\"3600\"],
|
|
\"volumeMounts\":[{\"name\":\"data\",\"mountPath\":\"/data\"}]
|
|
}],
|
|
\"volumes\":[{
|
|
\"name\":\"data\",
|
|
\"persistentVolumeClaim\":{\"claimName\":\"$PVC\"}
|
|
}]
|
|
}
|
|
}" > /dev/null 2>&1
|
|
|
|
kubectl -n "$NAMESPACE" wait --for=condition=Ready "pod/$HELPER_POD" --timeout=120s > /dev/null 2>&1
|
|
|
|
# Clear existing repos
|
|
kubectl -n "$NAMESPACE" exec "$HELPER_POD" -- sh -c "rm -rf /data/gitea/repositories/*" 2>/dev/null || true
|
|
|
|
# Upload repos — tar via stdin since kubectl cp needs tar in the container
|
|
echo " Uploading repositories..."
|
|
if [ "$REPOS_SOURCE" = "dir" ]; then
|
|
# repos/ directory — tar and stream into the PVC
|
|
tar -cf - -C "$REPOS_PATH" . | kubectl -n "$NAMESPACE" exec -i "$HELPER_POD" -- sh -c "tar -xf - -C /data/gitea/repositories/"
|
|
else
|
|
# gitea-repo.zip — stream and extract
|
|
cat "$REPOS_PATH" | kubectl -n "$NAMESPACE" exec -i "$HELPER_POD" -- sh -c "cat > /tmp/gitea-repo.zip && unzip -q -o /tmp/gitea-repo.zip -d /data/gitea/repositories/ && rm /tmp/gitea-repo.zip"
|
|
fi
|
|
|
|
# Restore data/ directory (avatars, attachments, LFS, etc.) if present
|
|
if [ -d "$RESTORE_DIR/data" ]; then
|
|
echo " Uploading data (avatars, attachments, LFS)..."
|
|
tar -cf - -C "$RESTORE_DIR/data" . | kubectl -n "$NAMESPACE" exec -i "$HELPER_POD" -- sh -c "tar -xf - -C /data/gitea/"
|
|
fi
|
|
|
|
# Fix ownership
|
|
kubectl -n "$NAMESPACE" exec "$HELPER_POD" -- chown -R 1000:1000 /data/gitea/
|
|
echo " Repositories restored."
|
|
|
|
# --- Step 5: Scale back up ---
|
|
echo ""
|
|
echo "[5/5] Scaling Gitea back up..."
|
|
kubectl -n "$NAMESPACE" scale deployment "$DEPLOYMENT" --replicas=1
|
|
kubectl -n "$NAMESPACE" rollout status deployment "$DEPLOYMENT" --timeout=120s
|
|
|
|
# Cleanup temp dir
|
|
rm -rf "$RESTORE_DIR"
|
|
|
|
echo ""
|
|
echo "=== Restore complete ==="
|
|
echo "Gitea should be back online with restored data."
|
|
echo "Verify at your Gitea URL that repos and users are present."
|