80 lines
2.9 KiB
Bash
80 lines
2.9 KiB
Bash
#!/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" ]
|