Start autonomous Belgium and North Sea RC
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
CONTAINER="geointel"
|
||||
BACKUP_DIR=""
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: bash scripts/verify_release_backup.sh --backup-dir PATH [--container NAME]
|
||||
|
||||
Verifies checksums, manifest shape and PostgreSQL archive readability. This
|
||||
command is read-only and never creates, restores or drops a database.
|
||||
EOF
|
||||
}
|
||||
|
||||
while [ "$#" -gt 0 ]; do
|
||||
case "$1" in
|
||||
--backup-dir) BACKUP_DIR="$2"; shift 2 ;;
|
||||
--container) CONTAINER="$2"; shift 2 ;;
|
||||
--help|-h) usage; exit 0 ;;
|
||||
*) echo "Unknown argument: $1" >&2; usage >&2; exit 2 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
if [ -z "$BACKUP_DIR" ]; then
|
||||
usage >&2
|
||||
exit 2
|
||||
fi
|
||||
for required in docker python3 sha256sum; do
|
||||
command -v "$required" >/dev/null 2>&1 || {
|
||||
echo "Missing required command: $required" >&2
|
||||
exit 2
|
||||
}
|
||||
done
|
||||
BACKUP_DIR="$(python3 -c 'import pathlib,sys; print(pathlib.Path(sys.argv[1]).expanduser().resolve())' "$BACKUP_DIR")"
|
||||
for required_file in manifest.json database.dump database.list database-metadata.tsv table-counts.tsv CHECKSUMS.sha256; do
|
||||
test -s "$BACKUP_DIR/$required_file" || {
|
||||
echo "Missing or empty backup artifact: $required_file" >&2
|
||||
exit 3
|
||||
}
|
||||
done
|
||||
|
||||
(
|
||||
cd "$BACKUP_DIR"
|
||||
sha256sum -c CHECKSUMS.sha256
|
||||
)
|
||||
|
||||
python3 - "$BACKUP_DIR/manifest.json" <<'PY'
|
||||
import json
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
payload = json.loads(pathlib.Path(sys.argv[1]).read_text(encoding="utf-8"))
|
||||
required = {
|
||||
"schema_version",
|
||||
"release_id",
|
||||
"created_at",
|
||||
"read_only_source",
|
||||
"database_name",
|
||||
"database_user",
|
||||
"image_id",
|
||||
"git_commit",
|
||||
}
|
||||
missing = sorted(required - payload.keys())
|
||||
if missing:
|
||||
raise SystemExit(f"Backup manifest misses: {', '.join(missing)}")
|
||||
if payload["schema_version"] != 1 or payload["read_only_source"] is not True:
|
||||
raise SystemExit("Unsupported or unsafe backup manifest")
|
||||
PY
|
||||
|
||||
if [ "$(docker inspect -f '{{.State.Running}}' "$CONTAINER" 2>/dev/null || true)" != "true" ]; then
|
||||
echo "Container '$CONTAINER' is required to run pg_restore --list." >&2
|
||||
exit 3
|
||||
fi
|
||||
TMP_LIST="$(mktemp)"
|
||||
trap 'rm -f -- "$TMP_LIST"' EXIT
|
||||
docker exec -i "$CONTAINER" pg_restore --list \
|
||||
< "$BACKUP_DIR/database.dump" \
|
||||
> "$TMP_LIST"
|
||||
cmp -s "$TMP_LIST" "$BACKUP_DIR/database.list" || {
|
||||
echo "The current pg_restore listing differs from the retained listing." >&2
|
||||
exit 3
|
||||
}
|
||||
grep -q "TABLE DATA" "$TMP_LIST" || {
|
||||
echo "Archive contains no table data entries." >&2
|
||||
exit 3
|
||||
}
|
||||
|
||||
echo "Release backup verified read-only: $BACKUP_DIR"
|
||||
Reference in New Issue
Block a user