#!/usr/bin/env bash set -euo pipefail # ================================================== # REQUIRED ENV VARS # ================================================== ENVIRONMENT="${ENVIRONMENT:-}" if [[ -z "$ENVIRONMENT" ]]; then echo "❌ ENVIRONMENT must be set to dev or prod" exit 1 fi # ================================================== # GLOBAL CONFIG # ================================================== K8S_STORAGE_ROOT="/k8s_storage" BACKUP_ROOT="/tmp/k8s-backups" DATE="$(date -u +%Y-%m-%d_%H-%M-%S)" BACKUP_DIR="$BACKUP_ROOT/$DATE" mkdir -p "$BACKUP_DIR" # NEVER touch raw Postgres data TAR_EXCLUDES=( "$K8S_STORAGE_ROOT/postgres" "$K8S_STORAGE_ROOT/lost+found" ) # ================================================== # ENVIRONMENT SWITCH # ================================================== case "$ENVIRONMENT" in dev) PG_SECRET_NAME="postgres-dev" PG_POD_SELECTOR="app=postgres-dev" S3_PREFIX="dev" NAMESPACE="dev" ;; prod) if [[ "${I_UNDERSTAND_THIS_IS_PROD:-}" != "true" ]]; then echo "❌ Refusing to run PROD backup without confirmation" echo " Re-run with: I_UNDERSTAND_THIS_IS_PROD=true" exit 1 fi PG_SECRET_NAME="postgres-prod" PG_POD_SELECTOR="app=postgres-prod" S3_PREFIX="prod" NAMESPACE="default" ;; *) echo "❌ Invalid ENVIRONMENT: $ENVIRONMENT (must be dev or prod)" exit 1 ;; esac echo "=== Backup started ($(date -u)) ===" echo "Environment: $ENVIRONMENT" echo "Namespace: $NAMESPACE" # ================================================== # LOCATE POSTGRES POD # ================================================== POSTGRES_POD=$(kubectl get pods \ -n "$NAMESPACE" \ -l "$PG_POD_SELECTOR" \ -o jsonpath='{.items[0].metadata.name}') if [[ -z "$POSTGRES_POD" ]]; then echo "❌ No Postgres pod found for selector: $PG_POD_SELECTOR" kubectl get pods -n "$NAMESPACE" exit 1 fi echo "Using Postgres pod: $POSTGRES_POD" # ================================================== # READ DATABASE_URL FROM SECRET # ================================================== DATABASE_URL=$(kubectl get secret "$PG_SECRET_NAME" \ -n "$NAMESPACE" \ -o jsonpath='{.data.DATABASE_URL}' | base64 -d) if [[ -z "$DATABASE_URL" ]]; then echo "❌ DATABASE_URL missing in secret $PG_SECRET_NAME" exit 1 fi # Parse DATABASE_URL POSTGRES_USER="$(echo "$DATABASE_URL" | sed -E 's|.*://([^:]+):.*|\1|')" POSTGRES_DB="$(echo "$DATABASE_URL" | sed -E 's|.*/([^?]+).*|\1|')" if [[ -z "$POSTGRES_USER" || -z "$POSTGRES_DB" ]]; then echo "❌ Failed to parse DATABASE_URL" exit 1 fi echo "Dumping database: $POSTGRES_DB (user: $POSTGRES_USER)" # ================================================== # POSTGRES LOGICAL DUMP (SAFE) # ================================================== kubectl exec -n "$NAMESPACE" "$POSTGRES_POD" -- \ pg_dump "$POSTGRES_DB" \ > "$BACKUP_DIR/postgres.sql" echo "✔ pg_dump complete ($(du -h "$BACKUP_DIR/postgres.sql" | cut -f1))" # ================================================== # NORMALISE PERMISSIONS (EXCLUDING POSTGRES) # ================================================== echo "Normalising permissions (excluding Postgres data)..." sudo find "$K8S_STORAGE_ROOT" \ -mindepth 1 \ -maxdepth 1 \ ! -name postgres \ -exec chmod -R a+rX {} \; || true # ================================================== # ARCHIVE K8S STORAGE (SAFE) # ================================================== TAR_EXCLUDE_ARGS=() for path in "${TAR_EXCLUDES[@]}"; do TAR_EXCLUDE_ARGS+=(--exclude="$path") done tar \ --ignore-failed-read \ --warning=no-file-changed \ -czf "$BACKUP_DIR/k8s_storage_$DATE.tar.gz" \ "${TAR_EXCLUDE_ARGS[@]}" \ "$K8S_STORAGE_ROOT" echo "✔ k8s_storage archived ($(du -h "$BACKUP_DIR/k8s_storage_$DATE.tar.gz" | cut -f1))" # ================================================== # UPLOAD TO S3 # ================================================== S3_BUCKET="s3://mist-backups/$S3_PREFIX/$DATE" aws s3 cp "$BACKUP_DIR" "$S3_BUCKET" --recursive echo "✔ Uploaded to $S3_BUCKET" # ================================================== # RESTORE GUIDE # ================================================== echo "" echo "========================================" echo "=== RESTORE GUIDE ($ENVIRONMENT)" echo "========================================" echo "" echo "Restore volumes:" echo " sudo tar -xzf k8s_storage_$DATE.tar.gz -C /" echo "" echo "Restore Postgres:" echo " kubectl exec -n $NAMESPACE -i $POSTGRES_POD -- \\" echo " psql $POSTGRES_DB < postgres.sql" echo "" echo "=== Backup completed successfully ==="