Newer
Older
openstack-caracal-dc-dc / scripts / maas-profile-assert.sh
#!/usr/bin/env bash
# scripts/maas-profile-assert.sh <profile> <expected-rack-hostname>[,<rack>...]
#
# Prove WHICH MAAS REGION a CLI profile resolves to, before any command that
# reads or writes that region. Exit 0 only if the profile's rack-controller set
# matches the expected set EXACTLY.
#
# WHY THIS EXISTS (measured 2026-07-30, D-132 q1 per-DC region migration).
# Every MAAS call in this repo defaults to `MAAS_PROFILE=admin` (13 scripts).
# On voffice1 that profile resolves to the OFFICE1 region
# (http://10.10.0.20:5240/), while the per-DC regions ruled by D-132 q1 live at
# 10.12.8.6 / 10.12.68.6. The two regions hold SEPARATE databases, so:
#   - a carve recreated against the wrong profile is an idempotent NO-OP that
#     prints PASS -- the fixed state was already there, in the wrong region;
#   - `machine delete` against the wrong profile DESTROYS the real record.
# This is the wrong-HOST class this repo has been bitten by three times
# (dc-mirror.sh run on voffice1; the egress probe run from the rack; P5 probing
# the wrong filesystem) -- now wrong-REGION, where the destructive direction is
# unrecoverable. A count alone is NOT proof: two regions can hold the same
# number of machines. Rack-controller IDENTITY is the discriminator, because a
# rack controller registers to exactly one region.
#
# ASSERT ON CONTENT, NEVER ON EXISTENCE: a reachable API that returns an empty
# or unparseable rack list REFUSES (exit 2) rather than passing. "Could not
# look" is never "nothing there".
#
# Exit: 0 profile resolves to the expected region
#       1 profile resolves SOMEWHERE ELSE (or the rack set differs)
#       2 could not evaluate (unknown profile, unreachable API, bad JSON)
# ASCII + LF.
set -uo pipefail

PROFILE="${1:-}"
EXPECTED="${2:-}"

if [ -z "$PROFILE" ] || [ -z "$EXPECTED" ]; then
  echo "usage: maas-profile-assert.sh <profile> <expected-rack-hostname>[,<rack>...]" >&2
  echo "  e.g. maas-profile-assert.sh vr1-dc0-region hot-kid" >&2
  echo "       maas-profile-assert.sh admin voffice1,vvr1-dc0,vvr1-dc1" >&2
  exit 2
fi

command -v maas >/dev/null 2>&1 || { echo "REFUSE: no 'maas' CLI on this host" >&2; exit 2; }

RAW="$(maas "$PROFILE" rack-controllers read 2>&1)"
RC=$?
if [ "$RC" -ne 0 ]; then
  # Do not echo $RAW unfiltered -- a maas CLI error can carry the profile URL and,
  # on some failures, credential fragments. Report the shape, not the body.
  echo "REFUSE: 'maas $PROFILE rack-controllers read' exited $RC (unknown profile or unreachable region)" >&2
  exit 2
fi

ACTUAL="$(printf '%s' "$RAW" | python3 -c '
import json, sys
try:
    d = json.load(sys.stdin)
except Exception:
    sys.exit(3)
if not isinstance(d, list) or not d:
    sys.exit(4)
names = sorted(x.get("hostname", "") for x in d)
if not all(names):
    sys.exit(5)
print(",".join(names))
' 2>/dev/null)"
PRC=$?
if [ "$PRC" -eq 3 ]; then
  echo "REFUSE: profile '$PROFILE' returned unparseable JSON" >&2; exit 2
fi
if [ "$PRC" -eq 4 ]; then
  echo "REFUSE: profile '$PROFILE' returned an EMPTY rack-controller set" >&2
  echo "        (a region with no rack controller cannot be identified -- refusing rather than passing)" >&2
  exit 2
fi
if [ "$PRC" -eq 5 ]; then
  echo "REFUSE: profile '$PROFILE' returned a NAMELESS rack-controller entry" >&2
  echo "        (an unnamed rack cannot discriminate one region from another)" >&2
  exit 2
fi
if [ "$PRC" -ne 0 ] || [ -z "$ACTUAL" ]; then
  echo "REFUSE: could not derive the rack-controller set for profile '$PROFILE'" >&2; exit 2
fi

WANT="$(printf '%s' "$EXPECTED" | tr ',' '\n' | sed '/^$/d' | sort | paste -sd, -)"

if [ "$ACTUAL" = "$WANT" ]; then
  echo "OK   profile '$PROFILE' -> region with racks [$ACTUAL] (as expected)"
  exit 0
fi

echo "FAIL profile '$PROFILE' resolves to the WRONG region" >&2
echo "     expected racks: [$WANT]" >&2
echo "     actual   racks: [$ACTUAL]" >&2
exit 1