Pre-launch — Gluecron is in final validation. Public signups and git hosting for non-owner users open after launch review.
CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history

backup-restore-drill.sh

Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.

backup-restore-drill.shBlame112 lines · 1 contributor
1e79799CC LABS App1#!/usr/bin/env bash
2# Backup-restore drill. Proves the backup pipeline works end-to-end.
3# See docs/BACKUP_RESTORE_DRILL.md for the full runbook.
4
5set -uo pipefail
6
7KEEP_DUMP=0
8VERBOSE=0
9for arg in "$@"; do
10 case "$arg" in
11 --keep-dump) KEEP_DUMP=1 ;;
12 --verbose) VERBOSE=1 ;;
13 *) ;;
14 esac
15done
16
17DATABASE_URL="${DATABASE_URL:-}"
18SCRATCH_DATABASE_URL="${SCRATCH_DATABASE_URL:-}"
19
20if [[ -z "$DATABASE_URL" || -z "$SCRATCH_DATABASE_URL" ]]; then
21 echo "ERROR: DATABASE_URL and SCRATCH_DATABASE_URL must both be set." >&2
22 exit 2
23fi
24
25if ! command -v pg_dump >/dev/null 2>&1; then
26 echo "ERROR: pg_dump not found. apt-get install -y postgresql-client" >&2
27 exit 2
28fi
29if ! command -v psql >/dev/null 2>&1; then
30 echo "ERROR: psql not found." >&2
31 exit 2
32fi
33
34ts="$(date +%s)"
35start="$(date -u +%Y-%m-%dT%H:%M:%SZ)"
36start_seconds="$(date +%s)"
37dump="/tmp/gluecron-drill-${ts}.dump"
38stamp_dir="/var/lib/gluecron"
39
40log() {
41 printf '[drill %s] %s\n' "$(date -u +%Y-%m-%dT%H:%M:%SZ)" "$*"
42}
43
44log "dumping prod DB ..."
45if [[ "$VERBOSE" -eq 1 ]]; then
46 pg_dump --format=custom --no-owner --no-privileges --file="$dump" "$DATABASE_URL"
47else
48 pg_dump --format=custom --no-owner --no-privileges --file="$dump" "$DATABASE_URL" 2>/dev/null
49fi
50
51if [[ ! -s "$dump" ]]; then
52 log "FATAL: dump file is empty or missing."
53 exit 1
54fi
55dump_size=$(du -h "$dump" | cut -f1)
56log "dump complete: ${dump_size} at $dump"
57
58log "restoring into scratch ..."
59# Wipe the scratch DB schema (public only). Idempotent.
60psql -v ON_ERROR_STOP=1 "$SCRATCH_DATABASE_URL" >/dev/null <<'SQL'
61DROP SCHEMA IF EXISTS public CASCADE;
62CREATE SCHEMA public;
63GRANT ALL ON SCHEMA public TO PUBLIC;
64SQL
65
66if [[ "$VERBOSE" -eq 1 ]]; then
67 pg_restore --no-owner --no-privileges --dbname="$SCRATCH_DATABASE_URL" "$dump" || true
68else
69 pg_restore --no-owner --no-privileges --dbname="$SCRATCH_DATABASE_URL" "$dump" >/dev/null 2>&1 || true
70fi
71log "restore complete"
72
73q() {
74 local url="$1" sql="$2"
75 psql -At "$url" -c "$sql" 2>/dev/null | tr -d '[:space:]'
76}
77
78compare() {
79 local label="$1" sql="$2"
80 local a b
81 a="$(q "$DATABASE_URL" "$sql")"
82 b="$(q "$SCRATCH_DATABASE_URL" "$sql")"
83 if [[ "$a" == "$b" && -n "$a" ]]; then
84 printf ' \033[32mPASS\033[0m %-22s prod=%-12s scratch=%s\n' "$label" "$a" "$b"
85 return 0
86 fi
87 printf ' \033[31mFAIL\033[0m %-22s prod=%-12s scratch=%s\n' "$label" "$a" "$b"
88 return 1
89}
90
91log "verification:"
92fails=0
93compare "users count" "SELECT COUNT(*) FROM users;" || fails=$((fails+1))
94compare "repositories count" "SELECT COUNT(*) FROM repositories;" || fails=$((fails+1))
95compare "site_admins count" "SELECT COUNT(*) FROM site_admins;" || fails=$((fails+1))
96compare "schema tables" "SELECT COUNT(*) FROM information_schema.tables WHERE table_schema='public';" || fails=$((fails+1))
97
98elapsed=$(( $(date +%s) - start_seconds ))
99
100if [[ "$KEEP_DUMP" -ne 1 ]]; then
101 rm -f "$dump"
102fi
103
104if [[ "$fails" -eq 0 ]]; then
105 log "All checks passed. Took ${elapsed}s."
106 mkdir -p "$stamp_dir" 2>/dev/null || true
107 date +%s > "$stamp_dir/drill-last-success" 2>/dev/null || true
108 exit 0
109fi
110
111log "${fails} check(s) failed. Investigate before trusting backups."
112exit 1