113 行
3.6 KiB
Bash
可执行文件
113 行
3.6 KiB
Bash
可执行文件
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
BACKUP_FILE="${1:-${BACKUP_FILE:-}}"
|
|
if [[ -z "${BACKUP_FILE:-}" ]]; then
|
|
echo "Usage: $0 <backup-file.dump>" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ ! -f "$BACKUP_FILE" ]]; then
|
|
echo "[ERR] backup file not found: $BACKUP_FILE" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "${DATABASE_URL:-}" ]]; then
|
|
echo "[ERR] DATABASE_URL is required" >&2
|
|
exit 1
|
|
fi
|
|
|
|
RESTORE_SCOPE="${RESTORE_SCOPE:-}"
|
|
RESTORE_CONFIRM_DATABASE="${RESTORE_CONFIRM_DATABASE:-}"
|
|
DB_TOOL_MODE="${DB_TOOL_MODE:-auto}"
|
|
|
|
if [[ "$DB_TOOL_MODE" != "auto" && "$DB_TOOL_MODE" != "host" && "$DB_TOOL_MODE" != "docker" ]]; then
|
|
echo "[ERR] DB_TOOL_MODE must be auto, host, or docker" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$RESTORE_SCOPE" != "shadow" ]]; then
|
|
echo "[ERR] RESTORE_SCOPE=shadow is required; production restore is not available from this command path" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -z "$RESTORE_CONFIRM_DATABASE" ]]; then
|
|
echo "[ERR] RESTORE_CONFIRM_DATABASE is required" >&2
|
|
exit 1
|
|
fi
|
|
|
|
validate_dump_with_host() {
|
|
pg_restore --list "$BACKUP_FILE" >/dev/null
|
|
}
|
|
|
|
validate_shadow_database_name() {
|
|
local database_name="$1"
|
|
if [[ ! "$database_name" =~ (^|[_-])(shadow|drill|restore_test)([_-]|$) ]]; then
|
|
echo "[ERR] restore target name is not an explicit shadow/drill database" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
restore_with_host() {
|
|
local actual_database
|
|
actual_database="$(psql "$DATABASE_URL" -Atqc 'SELECT current_database()')"
|
|
if [[ -z "$actual_database" || "$actual_database" != "$RESTORE_CONFIRM_DATABASE" ]]; then
|
|
echo "[ERR] restore target confirmation mismatch: actual=${actual_database:-unknown}" >&2
|
|
exit 1
|
|
fi
|
|
validate_shadow_database_name "$actual_database"
|
|
echo "[INFO] DB restore target confirmed: $actual_database (shadow)"
|
|
pg_restore --clean --if-exists --no-owner --no-acl -d "$DATABASE_URL" "$BACKUP_FILE"
|
|
}
|
|
|
|
if [[ "$DB_TOOL_MODE" != "docker" ]] && command -v pg_restore >/dev/null 2>&1 && command -v psql >/dev/null 2>&1; then
|
|
if validate_dump_with_host; then
|
|
restore_with_host
|
|
echo "[OK] DB restore done (pg_restore): $BACKUP_FILE"
|
|
exit 0
|
|
fi
|
|
if [[ "$DB_TOOL_MODE" == "host" ]]; then
|
|
echo "[ERR] host pg_restore could not validate the dump" >&2
|
|
exit 1
|
|
fi
|
|
echo "[WARN] host pg_restore could not validate the dump; trying docker fallback" >&2
|
|
fi
|
|
|
|
if [[ "$DB_TOOL_MODE" == "host" ]]; then
|
|
echo "[ERR] DB_TOOL_MODE=host requires pg_restore and psql" >&2
|
|
exit 1
|
|
fi
|
|
|
|
if ! command -v docker >/dev/null 2>&1; then
|
|
echo "[ERR] pg_restore/psql not found and docker not available" >&2
|
|
exit 1
|
|
fi
|
|
|
|
DB_CONTAINER="${DB_CONTAINER:-hyb-oa-trialrun-db}"
|
|
POSTGRES_DB="${POSTGRES_DB:-}"
|
|
POSTGRES_USER="${POSTGRES_USER:-}"
|
|
POSTGRES_PASSWORD="${POSTGRES_PASSWORD:-}"
|
|
|
|
if [[ -z "$POSTGRES_DB" || -z "$POSTGRES_USER" || -z "$POSTGRES_PASSWORD" ]]; then
|
|
echo "[ERR] Using docker fallback requires POSTGRES_DB/POSTGRES_USER/POSTGRES_PASSWORD" >&2
|
|
exit 1
|
|
fi
|
|
|
|
ACTUAL_DATABASE="$(docker exec -e PGPASSWORD="$POSTGRES_PASSWORD" "$DB_CONTAINER" \
|
|
psql -U "$POSTGRES_USER" -d "$POSTGRES_DB" -Atqc 'SELECT current_database()')"
|
|
|
|
if [[ -z "$ACTUAL_DATABASE" || "$ACTUAL_DATABASE" != "$RESTORE_CONFIRM_DATABASE" ]]; then
|
|
echo "[ERR] restore target confirmation mismatch: actual=${ACTUAL_DATABASE:-unknown}" >&2
|
|
exit 1
|
|
fi
|
|
validate_shadow_database_name "$ACTUAL_DATABASE"
|
|
|
|
docker exec -i "$DB_CONTAINER" pg_restore --list <"$BACKUP_FILE" >/dev/null
|
|
|
|
echo "[INFO] DB restore target confirmed: $ACTUAL_DATABASE (shadow)"
|
|
docker exec -i -e PGPASSWORD="$POSTGRES_PASSWORD" "$DB_CONTAINER" \
|
|
pg_restore --clean --if-exists --no-owner --no-acl -U "$POSTGRES_USER" -d "$POSTGRES_DB" \
|
|
<"$BACKUP_FILE"
|
|
|
|
echo "[OK] DB restore done (docker exec): $BACKUP_FILE"
|