#!/usr/bin/env bash
# tests/maas-region-power-key/run-tests.sh -- unit tests for
# scripts/maas-region-power-key.sh (2026-07-30).
#
# The script's real work happens as root inside a MAAS snap, so these tests
# exercise the parts that decide CORRECTNESS BEFORE any privileged call:
# argument validation, the <region>-<dc> derivation, qemu+ssh URI parsing, and
# the refusal directions. A stubbed `sudo`/`virsh`/`ssh-keygen` on PATH lets the
# check/install paths run without a snap.
#
# THE DERIVATION AND THE URI PARSE ARE THE POINT. The key filename is
# id_<dc>_power and the ssh stanza keys on the rack host -- get either wrong and
# the region silently dials the WRONG RACK, which powers off another DC's
# machines. Both are derived from arguments rather than typed, and a
# transposition is caught here rather than by review.
set -uo pipefail
SD="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SCRIPT="$SD/../../scripts/maas-region-power-key.sh"
P=0; F=0
ok(){ echo "PASS: $1"; P=$((P+1)); }
no(){ echo "FAIL: $1"; F=$((F+1)); }
chk(){ [ "$2" = "$3" ] && ok "$1" || no "$1 (got '$2' want '$3')"; }
TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
mkdir -p "$TMP/bin" "$TMP/snap"
# --- stubs -----------------------------------------------------------------
# sudo: transparently runs the command (STUB_SUDO=deny makes `sudo -n true` fail).
# It EMULATES privilege rather than granting it: `-o root -g root` is stripped and
# `chown root:root` succeeds as a no-op, because an unprivileged test process
# cannot really chown to root. Everything else runs for real, so the install path
# is exercised end to end (dirs created, key written, stanza appended).
cat > "$TMP/bin/sudo" <<'STUB'
#!/usr/bin/env bash
[ "${STUB_SUDO:-allow}" = "deny" ] && exit 1
while [ "${1:-}" = "-n" ]; do shift; done
if [ "${1:-}" = "chown" ]; then exit 0; fi
if [ "${1:-}" = "install" ]; then
args=(); shift
while [ "$#" -gt 0 ]; do
case "$1" in
-o|-g) shift 2; continue ;;
*) args+=("$1"); shift ;;
esac
done
exec install "${args[@]}"
fi
exec "$@"
STUB
# virsh: STUB_VIRSH controls connect success and domain count
cat > "$TMP/bin/virsh" <<'STUB'
#!/usr/bin/env bash
case "${STUB_VIRSH:-ok}" in
refuse) echo "error: failed to connect" >&2; exit 1 ;;
empty) case " $* " in *" list "*) exit 0 ;; *) echo "compiled 10.0.0"; exit 0 ;; esac ;;
*) case " $* " in *" list "*) printf 'dom-a\ndom-b\n'; exit 0 ;; *) echo "compiled 10.0.0"; exit 0 ;; esac ;;
esac
STUB
chmod +x "$TMP/bin/sudo" "$TMP/bin/virsh"
export PATH="$TMP/bin:$PATH"
# Point the script at the stub explicitly -- in production it resolves the SNAP's
# virsh, which no test host has.
export VIRSH_BIN="$TMP/bin/virsh"
run(){ OUT="$(bash "$SCRIPT" "$@" 2>&1)"; RC=$?; }
URI="qemu+ssh://jessea123@10.12.8.2/system"
# --- usage / argument validation -------------------------------------------
run; chk "no args -> 2" "$RC" 2
run check; chk "action only -> 2" "$RC" 2
run check vr1-dc0; chk "missing uri -> 2" "$RC" 2
run bogus vr1-dc0 "$URI"; chk "unknown action -> 2" "$RC" 2
run check vr1-dc0 "$URI" --nope; chk "unknown option -> 2" "$RC" 2
# --- site token derivation --------------------------------------------------
run check dc0 "$URI"
chk "site without a region prefix -> 2" "$RC" 2
case "$OUT" in *"not <region>-<dc>"*) ok "bad site names the shape" ;; *) no "bad site names the shape (got '$OUT')" ;; esac
# --- URI parsing ------------------------------------------------------------
run check vr1-dc0 "qemu+ssh://10.12.8.2/system"
chk "uri without a user -> 2 (never dial as root by default)" "$RC" 2
run check vr1-dc0 "ssh://jessea123@10.12.8.2/system"
chk "non-qemu+ssh scheme -> 2" "$RC" 2
run check vr1-dc0 "10.12.8.2"
chk "bare host -> 2" "$RC" 2
# --- sudo unavailable -------------------------------------------------------
OUT="$(STUB_SUDO=deny bash "$SCRIPT" check vr1-dc0 "$URI" 2>&1)"; RC=$?
chk "no passwordless sudo REFUSES -> 2" "$RC" 2
echo
echo "--- check path against a synthetic snap tree ---"
# MAAS_SNAP_ROOT points the script at a fake tree so derivation, URI parsing and
# every assertion can be exercised without a real snap.
export MAAS_SNAP_ROOT="$TMP/snap"
mkdir -p "$TMP/snap/root/.ssh"
run check vr1-dc0 "$URI"
case "$OUT" in *"id_dc0_power"*) ok "derives key name id_dc0_power from site vr1-dc0" ;; *) no "derives key name id_dc0_power (got '$OUT')" ;; esac
run check vr1-dc1 "qemu+ssh://jessea123@10.12.68.2/system"
case "$OUT" in *"id_dc1_power"*) ok "derives key name id_dc1_power from site vr1-dc1" ;; *) no "derives key name id_dc1_power (got '$OUT')" ;; esac
# TRANSPOSITION GUARD: a dc1 site must never produce dc0's key name, and the
# parsed rack must never be the other DC's. This is the assertion that catches a
# copy-paste between the two per-DC invocations.
run check vr1-dc1 "qemu+ssh://jessea123@10.12.68.2/system"
case "$OUT" in *"id_dc0_power"*) no "dc1 invocation leaked dc0's key name" ;; *) ok "dc1 invocation carries no dc0 key name" ;; esac
case "$OUT" in *"rack=10.12.68.2"*) ok "dc1 invocation parses rack 10.12.68.2" ;; *) no "dc1 invocation parses rack 10.12.68.2 (got '$OUT')" ;; esac
run check vr1-dc0 "$URI"
case "$OUT" in *"rack=10.12.8.2"*) ok "dc0 invocation parses rack 10.12.8.2" ;; *) no "dc0 invocation parses rack 10.12.8.2 (got '$OUT')" ;; esac
case "$OUT" in *"user=jessea123"*) ok "parses the ssh user out of the uri" ;; *) no "parses the ssh user out of the uri (got '$OUT')" ;; esac
# --- check assertions against the synthetic tree ----------------------------
echo
echo "--- check assertions ---"
KEYPATH="$TMP/snap/root/.ssh/id_dc0_power"
CFGPATH="$TMP/snap/root/.ssh/config"
# no key at all must REFUSE (2), never report a clean tree
rm -f "$KEYPATH" "$CFGPATH"
run check vr1-dc0 "$URI"
chk "absent key REFUSES -> 2" "$RC" 2
case "$OUT" in *"could not look"*) ok "absent key says 'could not look' is not 'nothing wrong'" ;; *) no "absent key explains the refusal (got '$OUT')" ;; esac
ssh-keygen -q -t ed25519 -N '' -C 'dc0-power-test' -f "$KEYPATH" 2>/dev/null
if [ ! -f "$KEYPATH" ]; then
no "could not generate a test keypair (ssh-keygen missing?)"
else
# key present, config absent -> the config assertions must FAIL, not pass
rm -f "$CFGPATH"
run check vr1-dc0 "$URI"
case "$OUT" in *"[FAIL] ssh config present"*) ok "absent ssh config FAILS" ;; *) no "absent ssh config FAILS (got '$OUT')" ;; esac
[ "$RC" = 1 ]; chk "key-without-config exits 1" "$?" 0
# a config that mentions the key but for the WRONG Host must not satisfy the
# binding assertion -- this is the cross-DC swap the stanza check exists for.
printf 'Host 10.12.68.2\n IdentityFile %s\n IdentitiesOnly yes\n' "$KEYPATH" > "$CFGPATH"
run check vr1-dc0 "$URI"
case "$OUT" in *"[FAIL] config binds Host 10.12.8.2"*) ok "config bound to the WRONG rack FAILS" ;; *) no "config bound to the WRONG rack FAILS (got '$OUT')" ;; esac
# correct binding, but IdentitiesOnly missing
printf 'Host 10.12.8.2\n IdentityFile %s\n' "$KEYPATH" > "$CFGPATH"
run check vr1-dc0 "$URI"
case "$OUT" in *"[FAIL] config sets IdentitiesOnly"*) ok "missing IdentitiesOnly FAILS" ;; *) no "missing IdentitiesOnly FAILS (got '$OUT')" ;; esac
# fully correct config + reachable virsh -> pass
printf 'Host 10.12.8.2\n IdentityFile %s\n IdentitiesOnly yes\n' "$KEYPATH" > "$CFGPATH"
chmod 600 "$KEYPATH"
OUT="$(STUB_VIRSH=ok bash "$SCRIPT" check vr1-dc0 "$URI" 2>&1)"; RC=$?
case "$OUT" in *"[ok] config binds Host 10.12.8.2"*) ok "correct config binding passes" ;; *) no "correct config binding passes (got '$OUT')" ;; esac
case "$OUT" in *"[ok] REAL virsh ("*") connect"*) ok "reachable virsh passes" ;; *) no "reachable virsh passes (got '$OUT')" ;; esac
# THE ARTIFACT ASSERTIONS. A perfect config with an unreachable or empty
# libvirt must FAIL -- otherwise the gate green-lights a region that cannot
# actually power anything, which is the whole point of checking.
OUT="$(STUB_VIRSH=refuse bash "$SCRIPT" check vr1-dc0 "$URI" 2>&1)"; RC=$?
case "$OUT" in *"[FAIL] REAL virsh ("*") connect"*) ok "unreachable virsh FAILS despite a perfect config" ;; *) no "unreachable virsh FAILS (got '$OUT')" ;; esac
[ "$RC" = 1 ]; chk "unreachable virsh exits 1" "$?" 0
OUT="$(STUB_VIRSH=empty bash "$SCRIPT" check vr1-dc0 "$URI" 2>&1)"; RC=$?
case "$OUT" in *"[FAIL] virsh enumerates domains"*) ok "virsh listing ZERO domains FAILS (a power op would no-op)" ;; *) no "virsh listing zero domains FAILS (got '$OUT')" ;; esac
[ "$RC" = 1 ]; chk "zero-domain virsh exits 1" "$?" 0
fi
# --- install refusals -------------------------------------------------------
echo
echo "--- install path ---"
run install vr1-dc0 "$URI" < /dev/null
chk "empty stdin key -> 2" "$RC" 2
printf 'this is not a key\n' > "$TMP/notakey"
run install vr1-dc0 "$URI" < "$TMP/notakey"
chk "unparseable stdin key -> 2" "$RC" 2
case "$OUT" in *"not a readable private key"*) ok "bad key names the reason" ;; *) no "bad key names the reason (got '$OUT')" ;; esac
ssh-keygen -q -t ed25519 -N '' -C 'test-k1' -f "$TMP/k1" 2>/dev/null
if [ ! -f "$TMP/k1" ]; then
no "could not generate a test keypair for install (ssh-keygen missing?)"
else
# DRY RUN must not write and must not claim to have installed.
# Remove the sibling .pub too: `ssh-keygen -lf <priv>` prefers an adjacent
# .pub, which is exactly the trap fp_of_privkey was hardened against.
rm -f "$KEYPATH" "$KEYPATH.pub" "$CFGPATH"
run install vr1-dc0 "$URI" < "$TMP/k1"
case "$OUT" in *"DRY RUN"*) ok "install without --commit is a DRY RUN" ;; *) no "install without --commit is a DRY RUN (got '$OUT')" ;; esac
[ -f "$KEYPATH" ] && no "DRY RUN wrote the key anyway" || ok "DRY RUN wrote nothing"
# --commit installs, then self-verifies.
# NOTE: overall exit 0 is UNREACHABLE in this synthetic tree -- an unprivileged
# test process cannot make the file root-owned, so the "key owned by root"
# assertion legitimately fails here. Asserting that specific line still passes
# in production is what the LIVE capture is for; here we assert the lines the
# harness can actually reach, and assert the root check FIRES rather than
# papering over it.
OUT="$(STUB_VIRSH=ok bash "$SCRIPT" install vr1-dc0 "$URI" --commit < "$TMP/k1" 2>&1)"; RC=$?
[ -f "$KEYPATH" ] && ok "--commit installed the key" || no "--commit installed the key"
case "$OUT" in *"appended Host 10.12.8.2"*) ok "--commit appended the Host stanza" ;; *) no "--commit appended the Host stanza (got '$OUT')" ;; esac
case "$OUT" in *"[ok] key mode is 0600"*) ok "--commit self-verify sees mode 0600" ;; *) no "--commit self-verify sees mode 0600 (got '$OUT')" ;; esac
case "$OUT" in *"[ok] config binds Host 10.12.8.2"*) ok "--commit self-verify sees the binding" ;; *) no "--commit self-verify sees the binding" ;; esac
case "$OUT" in *"[FAIL] key owned by root"*) ok "root-ownership assertion FIRES when not root-owned" ;; *) no "root-ownership assertion fires when not root-owned (got '$OUT')" ;; esac
# re-running with the SAME key is idempotent, not a second stanza
OUT="$(STUB_VIRSH=ok bash "$SCRIPT" install vr1-dc0 "$URI" --commit < "$TMP/k1" 2>&1)"; RC=$?
case "$OUT" in *"[idempotent]"*) ok "re-install of the same key is idempotent" ;; *) no "re-install of the same key is idempotent (got '$OUT')" ;; esac
N_STANZA="$(grep -c '^Host 10.12.8.2$' "$CFGPATH")"
chk "re-install did not duplicate the Host stanza" "$N_STANZA" "1"
# A DIFFERENT key over an existing one must REFUSE -- a silent overwrite here
# re-points a live region's power path at another DC's rack.
ssh-keygen -q -t ed25519 -N '' -C 'test-k2' -f "$TMP/k2" 2>/dev/null
OUT="$(STUB_VIRSH=ok bash "$SCRIPT" install vr1-dc0 "$URI" --commit < "$TMP/k2" 2>&1)"; RC=$?
chk "installing a DIFFERENT key over an existing one REFUSES -> 1" "$RC" 1
case "$OUT" in *"already holds a DIFFERENT key"*) ok "the refusal names the cross-DC swap risk" ;; *) no "the refusal names the swap risk (got '$OUT')" ;; esac
# and it must NOT have overwritten
FP_NOW="$(ssh-keygen -lf "$KEYPATH" 2>/dev/null | awk '{print $2}')"
FP_K1="$(ssh-keygen -lf "$TMP/k1" 2>/dev/null | awk '{print $2}')"
chk "the refused install left the original key in place" "$FP_NOW" "$FP_K1"
fi
echo
echo "maas-region-power-key: $P passed, $F failed"
[ "$F" -eq 0 ] || exit 1