Some checks failed
- deploy.sh: add DB health wait before migrations, prune old Docker images - restore.sh: add redis-exporter to stop list, replace sleep with DB health wait - verify-server.sh: add redis-exporter to expected containers, add Sentry + Redis exporter checks Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
164 lines
5.1 KiB
Bash
Executable File
164 lines
5.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# scripts/restore.sh — Database restore helper for Orion and Gitea
|
|
#
|
|
# Usage:
|
|
# bash scripts/restore.sh orion ~/backups/orion/daily/orion_20260214_030000.sql.gz
|
|
# bash scripts/restore.sh gitea ~/backups/gitea/daily/gitea_20260214_030000.sql.gz
|
|
#
|
|
# What it does:
|
|
# 1. Stops app containers (keeps DB running)
|
|
# 2. Drops and recreates the database
|
|
# 3. Restores from the .sql.gz backup
|
|
# 4. Runs Alembic migrations (Orion only)
|
|
# 5. Restarts all containers
|
|
|
|
set -euo pipefail
|
|
|
|
# =============================================================================
|
|
# Configuration
|
|
# =============================================================================
|
|
ORION_APP_DIR="${HOME}/apps/orion"
|
|
|
|
# =============================================================================
|
|
# Functions
|
|
# =============================================================================
|
|
log() {
|
|
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*"
|
|
}
|
|
|
|
usage() {
|
|
echo "Usage: $0 <target> <backup-file>"
|
|
echo ""
|
|
echo " target: 'orion' or 'gitea'"
|
|
echo " backup-file: path to .sql.gz file"
|
|
echo ""
|
|
echo "Examples:"
|
|
echo " $0 orion ~/backups/orion/daily/orion_20260214_030000.sql.gz"
|
|
echo " $0 gitea ~/backups/gitea/daily/gitea_20260214_030000.sql.gz"
|
|
exit 1
|
|
}
|
|
|
|
restore_orion() {
|
|
local backup_file="$1"
|
|
local container="orion-db-1"
|
|
local db_name="orion_db"
|
|
local db_user="orion_user"
|
|
|
|
log "=== Restoring Orion database ==="
|
|
|
|
# Stop app containers (keep DB and Redis running)
|
|
log "Stopping Orion app containers..."
|
|
cd "${ORION_APP_DIR}"
|
|
docker compose --profile full stop api celery-worker celery-beat flower redis-exporter 2>/dev/null || true
|
|
|
|
# Drop and recreate database
|
|
log "Dropping and recreating ${db_name}..."
|
|
docker exec "${container}" psql -U "${db_user}" -d postgres -c \
|
|
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '${db_name}' AND pid <> pg_backend_pid();" 2>/dev/null || true
|
|
docker exec "${container}" dropdb -U "${db_user}" --if-exists "${db_name}"
|
|
docker exec "${container}" createdb -U "${db_user}" "${db_name}"
|
|
|
|
# Restore
|
|
log "Restoring from ${backup_file}..."
|
|
gunzip -c "${backup_file}" | docker exec -i "${container}" psql -U "${db_user}" -d "${db_name}" --quiet
|
|
|
|
# Run migrations
|
|
log "Running Alembic migrations..."
|
|
docker compose --profile full start api 2>/dev/null || \
|
|
docker compose --profile full up -d api
|
|
|
|
# Wait for API container to be healthy before running migrations
|
|
log "Waiting for API container to be ready..."
|
|
for i in $(seq 1 12); do
|
|
if docker compose --profile full exec -T db pg_isready -U orion_user -d orion_db > /dev/null 2>&1; then
|
|
log "Database is ready (attempt $i/12)"
|
|
break
|
|
fi
|
|
[ "$i" -eq 12 ] && { log "WARNING: database may not be ready, attempting migration anyway"; }
|
|
sleep 5
|
|
done
|
|
|
|
docker compose --profile full exec -T -e PYTHONPATH=/app api python -m alembic upgrade heads
|
|
|
|
# Restart all
|
|
log "Restarting all services..."
|
|
docker compose --profile full up -d
|
|
|
|
log "=== Orion restore complete ==="
|
|
}
|
|
|
|
restore_gitea() {
|
|
local backup_file="$1"
|
|
local container="gitea-db"
|
|
local db_name="gitea"
|
|
local db_user="gitea"
|
|
local gitea_dir="${HOME}/gitea"
|
|
|
|
log "=== Restoring Gitea database ==="
|
|
|
|
# Stop Gitea container (keep DB running)
|
|
log "Stopping Gitea..."
|
|
cd "${gitea_dir}"
|
|
docker compose stop gitea 2>/dev/null || true
|
|
|
|
# Drop and recreate database
|
|
log "Dropping and recreating ${db_name}..."
|
|
docker exec "${container}" psql -U "${db_user}" -d postgres -c \
|
|
"SELECT pg_terminate_backend(pid) FROM pg_stat_activity WHERE datname = '${db_name}' AND pid <> pg_backend_pid();" 2>/dev/null || true
|
|
docker exec "${container}" dropdb -U "${db_user}" --if-exists "${db_name}"
|
|
docker exec "${container}" createdb -U "${db_user}" "${db_name}"
|
|
|
|
# Restore
|
|
log "Restoring from ${backup_file}..."
|
|
gunzip -c "${backup_file}" | docker exec -i "${container}" psql -U "${db_user}" -d "${db_name}" --quiet
|
|
|
|
# Restart Gitea
|
|
log "Restarting Gitea..."
|
|
docker compose up -d
|
|
|
|
log "=== Gitea restore complete ==="
|
|
}
|
|
|
|
# =============================================================================
|
|
# Main
|
|
# =============================================================================
|
|
if [ $# -lt 2 ]; then
|
|
usage
|
|
fi
|
|
|
|
TARGET="$1"
|
|
BACKUP_FILE="$2"
|
|
|
|
# Validate backup file
|
|
if [ ! -f "${BACKUP_FILE}" ]; then
|
|
log "ERROR: Backup file not found: ${BACKUP_FILE}"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! "${BACKUP_FILE}" == *.sql.gz ]]; then
|
|
log "ERROR: Expected a .sql.gz file, got: ${BACKUP_FILE}"
|
|
exit 1
|
|
fi
|
|
|
|
# Confirm
|
|
log "WARNING: This will DROP and RECREATE the ${TARGET} database!"
|
|
log "Backup file: ${BACKUP_FILE}"
|
|
read -rp "Continue? (y/N) " confirm
|
|
if [[ "${confirm}" != [yY] ]]; then
|
|
log "Aborted."
|
|
exit 0
|
|
fi
|
|
|
|
case "${TARGET}" in
|
|
orion)
|
|
restore_orion "${BACKUP_FILE}"
|
|
;;
|
|
gitea)
|
|
restore_gitea "${BACKUP_FILE}"
|
|
;;
|
|
*)
|
|
log "ERROR: Unknown target '${TARGET}'. Use 'orion' or 'gitea'."
|
|
usage
|
|
;;
|
|
esac
|