Implements the two L1 rendering tests that were failing scaffolds: - egress-class-render (R-NET-05/06/07): renders vpn + internet fixtures, diffs each against a committed golden, asserts the vpn steering label is present on vpn and absent on internet (fail-closed), and that flipping the class changes ONLY the egress path (R-NET-07). This fixes the --requirements R-NET-05 merge gate. - default-deny-baseline-render (R-NET-01): asserts every render carries the namespaced default-deny baseline, shaped deny-all + both directions. Each test carries a pure-offline render-check.sh (renderer + golden diff, no cluster). run.sh runs L1 render checks offline and emits a REAL pass/fail JUnit (docs/14: L1 rendering assertions run in CI without a cluster) instead of skipping L1 with the cluster-gated layers; the chainsaw-test.yaml wrappers invoke the same render-check.sh so an in-cluster run executes the identical assertion. Co-Authored-By: Claude Opus 4.8 <[email protected]>
51 lines
2.8 KiB
Bash
Executable File
51 lines
2.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# =============================================================================
|
|
# L1 render check (offline, no cluster) — egress-class-render
|
|
# Serves: R-NET-05 (egress is a declared property), R-NET-06/07 (vpn steering /
|
|
# flip only changes the egress path). Run by conformance/run.sh --layer l1 and by
|
|
# ci/l1.sh. Pure bash + the renderer (catalog/render.py); no cluster, no chainsaw.
|
|
#
|
|
# GIVEN two ServiceDefinitions identical except `egress: vpn` vs `egress: internet`,
|
|
# WHEN the renderer produces resources,
|
|
# THEN (1) each matches its committed golden byte-for-byte,
|
|
# (2) the vpn render carries the egress-class=vpn steering label and the
|
|
# internet render does NOT (fail-closed: no silent vpn path on internet),
|
|
# (3) the two renders differ ONLY in the egress-class value (R-NET-07):
|
|
# flipping the class changes the egress path, not the workload spec.
|
|
# =============================================================================
|
|
set -euo pipefail
|
|
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
ROOT="$(cd "${DIR}/../../../.." && pwd)" # repo root
|
|
RENDER="${ROOT}/catalog/render.py"
|
|
PYTHON="${PYTHON:-python3}"
|
|
LBL="homelab.keithjackson.dev"
|
|
|
|
tmp="$(mktemp -d)"
|
|
trap 'rm -rf "${tmp}"' EXIT
|
|
|
|
fail() { echo "RENDER-CHECK FAIL (egress-class-render): $*" >&2; exit 1; }
|
|
|
|
# (1) golden match for both classes.
|
|
"${PYTHON}" "${RENDER}" "${DIR}/fixtures/svcdef-egress-vpn.yaml" > "${tmp}/vpn.yaml"
|
|
"${PYTHON}" "${RENDER}" "${DIR}/fixtures/svcdef-egress-internet.yaml" > "${tmp}/internet.yaml"
|
|
diff -u "${DIR}/golden/egress-vpn.expected.yaml" "${tmp}/vpn.yaml" || fail "vpn render drifted from golden"
|
|
diff -u "${DIR}/golden/egress-internet.expected.yaml" "${tmp}/internet.yaml" || fail "internet render drifted from golden"
|
|
|
|
# (2) vpn steering label present on vpn, absent on internet (R-NET-05, fail-closed).
|
|
grep -q "${LBL}/egress-class: vpn" "${tmp}/vpn.yaml" \
|
|
|| fail "vpn render is missing the egress-class=vpn steering label"
|
|
if grep -q "${LBL}/egress-class: vpn" "${tmp}/internet.yaml"; then
|
|
fail "internet render must NOT carry the vpn steering label (fail-open leak)"
|
|
fi
|
|
grep -q "${LBL}/egress-class: internet" "${tmp}/internet.yaml" \
|
|
|| fail "internet render is missing the egress-class=internet label"
|
|
|
|
# (3) R-NET-07: the only difference is the egress-class value. Neutralize the class
|
|
# value in each render; the rest MUST be byte-identical.
|
|
sed 's#egress-class: vpn#egress-class: CLASS#' "${tmp}/vpn.yaml" > "${tmp}/vpn.norm"
|
|
sed 's#egress-class: internet#egress-class: CLASS#' "${tmp}/internet.yaml" > "${tmp}/internet.norm"
|
|
diff -u "${tmp}/vpn.norm" "${tmp}/internet.norm" \
|
|
|| fail "flipping egress class changed more than the egress path (R-NET-07 violated)"
|
|
|
|
echo "render-check OK: egress-class-render (R-NET-05/06/07)"
|