Newer
Older
openstack-caracal-dc-dc / tests / node-vm / run-tests.sh
#!/usr/bin/env bash
# tests/node-vm/run-tests.sh -- guard for modules/node-vm (MAC pinning).
# Static assertions that the interface MAC pin is present and correctly
# shaped (regression guard: an unpinned node NIC MAC can be silently
# REGENERATED by an in-place apply, stranding the node in MAAS -- the
# 2026-07-21 commissioning incident, docs/audit/commissioning-diag-20260721.txt).
# Also asserts the inner root actually pins all 9 nodes x 6 planes.
# Runs `tofu validate` on the module when the binary is available; skips
# gracefully when it is not. Exit: 0 all pass | 1 any case failed. ASCII + LF.
set -uo pipefail
HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT="$(cd "$HERE/../.." && pwd)"
MOD="$ROOT/opentofu/modules/node-vm"
INNER="$ROOT/opentofu/vr1-dc0-substrate/main.tf"
PASS=0; FAIL=0
ok(){ echo "  PASS  $1"; PASS=$((PASS+1)); }
no(){ echo "  FAIL  $1"; FAIL=$((FAIL+1)); }

[ -f "$MOD/main.tf" ] && ok "T1 module main.tf present" || no "T1 module main.tf present"
[ -f "$MOD/variables.tf" ] && ok "T2 module variables.tf present" || no "T2 module variables.tf present"

# T3: interface_macs variable exists with a safe default (unpinned allowed
# ONLY as an explicit empty default, for pre-enlistment use)
VAR_BLOCK="$(awk '/^variable "interface_macs" \{/{f=1} f{print; n+=gsub(/\{/,"{"); n-=gsub(/\}/,"}"); if(n==0 && f)exit}' "$MOD/variables.tf")"
grep -q 'type *= *list(string)' <<<"$VAR_BLOCK" \
  && ok "T3 interface_macs is list(string)" \
  || no "T3 interface_macs is list(string)"
grep -q 'default *= *\[\]' <<<"$VAR_BLOCK" \
  && ok "T4 interface_macs defaults to empty (opt-in pin)" \
  || no "T4 interface_macs defaults to empty (opt-in pin)"

# T5/T6: both validations present -- all-or-nothing length, and MAC format
grep -q 'length(var.interface_macs) == length(var.network_names)' <<<"$VAR_BLOCK" \
  && ok "T5 validation: all-or-nothing (no partial pinning)" \
  || no "T5 validation: all-or-nothing (no partial pinning)"
grep -q '0-9a-fA-F' <<<"$VAR_BLOCK" \
  && ok "T6 validation: MAC format regex present" \
  || no "T6 validation: MAC format regex present"

# T7: the interfaces comprehension actually consumes the pin, guarded on
# emptiness, in the schema-verified shape mac = { address = ... }
IF_LINE="$(grep -n 'mac *= *length(var.interface_macs) > 0 ? { address = var.interface_macs\[i\] } : null' "$MOD/main.tf" || true)"
[ -n "$IF_LINE" ] \
  && ok "T7 interfaces consume the pin (mac = { address = ... } / null)" \
  || no "T7 interfaces consume the pin (mac = { address = ... } / null)"

# T8: the inner root pins EVERY node: 9 macs lists, 54 MAC literals total,
# and the module call passes interface_macs through.
if [ -f "$INNER" ]; then
  MACS_LISTS="$(grep -c 'macs = \[' "$INNER")"
  MAC_LITERALS="$(grep -oE '"([0-9a-f]{2}:){5}[0-9a-f]{2}"' "$INNER" | wc -l)"
  [ "$MACS_LISTS" -eq 9 ] \
    && ok "T8 inner root: 9 per-node macs lists" \
    || no "T8 inner root: 9 per-node macs lists (found $MACS_LISTS)"
  [ "$MAC_LITERALS" -eq 54 ] \
    && ok "T9 inner root: 54 pinned MAC literals (9 nodes x 6 planes)" \
    || no "T9 inner root: 54 pinned MAC literals (found $MAC_LITERALS)"
  DUPES="$(grep -oE '"([0-9a-f]{2}:){5}[0-9a-f]{2}"' "$INNER" | sort | uniq -d | wc -l)"
  [ "$DUPES" -eq 0 ] \
    && ok "T10 inner root: no duplicate MACs" \
    || no "T10 inner root: no duplicate MACs ($DUPES duplicated)"
  grep -q 'interface_macs *= *each.value.macs' "$INNER" \
    && ok "T11 inner root: module call passes interface_macs" \
    || no "T11 inner root: module call passes interface_macs"
else
  no "T8-T11 inner root main.tf present"
fi

# T13-T15: power-ownership guard (operator-ruled 2026-07-21) -- the domain
# block must ignore_changes on running (MAAS owns node power; an apply must
# never flip it), must NOT ignore anything else there, and must cite MAAS.
DOM_BLOCK="$(awk '/^resource "libvirt_domain" "node" \{/{f=1} f{print; n+=gsub(/\{/,"{"); n-=gsub(/\}/,"}"); if(n==0 && f)exit}' "$MOD/main.tf")"
grep -q 'ignore_changes = \[running\]' <<<"$DOM_BLOCK" \
  && ok "T13 domain ignores running drift (MAAS owns power)" \
  || no "T13 domain ignores running drift (MAAS owns power)"
IGNORE_COUNT="$(grep -c 'ignore_changes' <<<"$DOM_BLOCK")"
[ "$IGNORE_COUNT" -eq 1 ] \
  && ok "T14 exactly one ignore_changes in the domain block (no scope creep)" \
  || no "T14 exactly one ignore_changes in the domain block (found $IGNORE_COUNT)"
grep -q 'MAAS' <<<"$DOM_BLOCK" \
  && ok "T15 guard cites MAAS power ownership" \
  || no "T15 guard cites MAAS power ownership"

# T12: module still validates when tofu is available (init -backend=false is
# offline once the provider is in the plugin cache; skip cleanly otherwise)
if command -v tofu >/dev/null 2>&1; then
  TMP="$(mktemp -d)"; trap 'rm -rf "$TMP"' EXIT
  cp "$MOD"/*.tf "$TMP"/
  if (cd "$TMP" && tofu init -backend=false -input=false >/dev/null 2>&1 \
      && tofu validate >/dev/null 2>&1); then
    ok "T12 module validates (tofu validate)"
  else
    no "T12 module validates (tofu validate)"
  fi
else
  echo "  SKIP  T12 tofu binary not available (static checks above still bind)"
fi

echo; echo "node-vm: $PASS passed, $FAIL failed"
[ "$FAIL" -eq 0 ]