Files
OmniRoute/bin/restore-policies.sh
Diego Rodrigues de Sa e Souza d12c3b37da fix: resolve two macOS-only test/script failures in unit suite (#8577)
bin/restore-policies.sh used readarray (bash 4+), which fails on macOS
bash 3.2. Replace with a compatible while-read loop.

machineId.test.ts disableWindowsRegistryStrategy() did not neutralize
the macOS ioreg strategy, so mocked os.hostname() was never reached
on macOS and both ladder tests failed. Stub execSync for ioreg commands
so the fallback chain reaches os.hostname() as intended.

Production src/shared/utils/machineId.ts is correct and unchanged.

Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
2026-08-07 18:07:42 -03:00

79 lines
3.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# bin/restore-policies.sh — restore ONLY the API-key policy tables from a
# snapshot, leaving request/audit/runtime state intact. Used by the auth-layer
# incident-recovery flow when policies_active is empty
# but the rest of the database is healthy (so a full restore-data is overkill).
#
# "Policy" tables = api_key* definition tables (the key + its limits/allowed
# quotas/context sources), EXCLUDING usage counters and reset logs so a restore
# never rewinds live usage accounting.
set -euo pipefail
SCRIPT_NAME="restore-policies"
source "$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)/_ops-common.sh"
usage() {
cat <<'EOF'
Usage: bin/restore-policies.sh <snapshot-id> [--data-dir <path>] [--yes|-y] [-h|--help]
Replaces the API-key policy tables (api_key*, excluding *counter* / *_log*) in
the live DB with the copies from a snapshot, inside one transaction. The live DB
is snapshotted to $DB_BACKUPS_DIR/pre-policy-restore_<UTC> first. Requires sqlite3.
EOF
}
ID=""
while [ $# -gt 0 ]; do
case "$1" in
--yes | -y) ASSUME_YES=1; shift ;;
--data-dir) ops_set_data_dir "${2:?--data-dir needs a value}"; shift 2 ;;
-h | --help) usage; exit 0 ;;
-*) ops_die "unknown argument: $1 (see --help)" ;;
*) ID="$1"; shift ;;
esac
done
[ -n "$ID" ] || ops_die "snapshot id required (see --help)"
ops_require_cmd sqlite3
snap="$(ops_find_snapshot "$ID")"
[ -f "$OMNIROUTE_SQLITE" ] || ops_die "no live DB at $OMNIROUTE_SQLITE (use restore-data.sh for a full restore)"
# Policy definition tables present in BOTH the snapshot and the live DB. GLOB
# keeps `_` literal; we drop usage counters / logs so accounting isn't rewound.
tables=()
while IFS= read -r t; do tables+=("$t"); done < <(
sqlite3 "$snap/storage.sqlite" \
"SELECT name FROM sqlite_master WHERE type='table' AND name GLOB 'api_key*' \
AND name NOT GLOB '*counter*' AND name NOT GLOB '*_log*' ORDER BY name;"
)
[ "${#tables[@]}" -gt 0 ] || ops_die "snapshot has no api_key* policy tables"
ops_log "policy tables to restore: ${tables[*]}"
ops_confirm "Replace ${#tables[@]} policy table(s) in $OMNIROUTE_SQLITE from $snap?" || ops_die "aborted"
# Safety snapshot of the live DB before mutating it.
safety="$OMNIROUTE_BACKUPS_DIR/pre-policy-restore_$(date -u +%Y%m%dT%H%M%SZ)"
mkdir -p "$safety"
sqlite3 "$OMNIROUTE_SQLITE" "VACUUM INTO '$safety/storage.sqlite'"
ops_log "live DB saved to $safety"
# Replace each policy table inside a single transaction, attaching the snapshot
# (only tables that also exist in the live DB are touched).
sql="ATTACH DATABASE '$snap/storage.sqlite' AS snap;
PRAGMA foreign_keys=OFF;
BEGIN;"
for t in "${tables[@]}"; do
if [ -n "$(sqlite3 "$OMNIROUTE_SQLITE" "SELECT 1 FROM sqlite_master WHERE type='table' AND name='$t' LIMIT 1;")" ]; then
sql="$sql
DELETE FROM main.\"$t\";
INSERT INTO main.\"$t\" SELECT * FROM snap.\"$t\";"
else
ops_log "skipping '$t' — not present in live DB"
fi
done
sql="$sql
COMMIT;
DETACH DATABASE snap;"
printf '%s\n' "$sql" | sqlite3 "$OMNIROUTE_SQLITE"
ops_log "policies restored from $snap — restart OmniRoute to apply"