feat(backup): add MinIO recovery smoke test script
This commit is contained in:
87
scripts/recovery-smoke-minio.sh
Executable file
87
scripts/recovery-smoke-minio.sh
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
CREDS_FILE="${CREDS_FILE:-$HOME/.openclaw/credentials/minio-zap.env}"
|
||||
MC_BIN="${MC_BIN:-$HOME/.openclaw/workspace/bin/mc}"
|
||||
PREFIX_ROOT="${PREFIX_ROOT:-workspace-backups}"
|
||||
MAX_AGE_HOURS="${MAX_AGE_HOURS:-12}"
|
||||
|
||||
if [[ ! -x "$MC_BIN" ]]; then
|
||||
MC_BIN="$(command -v mc || true)"
|
||||
fi
|
||||
|
||||
fail() {
|
||||
echo "STATE=FAIL"
|
||||
echo "ERROR=$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
[[ -f "$CREDS_FILE" ]] || fail "Missing creds file: $CREDS_FILE"
|
||||
[[ -x "$MC_BIN" ]] || fail "MinIO client not found (set MC_BIN)"
|
||||
|
||||
# shellcheck disable=SC1090
|
||||
source "$CREDS_FILE"
|
||||
|
||||
[[ -n "${MINIO_ENDPOINT:-}" ]] || fail "MINIO_ENDPOINT missing"
|
||||
[[ -n "${MINIO_ACCESS_KEY:-}" ]] || fail "MINIO_ACCESS_KEY missing"
|
||||
[[ -n "${MINIO_SECRET_KEY:-}" ]] || fail "MINIO_SECRET_KEY missing"
|
||||
[[ -n "${MINIO_BUCKET:-}" ]] || fail "MINIO_BUCKET missing"
|
||||
|
||||
"$MC_BIN" alias set minio "$MINIO_ENDPOINT" "$MINIO_ACCESS_KEY" "$MINIO_SECRET_KEY" >/dev/null
|
||||
|
||||
latest_prefix="$($MC_BIN ls "minio/$MINIO_BUCKET/$PREFIX_ROOT" 2>/dev/null | awk '{print $NF}' | tr -d '/' | grep -E '^[0-9]{8}T[0-9]{6}Z$' | sort | tail -n1 || true)"
|
||||
[[ -n "$latest_prefix" ]] || fail "No backup prefixes found under $PREFIX_ROOT"
|
||||
|
||||
# freshness check
|
||||
backup_iso="${latest_prefix:0:4}-${latest_prefix:4:2}-${latest_prefix:6:2} ${latest_prefix:9:2}:${latest_prefix:11:2}:${latest_prefix:13:2} UTC"
|
||||
backup_epoch="$(date -u -d "$backup_iso" +%s 2>/dev/null || echo 0)"
|
||||
now_epoch="$(date -u +%s)"
|
||||
[[ "$backup_epoch" -gt 0 ]] || fail "Could not parse timestamp from prefix: $latest_prefix"
|
||||
age_h=$(( (now_epoch - backup_epoch) / 3600 ))
|
||||
if (( age_h > MAX_AGE_HOURS )); then
|
||||
fail "Latest backup too old (${age_h}h > ${MAX_AGE_HOURS}h): $latest_prefix"
|
||||
fi
|
||||
|
||||
prefix_path="minio/$MINIO_BUCKET/$PREFIX_ROOT/$latest_prefix"
|
||||
archive="openclaw-${latest_prefix}.tar.gz"
|
||||
sha_file="${archive}.sha256"
|
||||
manifest="manifest.txt"
|
||||
|
||||
# Ensure required objects exist
|
||||
"$MC_BIN" stat "$prefix_path/$archive" >/dev/null 2>&1 || fail "Missing archive: $archive"
|
||||
"$MC_BIN" stat "$prefix_path/$sha_file" >/dev/null 2>&1 || fail "Missing checksum: $sha_file"
|
||||
"$MC_BIN" stat "$prefix_path/$manifest" >/dev/null 2>&1 || fail "Missing manifest: $manifest"
|
||||
|
||||
TMPDIR="$(mktemp -d)"
|
||||
trap 'rm -rf "$TMPDIR"' EXIT
|
||||
|
||||
"$MC_BIN" cp "$prefix_path/$archive" "$TMPDIR/" >/dev/null
|
||||
"$MC_BIN" cp "$prefix_path/$sha_file" "$TMPDIR/" >/dev/null
|
||||
"$MC_BIN" cp "$prefix_path/$manifest" "$TMPDIR/" >/dev/null
|
||||
|
||||
cd "$TMPDIR"
|
||||
awk '{print $1" '$archive'"}' "$sha_file" > check.sha256
|
||||
sha256sum -c check.sha256 >/dev/null || fail "SHA256 verification failed"
|
||||
|
||||
mkdir -p restore
|
||||
# Extract without touching live ~/.openclaw
|
||||
tar -xzf "$archive" -C restore || fail "Archive extraction failed"
|
||||
|
||||
# Basic structure checks
|
||||
for req in \
|
||||
"restore/.openclaw/openclaw.json" \
|
||||
"restore/.openclaw/agents" \
|
||||
"restore/.openclaw/credentials" \
|
||||
"restore/.openclaw/workspace"; do
|
||||
[[ -e "$req" ]] || fail "Missing required path in restore: $req"
|
||||
done
|
||||
|
||||
size_bytes="$(stat -c '%s' "$archive")"
|
||||
|
||||
echo "STATE=PASS"
|
||||
echo "LATEST_PREFIX=$latest_prefix"
|
||||
echo "AGE_HOURS=$age_h"
|
||||
echo "ARCHIVE_BYTES=$size_bytes"
|
||||
echo "CHECKSUM=OK"
|
||||
echo "EXTRACT=OK"
|
||||
echo "STRUCTURE=OK"
|
||||
Reference in New Issue
Block a user