- docs/MAINTAINABILITY.md: limits and architecture rules - .golangci.yml (pinned v2.14.0) + file-size check; make lint runs first in every gate - web/go.mod stops the Go tool from walking web/node_modules - layering: Target -> internal/snmptarget, TableKey -> profile.Metric.TableKey; poller no longer imports discovery Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Lx9FNLG2CqeBbVGGd9bFsP
106 lines
4.6 KiB
Bash
Executable File
106 lines
4.6 KiB
Bash
Executable File
#!/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 <fixture> -> 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
|