diff --git a/scripts/maas b/scripts/maas new file mode 100644 index 0000000..c57f41e --- /dev/null +++ b/scripts/maas @@ -0,0 +1,17 @@ +#!/usr/bin/env bash +# fake maas: serves read endpoints from fixtures. `vlans read ` is filtered by +# fabric_id and MIMICS REAL MAAS by erroring (non-JSON, non-zero) on an unknown/absent +# fabric id -- this is what tripped the live run when a placeholder id leaked through. +prof="${1:-}"; obj="${2:-}"; act="${3:-}"; fab="${4:-}" +case "$obj $act" in + "subnets read") cat "${FIX_SUBNETS:?}"; exit 0 ;; + "spaces read") cat "${FIX_SPACES:?}"; exit 0 ;; + "ipranges read") cat "${FIX_IPRANGES:?}"; exit 0 ;; + "fabrics read") cat "${FIX_FABRICS:?}"; exit 0 ;; + "vlans read") + if jq -e --arg f "$fab" 'any(.[]; (.fabric_id|tostring)==$f)' "${FIX_VLANS:?}" >/dev/null 2>&1; then + jq --arg f "$fab" '[.[]|select((.fabric_id|tostring)==$f)]' "${FIX_VLANS:?}"; exit 0 + fi + echo "Unable to find fabric with id '$fab'." >&2; echo "Not Found"; exit 2 ;; +esac +echo "{}"; exit 0 diff --git a/scripts/phase-00-maas-standup.sh b/scripts/phase-00-maas-standup.sh index 76b8b62..f88612b 100644 --- a/scripts/phase-00-maas-standup.sh +++ b/scripts/phase-00-maas-standup.sh @@ -47,7 +47,9 @@ note() { echo " - $*"; } fail() { echo "FAIL: $*" >&2; FATAL=$((FATAL+1)); } need_jq || exit 1 -maas_q() { maas "$MAAS_PROFILE" "$@"; } +# read wrapper: emit valid JSON, or "[]" if the read failed / was not JSON, so a +# stray MAAS error (e.g. a bogus/absent fabric id) can never crash us under set -e. +maas_json() { local out; out="$(maas "$MAAS_PROFILE" "$@" 2>/dev/null || true)"; printf '%s' "$out" | jq empty 2>/dev/null && printf '%s' "$out" || printf '[]'; } emit() { # local desc="$1"; shift @@ -66,17 +68,17 @@ } # --- PATTERN-1 resolve-by-CIDR/name helpers (no hardcoded ids) --------------- -sub_id() { maas_q subnets read | jq -r --arg c "$1" '.[]|select(.cidr==$c)|.id' | head -1; } -sub_vid() { maas_q subnets read | jq -r --arg c "$1" '.[]|select(.cidr==$c)|(.vlan.vid|tostring)' | head -1; } -sub_fabid() { maas_q subnets read | jq -r --arg c "$1" '.[]|select(.cidr==$c)|(.vlan.fabric_id|tostring)' | head -1; } -sub_space() { maas_q subnets read | jq -r --arg c "$1" '.[]|select(.cidr==$c)|(.space // "")' | head -1; } -sub_field() { maas_q subnets read | jq -r --arg c "$1" --arg f "$2" '.[]|select(.cidr==$c)|(.[$f] // "")' | head -1; } -sub_mtu() { maas_q subnets read | jq -r --arg c "$1" '.[]|select(.cidr==$c)|(.vlan.mtu|tostring)' | head -1; } -sub_dns() { maas_q subnets read | jq -r --arg c "$1" '.[]|select(.cidr==$c)|(.dns_servers // []|join(","))' | head -1; } -space_id() { maas_q spaces read | jq -r --arg n "$1" '.[]|select(.name==$n)|(.id|tostring)' | head -1; } -fab_byname() { maas_q fabrics read | jq -r --arg n "$1" '.[]|select(.name==$n)|(.id|tostring)' | head -1; } -vlanobj() { maas_q vlans read "$1" | jq -r --arg v "$2" '.[]|select((.vid|tostring)==$v)|(.id|tostring)' | head -1; } -vlanspace() { maas_q vlans read "$1" | jq -r --arg v "$2" '.[]|select((.vid|tostring)==$v)|(.space // "")' | head -1; } +sub_id() { maas_json subnets read | jq -r --arg c "$1" '.[]|select(.cidr==$c)|.id' | head -1; } +sub_vid() { maas_json subnets read | jq -r --arg c "$1" '.[]|select(.cidr==$c)|(.vlan.vid|tostring)' | head -1; } +sub_fabid() { maas_json subnets read | jq -r --arg c "$1" '.[]|select(.cidr==$c)|(.vlan.fabric_id|tostring)' | head -1; } +sub_space() { maas_json subnets read | jq -r --arg c "$1" '.[]|select(.cidr==$c)|(.space // "")' | head -1; } +sub_field() { maas_json subnets read | jq -r --arg c "$1" --arg f "$2" '.[]|select(.cidr==$c)|(.[$f] // "")' | head -1; } +sub_mtu() { maas_json subnets read | jq -r --arg c "$1" '.[]|select(.cidr==$c)|(.vlan.mtu|tostring)' | head -1; } +sub_dns() { maas_json subnets read | jq -r --arg c "$1" '.[]|select(.cidr==$c)|(.dns_servers // []|join(","))' | head -1; } +space_id() { maas_json spaces read | jq -r --arg n "$1" '.[]|select(.name==$n)|(.id|tostring)' | head -1; } +fab_byname() { maas_json fabrics read | jq -r --arg n "$1" '.[]|select(.name==$n)|(.id|tostring)' | head -1; } +vlanobj() { case "$1" in "<"*) return;; esac; maas_json vlans read "$1" | jq -r --arg v "$2" '.[]|select((.vid|tostring)==$v)|(.id|tostring)' | head -1; } +vlanspace() { case "$1" in "<"*) return;; esac; maas_json vlans read "$1" | jq -r --arg v "$2" '.[]|select((.vid|tostring)==$v)|(.space // "")' | head -1; } vlan0obj() { vlanobj "$1" 0; } # the untagged (vid 0) default VLAN of a fabric # --- target plane table (D-058): name|cidr|kind|vid|parent_cidr|gw|viplo|viphi|dnssrc @@ -208,7 +210,7 @@ # ---- reserved API-VIP band ---- if [ -n "$viplo" ]; then - if maas_q ipranges read | jq -e --arg lo "$viplo" '.[]|select(.start_ip==$lo)' >/dev/null 2>&1; then + if maas_json ipranges read | jq -e --arg lo "$viplo" '.[]|select(.start_ip==$lo)' >/dev/null 2>&1; then note "reserved range starting $viplo exists -- SKIP" else rsid="$(sub_id "$cidr")"; [ -n "$rsid" ] || rsid="" diff --git a/scripts/run-tests.sh b/scripts/run-tests.sh new file mode 100644 index 0000000..ea50f45 --- /dev/null +++ b/scripts/run-tests.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +# Behavior regression for phase-00-maas-standup.sh (D-058). Fake `maas` + real jq. +# Drives DRY-RUN and asserts WOULD/SKIP/DRIFT/refuse behaviour across scenarios. +set -uo pipefail +HERE="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +SCRIPT="$(cd "$HERE/../../scripts" && pwd)/phase-00-maas-standup.sh" +BIN="$HERE/fakebin"; FIX="$HERE/fix" +chmod +x "$BIN"/* 2>/dev/null || true # GitHub Desktop lands files mode 100644 +command -v jq >/dev/null || { echo "FAIL: jq required"; exit 1; } +python3 "$HERE/make_fixtures.py" >/dev/null +rc_all=0; OUT="$(mktemp)" + +run() { #