Files
Assistant/deploy/verify-postgres-restore.sh
m e9594306e5
CI / test (push) Successful in 10s
ops: add verified PostgreSQL backups
2026-08-16 17:53:40 +03:00

73 lines
2.4 KiB
Bash
Executable File

#!/bin/sh
set -eu
if [ "$#" -ne 1 ]; then
echo "usage: $0 <backup-directory>" >&2
exit 2
fi
backup_dir="$1"
image="${ASSISTANT_RESTORE_IMAGE:-postgres:18.6-alpine}"
container="assistant-postgres-restore-check-$(date -u +%Y%m%dT%H%M%SZ)-$$"
password="$(od -An -N24 -tx1 /dev/urandom | tr -d ' \n')"
created=false
cleanup() {
if [ "$created" = true ]; then
docker rm -f "$container" >/dev/null 2>&1 || true
fi
}
trap cleanup EXIT HUP INT TERM
for file in assistant.dump globals.sql database.name owner.name SHA256SUMS; do
if [ ! -f "$backup_dir/$file" ]; then
echo "Backup file is missing: $file" >&2
exit 1
fi
done
(
cd "$backup_dir"
sha256sum -c SHA256SUMS
)
database="$(sed -n '1p' "$backup_dir/database.name")"
owner="$(sed -n '1p' "$backup_dir/owner.name")"
[ -n "$database" ] && [ -n "$owner" ] || {
echo "Database metadata is empty" >&2
exit 1
}
case "$database:$owner" in
*[!A-Za-z0-9_:.-]*) echo "Unsafe database metadata" >&2; exit 1 ;;
esac
docker run -d --name "$container" \
--tmpfs /var/lib/postgresql:rw,noexec,nosuid,size=1g \
-e POSTGRES_PASSWORD="$password" \
"$image" >/dev/null
created=true
attempt=0
until docker exec "$container" pg_isready -U postgres -d postgres >/dev/null 2>&1; do
attempt=$((attempt + 1))
if [ "$attempt" -ge 60 ]; then
docker logs "$container" >&2
echo "Restore-check PostgreSQL did not become ready" >&2
exit 1
fi
sleep 1
done
docker exec -i "$container" psql -v ON_ERROR_STOP=1 -U postgres -d postgres <"$backup_dir/globals.sql" >/dev/null
docker exec "$container" psql -v ON_ERROR_STOP=1 -U postgres -d postgres \
-c "CREATE DATABASE \"$database\" OWNER \"$owner\"" >/dev/null
docker exec -i "$container" pg_restore --exit-on-error --no-password -U postgres -d "$database" <"$backup_dir/assistant.dump" >/dev/null
schema_count="$(docker exec "$container" psql -U postgres -d "$database" -Atqc "select count(*) from information_schema.schemata where schema_name not in ('pg_catalog','information_schema') and schema_name not like 'pg_toast%'")"
table_count="$(docker exec "$container" psql -U postgres -d "$database" -Atqc "select count(*) from information_schema.tables where table_schema not in ('pg_catalog','information_schema')")"
if [ "$schema_count" -lt 1 ] || [ "$table_count" -lt 1 ]; then
echo "Restored database lacks expected user objects" >&2
exit 1
fi
printf 'restore verified: database=%s schemas=%s tables=%s\n' "$database" "$schema_count" "$table_count"