#!/usr/bin/env bash # Gate M2: every fixture's expect.yaml passes (including v1 and v3) and the PDU # budget holds. Procedure: docs/milestones/M2-discovery-and-profiles.md. # 1. go test -race ./internal/fixtures: every testdata/snmprec/*.expect.yaml over IPv6 and IPv4. # 2. `nmon profile test` against the v1 and v3 fixtures served by the simagent CLI. # 3. With NMON_LIBRENMS_DIR set: real LibreNMS recordings must match their profile, with no type mismatch. # Needs: go, make, awk. set -uo pipefail # Maintainability is a hard rule (docs/MAINTAINABILITY.md): no gate passes with lint findings. ( cd "$(git rev-parse --show-toplevel)" && make lint ) || { echo "FAIL: make lint"; exit 1; } ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" cd "$ROOT" TMP="$(mktemp -d)" FAILS=0 AGENT_PID="" pass() { echo "PASS: $*"; } fail() { echo "FAIL: $*"; FAILS=$((FAILS + 1)); } die() { fail "$*"; exit 1; } cleanup() { [ -n "$AGENT_PID" ] && kill "$AGENT_PID" 2>/dev/null rm -rf "$TMP" } trap cleanup EXIT trap 'exit 130' INT TERM pick_port() { local p while :; do p=$((20000 + RANDOM % 30000)) ss -H -lntu 2>/dev/null | awk '{print $5}' | grep -q "[:.]$p\$" || { echo "$p"; return; } done } # 1. Fixture harness, without the real-device pass (that is step 3). echo "== fixtures (go test -race ./internal/fixtures)" if env -u NMON_LIBRENMS_DIR go test -race -count=1 -v ./internal/fixtures/... >"$TMP/fixtures.log" 2>&1; then pass "go test -race ./internal/fixtures" else grep -E "^\s*(---|===) (FAIL)|^\s+\S+_test.go:[0-9]+:|^FAIL|panic" "$TMP/fixtures.log" | head -60 fail "go test -race ./internal/fixtures" fi printf '%-24s %-6s %s\n' FIXTURE RESULT SECONDS awk ' /^ --- (PASS|FAIL): TestFixtures\/[^\/ ]+ / { n = $3; sub(/^TestFixtures\//, "", n); t = $4; gsub(/[()s]/, "", t); printf "%-24s %-6s %s\n", n, $2 == "PASS:" ? "PASS" : "FAIL", t } ' "$TMP/fixtures.log" | sort N="$(grep -cE '^ --- PASS: TestFixtures/[^/ ]+ ' "$TMP/fixtures.log")" [ "$N" -ge 25 ] && pass "$N fixtures passed over IPv6 and IPv4" || fail "only $N fixtures passed" for t in TestRenumberKeepsInterfaceIDs TestRMAOpensHardwareReplacedReview TestStackSwapOpensOneReviewAndMergesNothing TestV1DiscoversWithGetNext TestV3AuthPrivWorks; do grep -q -- "--- PASS: $t " "$TMP/fixtures.log" && pass "$t" || fail "$t" done # 2. nmon profile test against the v1 and v3 fixtures. echo "== nmon profile test (v1, v3)" make build >"$TMP/build.log" 2>&1 || { tail -n 20 "$TMP/build.log"; die "make build"; } go build -o "$TMP/simagent" ./internal/simagent/cmd/simagent || die "build simagent" serve() { # serve -> sets PORT, AGENT_PID PORT="$(pick_port)" "$TMP/simagent" -addr "[::1]:$PORT" -agent "testdata/snmprec/$1.agent.yaml" >"$TMP/simagent-$1.log" 2>&1 & AGENT_PID=$! for _ in $(seq 1 50); do grep -q listening "$TMP/simagent-$1.log" 2>/dev/null && break; sleep 0.1; done grep -q listening "$TMP/simagent-$1.log" || { cat "$TMP/simagent-$1.log"; die "simagent $1 did not start"; } } stop() { kill "$AGENT_PID" 2>/dev/null; wait "$AGENT_PID" 2>/dev/null; AGENT_PID=""; } serve v1 if ./nmon profile test ::1 --port "$PORT" --version v1 --community nmon-v1 >"$TMP/v1.out" 2>&1 && grep -q '^profile generic' "$TMP/v1.out" && grep -q 'counter_32bit_fast_link' "$TMP/v1.out" && grep -q ' 0 type mismatches' "$TMP/v1.out"; then pass "v1: $(grep -m1 '^interfaces' "$TMP/v1.out" | tr -s ' '), $(grep 'total ' "$TMP/v1.out" | tr -s ' ')" else cat "$TMP/v1.out"; fail "nmon profile test against the v1 fixture" fi stop serve v3 if ./nmon profile test ::1 --port "$PORT" --version v3 --v3-user nmon --v3-auth-proto SHA256 --v3-auth-pass authpass-123 \ --v3-priv-proto AES --v3-priv-pass privpass-123 >"$TMP/v3.out" 2>&1 && grep -q '^profile generic' "$TMP/v3.out" && grep -q ' 0 type mismatches' "$TMP/v3.out"; then pass "v3 authPriv: $(grep -m1 '^interfaces' "$TMP/v3.out" | tr -s ' '), $(grep 'total ' "$TMP/v3.out" | tr -s ' ')" else cat "$TMP/v3.out"; fail "nmon profile test against the v3 fixture" fi stop # 3. Real device recordings, when a LibreNMS checkout is available. echo "== real-device recordings" if [ -n "${NMON_LIBRENMS_DIR:-}" ]; then if go test -count=1 -v -run TestLibreNMSRecordings ./internal/fixtures/ >"$TMP/real.log" 2>&1; then pass "real recordings under $NMON_LIBRENMS_DIR" else grep -E "^\s+\S+_test.go:[0-9]+:|^--- FAIL|^ --- FAIL" "$TMP/real.log" | grep -v REAL | head -40 fail "real recordings" fi grep -h REAL "$TMP/real.log" | sed -E 's/^ *[a-z_]+_test.go:[0-9]+: //' | sort else echo "skipped: NMON_LIBRENMS_DIR is not set" fi if [ "$FAILS" -eq 0 ]; then echo "GATE M2: PASS"; else echo "GATE M2: FAIL ($FAILS)"; exit 1; fi