#!/usr/bin/env bash # Backup-restore-övning (hardening-checklistan: "genomfört och protokollfört, kvartalsvis"). # Kör: DATABASE_URL=postgres://… ./infrastructure/deployment/backup-restore-test.sh # # Gör: logisk dump → återställ till temporär databas → jämför radantal per tabell # → skriv protokoll → städa. Avslutar med exitkod 1 vid avvikelse. set -euo pipefail DATABASE_URL="${DATABASE_URL:?Sätt DATABASE_URL}" # Roll med CREATEDB för test-databasen (RDS master/operatör). Faller tillbaka på DATABASE_URL. ADMIN_URL="${ADMIN_DATABASE_URL:-$DATABASE_URL}" STAMP=$(date +%Y%m%d_%H%M%S) WORKDIR="${BACKUP_DIR:-/tmp/backup-restore-test}" DUMP="$WORKDIR/dump-$STAMP.pgdump" RESTORE_DB="restore_test_$STAMP" PROTOKOLL="$WORKDIR/protokoll-$STAMP.md" mkdir -p "$WORKDIR" # Härled anslutning utan databasnamn för att kunna skapa/droppa test-databasen. BASE_URL="${DATABASE_URL%/*}" ADMIN_BASE_URL="${ADMIN_URL%/*}" echo "==> 1/5 Dumpar databasen" pg_dump "$DATABASE_URL" --format=custom --file="$DUMP" SIZE=$(du -h "$DUMP" | cut -f1) echo "==> 2/5 Skapar temporär databas $RESTORE_DB" psql "$ADMIN_BASE_URL/postgres" -qc "CREATE DATABASE $RESTORE_DB" 2>/dev/null \ || psql "$ADMIN_URL" -qc "CREATE DATABASE $RESTORE_DB" cleanup() { psql "$ADMIN_BASE_URL/postgres" -qc "DROP DATABASE IF EXISTS $RESTORE_DB" 2>/dev/null \ || psql "$ADMIN_URL" -qc "DROP DATABASE IF EXISTS $RESTORE_DB" || true } trap cleanup EXIT echo "==> 3/5 Återställer till $RESTORE_DB" # Återställ som admin-rollen (äger test-databasen, får skapa extensions). pg_restore --dbname="$ADMIN_BASE_URL/$RESTORE_DB" --no-owner --no-privileges "$DUMP" echo "==> 4/5 Jämför radantal per tabell" COUNT_SQL="SELECT relname, n_live_tup FROM pg_stat_user_tables ORDER BY relname" ROWS_SQL="SELECT string_agg(format('%s:%s', t.tablename, (xpath('/row/cnt/text()', query_to_xml(format('SELECT count(*) AS cnt FROM %I', t.tablename), false, true, '')))[1]::text), E'\n' ORDER BY t.tablename) FROM pg_tables t WHERE schemaname='public'" ORIG=$(psql "$DATABASE_URL" -tA -c "$ROWS_SQL") REST=$(psql "$ADMIN_BASE_URL/$RESTORE_DB" -tA -c "$ROWS_SQL") STATUS="GODKÄND" DIFF_OUT="" if [ "$ORIG" != "$REST" ]; then STATUS="UNDERKÄND" DIFF_OUT=$(diff <(echo "$ORIG") <(echo "$REST") || true) fi echo "==> 5/5 Skriver protokoll: $PROTOKOLL" TABLES=$(echo "$ORIG" | wc -l) TOTAL=$(echo "$ORIG" | awk -F: '{s+=$2} END {print s}') { echo "# Backup-restore-protokoll $STAMP" echo echo "| Fält | Värde |" echo "| ---- | ----- |" echo "| Resultat | **$STATUS** |" echo "| Dumpstorlek | $SIZE |" echo "| Tabeller jämförda | $TABLES |" echo "| Totalt antal rader | $TOTAL |" echo "| Dump | $DUMP |" echo if [ -n "$DIFF_OUT" ]; then echo '## Avvikelser' echo '```' echo "$DIFF_OUT" echo '```' else echo "Alla tabellers radantal identiska mellan original och återställning." fi } > "$PROTOKOLL" cat "$PROTOKOLL" [ "$STATUS" = "GODKÄND" ]