Manual Test Runbook — D4 + X4: Kyverno Policy Bundle
Owner: Sagar | Time: ~20 min (offline) · +30 min (optional kind-cluster install) | Sandbox: none for Parts A–B; local kind for Part C
D4 ships the SnowOps Kyverno bundle (policy/kyverno/rules/). X4 ships the
test harness (policy/kyverno/tests/). This runbook validates both —
offline via kyverno test, then end-to-end via an actual admission round-trip
on a local kind cluster (Part C is optional).
Prerequisites
- Local tooling:
kyverno >= 1.18(brew install kyvernoon macOS).- (Part C only)
kind,kubectl, Docker daemon running.
- Working directory: repo root.
- Clean working tree.
Steps
Part A — kyverno test per-rule fixtures (~2 min)
- Run the full X4 suite:
- Expected:
Running 5 kyverno test suites...
── disallow-latest-tag ── PASS
── disallow-privileged-containers ── PASS
── require-network-policy ── PASS
── require-pod-labels ── PASS
── require-signed-images ── PASS
All 5 suites passed.
- Per-rule breakdown:
disallow-latest-tag— 6 assertions (pass + fail per rule × 3 pods × 2 rules)disallow-privileged-containers— 10 assertions (5 pods × 2 rules)require-pod-labels— 3 assertions (well-labelled / missing-owner / no-labels)require-network-policy— 1 assertion (Namespace → generated default-deny NetworkPolicy matchestests/require-network-policy/generated.yaml)-
require-signed-images— 1 assertion (non-ACR image short-circuits toskip; ACR signature verification is exercised in Part C only — kyverno-test cannot reach a live registry) -
If a suite fails, fix the rule and the fixture before re-running. The wrapper prints the full kyverno-test output for any failing suite.
Part B — per-rule deep-dive (~5 min, optional)
- Run a single suite with verbose output to confirm individual results:
- Spot-check the result table:
tagged-pod× both rules →Pass / Okuntagged-pod×require-image-tag→Pass / Ok(test assertsfail)latest-tag-pod×disallow-latest-tag→Pass / Ok(test assertsfail)
"Result: Pass" in the table = "the assertion matched". "Reason: Ok" = the policy WAS evaluated (vs "Excluded" / "Not found"). Both must be present for a clean run.
Part C — live admission on a kind cluster (~30 min, optional)
Required before signing off the rule bundle for client install. Skip if only iterating on rule wording or the test harness.
- Spin up a local kind cluster + install Kyverno:
kind create cluster --name snowops-d4-test
kubectl create namespace kyverno
# Kyverno v1.18.x install via Helm
helm repo add kyverno https://kyverno.github.io/kyverno/
helm install kyverno kyverno/kyverno --namespace kyverno --version 3.4.x \
--set replicaCount=1
kubectl -n kyverno rollout status deploy/kyverno-admission-controller --timeout=5m
- Apply the SnowOps rule bundle:
kubectl apply -f policy/kyverno/rules/
kubectl get clusterpolicy
# NAME BACKGROUND VALIDATE ACTION READY
# snowops-disallow-latest-tag true Enforce true
# snowops-disallow-privileged-containers true Enforce true
# snowops-require-default-deny-netpol true Enforce true
# snowops-require-pod-labels true Enforce true
# snowops-require-signed-images true Enforce true
- Validate each rule end-to-end. Create a workload namespace first:
kubectl create namespace app
# Expect: kyverno generates a default-deny NetworkPolicy automatically.
kubectl -n app get networkpolicy default-deny
# NAME POD-SELECTOR AGE
# default-deny <none> 5s
-
Attempt to create a Pod with
:latest: -
Attempt to create a privileged Pod (use a quick manifest):
kubectl -n app apply -f - <<'EOF' apiVersion: v1 kind: Pod metadata: name: priv-test labels: app.kubernetes.io/name: priv-test app.kubernetes.io/version: 1.0.0 app.kubernetes.io/part-of: snowops-suite snowops.io/owner: platform-team spec: containers: - name: bad image: nginx:1.27.0 securityContext: privileged: true EOF # Expect rejection: "Privileged containers are not allowed." -
Attempt to create a Pod missing the SnowOps labels:
-
Create a fully-conformant Pod and confirm admission succeeds:
kubectl -n app apply -f - <<'EOF' apiVersion: v1 kind: Pod metadata: name: good-pod labels: app.kubernetes.io/name: good-pod app.kubernetes.io/version: 1.0.0 app.kubernetes.io/part-of: snowops-suite snowops.io/owner: platform-team spec: containers: - name: web image: nginx:1.27.0 EOF # Expect: pod/good-pod created -
Confirm the exclude block keeps system namespaces working:
-
Cleanup:
Pass criteria
- Part A —
policy/kyverno/tests/run-tests.shreports 5/5 suites passing. - Part B — per-suite
kyverno test --detailed-resultsruns showReason: Okfor every evaluated case (noExcluded/Not found). - (Part C) kind cluster boots and Kyverno admission controller is
Ready. - (Part C)
kubectl apply -f policy/kyverno/rules/succeeds andkubectl get clusterpolicyshows all 5 policiesReady=true. - (Part C) Each of the four failure paths (latest tag, privileged, missing labels, untagged image) is rejected by the admission webhook with the expected error message.
- (Part C) A fully-conformant Pod is admitted.
- (Part C)
default-denyNetworkPolicy is auto-generated in workload namespaces and NOT generated inkube-system/kyverno/default/kube-public/kube-node-lease.
Teardown
D4 + X4 have no cloud side-effects. Part C creates a local kind cluster that
must be deleted with kind delete cluster --name snowops-d4-test.
If Part A flagged a regression, revert the offending file:
Sign-off
- Tester: _ | Date: _ | Result: PASS / FAIL / N/A
- Notes: