Add container-workload-render L1 test (R-PLAT-02)
The renderer's PaaS path (kind: container) had no verifying conformance test — the R-PLAT-02 token appears only in renderer comments, so a change touching catalog/ surfaced it as an uncovered touched-requirement (blocking the merge gate). Adds the container render analog of vm-lifecycle (R-PLAT-03): asserts a kind: container ServiceDefinition renders a Deployment (running the declared image) + a Service, with the egress-class/auth-mode labels admission requires. Offline render-check.sh, wrapped by a chainsaw Test for in-cluster runs. Co-Authored-By: Claude Opus 4.8 <[email protected]>
This commit is contained in:
1 parent
4a017c590e
commit
a79f24f75e
4 files changed
+254
No files matched your search
@@ -0,0 +1,36 @@
|
||||
# =============================================================================
|
||||
# L1 RENDERING — kind: container renders the PaaS workload (R-PLAT-02)
|
||||
# Catalog: docs/13 `kind: container` (PaaS default). The render analog of the
|
||||
# vm-lifecycle (R-PLAT-03) test for the container path.
|
||||
#
|
||||
# GIVEN a kind: container ServiceDefinition,
|
||||
# WHEN the renderer produces resources,
|
||||
# THEN it renders a Deployment (the container workload) + a Service, matching the
|
||||
# golden, with the egress-class + auth-mode labels admission requires.
|
||||
#
|
||||
# STATUS: IMPLEMENTED (T-0.3). render-check.sh is a pure offline check (renderer +
|
||||
# golden diff + structural asserts, no cluster). conformance/run.sh --layer l1 and
|
||||
# ci/l1.sh run it directly; this Chainsaw Test wraps the SAME script so an in-cluster
|
||||
# run executes the identical assertion. Rendering != enforcement (behavior at L2).
|
||||
# =============================================================================
|
||||
apiVersion: chainsaw.kyverno.io/v1alpha1
|
||||
kind: Test
|
||||
metadata:
|
||||
name: container-workload-render
|
||||
labels:
|
||||
conformance.home.k3s/R-PLAT-02: "true"
|
||||
annotations:
|
||||
conformance.home.k3s/requirements: "R-PLAT-02"
|
||||
conformance.home.k3s/layer: "L1"
|
||||
conformance.home.k3s/safety-critical: "false"
|
||||
conformance.home.k3s/intent: >-
|
||||
A kind: container ServiceDefinition renders a Deployment + Service (the PaaS path).
|
||||
spec:
|
||||
steps:
|
||||
- name: render-and-assert-container-workload
|
||||
description: >-
|
||||
Render a container ServiceDefinition and assert it produces a Deployment running
|
||||
the declared image plus a Service, with the required pod labels, matching golden.
|
||||
try:
|
||||
- script:
|
||||
content: bash ./render-check.sh
|
||||
@@ -0,0 +1,14 @@
|
||||
# L1 fixture: a minimal kind: container ServiceDefinition — the PaaS path (R-PLAT-02).
|
||||
# It must render a Deployment (the container workload) + a Service for its exposed port.
|
||||
# Validates against schemas/servicedefinition.schema.json.
|
||||
name: paas-demo
|
||||
kind: container
|
||||
source:
|
||||
image: nginx:1.27
|
||||
ingressRoutes:
|
||||
- host: paas-demo.home.keithjackson.dev
|
||||
port: 8080
|
||||
auth:
|
||||
mode: forward-auth
|
||||
groups:
|
||||
- platform-admins
|
||||
@@ -0,0 +1,152 @@
|
||||
---
|
||||
# Namespace (trust zone; default-deny + policies apply)
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: paas-demo
|
||||
labels:
|
||||
homelab.keithjackson.dev/app-namespace: 'true'
|
||||
gateway-access: shared
|
||||
---
|
||||
# Default-deny baseline (R-NET-01): deny all, allow only DNS
|
||||
apiVersion: networking.k8s.io/v1
|
||||
kind: NetworkPolicy
|
||||
metadata:
|
||||
name: default-deny
|
||||
namespace: paas-demo
|
||||
labels:
|
||||
homelab.keithjackson.dev/baseline: default-deny
|
||||
spec:
|
||||
podSelector: {}
|
||||
policyTypes:
|
||||
- Ingress
|
||||
- Egress
|
||||
ingress: []
|
||||
egress:
|
||||
- to:
|
||||
- namespaceSelector:
|
||||
matchLabels:
|
||||
kubernetes.io/metadata.name: kube-system
|
||||
ports:
|
||||
- protocol: UDP
|
||||
port: 53
|
||||
- protocol: TCP
|
||||
port: 53
|
||||
---
|
||||
# Deployment (R-PLAT-02)
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: paas-demo
|
||||
namespace: paas-demo
|
||||
labels:
|
||||
app.kubernetes.io/name: paas-demo
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: paas-demo
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: paas-demo
|
||||
homelab.keithjackson.dev/egress-class: internet
|
||||
homelab.keithjackson.dev/auth-mode: forward-auth
|
||||
annotations:
|
||||
homelab.keithjackson.dev/auth-mode: forward-auth
|
||||
homelab.keithjackson.dev/auth-groups: platform-admins
|
||||
spec:
|
||||
containers:
|
||||
- name: paas-demo
|
||||
image: nginx:1.27
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
---
|
||||
# Service
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: paas-demo
|
||||
namespace: paas-demo
|
||||
labels:
|
||||
app.kubernetes.io/name: paas-demo
|
||||
spec:
|
||||
selector:
|
||||
app.kubernetes.io/name: paas-demo
|
||||
ports:
|
||||
- name: port-8080
|
||||
port: 8080
|
||||
targetPort: 8080
|
||||
---
|
||||
# HTTPRoute paas-demo-8080 (R-TLS/R-DNS)
|
||||
apiVersion: gateway.networking.k8s.io/v1
|
||||
kind: HTTPRoute
|
||||
metadata:
|
||||
name: paas-demo-8080
|
||||
namespace: paas-demo
|
||||
labels:
|
||||
app.kubernetes.io/name: paas-demo
|
||||
annotations:
|
||||
homelab.keithjackson.dev/auth-mode: forward-auth
|
||||
homelab.keithjackson.dev/auth-groups: platform-admins
|
||||
spec:
|
||||
parentRefs:
|
||||
- name: shared
|
||||
namespace: gateway
|
||||
sectionName: https
|
||||
hostnames:
|
||||
- paas-demo.home.keithjackson.dev
|
||||
rules:
|
||||
- matches:
|
||||
- path:
|
||||
type: PathPrefix
|
||||
value: /
|
||||
backendRefs:
|
||||
- name: paas-demo
|
||||
port: 8080
|
||||
---
|
||||
# ServiceMonitor (R-OBS-01)
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: ServiceMonitor
|
||||
metadata:
|
||||
name: paas-demo
|
||||
namespace: paas-demo
|
||||
labels:
|
||||
release: kube-prometheus-stack
|
||||
app.kubernetes.io/name: paas-demo
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: paas-demo
|
||||
namespaceSelector:
|
||||
matchNames:
|
||||
- paas-demo
|
||||
endpoints:
|
||||
- port: metrics
|
||||
interval: 30s
|
||||
---
|
||||
# Flux ImageRepository (R-UPD)
|
||||
apiVersion: image.toolkit.fluxcd.io/v1beta2
|
||||
kind: ImageRepository
|
||||
metadata:
|
||||
name: paas-demo
|
||||
namespace: paas-demo
|
||||
spec:
|
||||
image: nginx
|
||||
interval: 1h0m0s
|
||||
---
|
||||
# Flux ImagePolicy (R-UPD)
|
||||
apiVersion: image.toolkit.fluxcd.io/v1beta2
|
||||
kind: ImagePolicy
|
||||
metadata:
|
||||
name: paas-demo
|
||||
namespace: paas-demo
|
||||
annotations:
|
||||
homelab.keithjackson.dev/update-channel: stable
|
||||
homelab.keithjackson.dev/auto-rollback: 'true'
|
||||
spec:
|
||||
imageRepositoryRef:
|
||||
name: paas-demo
|
||||
policy:
|
||||
semver:
|
||||
range: '>=0.0.0'
|
||||
@@ -0,0 +1,52 @@
|
||||
#!/usr/bin/env bash
|
||||
# =============================================================================
|
||||
# L1 render check (offline, no cluster) — container-workload-render
|
||||
# Serves: R-PLAT-02 (PaaS: a kind: container ServiceDefinition renders a container
|
||||
# workload). The render analog of vm-lifecycle (R-PLAT-03) for the container path.
|
||||
# Run by conformance/run.sh --layer l1 and ci/l1.sh. Pure bash + catalog/render.py.
|
||||
#
|
||||
# GIVEN a kind: container ServiceDefinition with an exposed port,
|
||||
# WHEN the renderer produces resources,
|
||||
# THEN (1) the render matches the committed golden byte-for-byte, and
|
||||
# (2) it includes a Deployment running the SD's image plus a Service for the
|
||||
# exposed port, and the pod carries the egress-class + auth-mode labels the
|
||||
# platform's Kyverno policies require (a rendered container that admission
|
||||
# would accept).
|
||||
# =============================================================================
|
||||
set -euo pipefail
|
||||
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
ROOT="$(cd "${DIR}/../../../.." && pwd)" # repo root
|
||||
RENDER="${ROOT}/catalog/render.py"
|
||||
PYTHON="${PYTHON:-python3}"
|
||||
|
||||
tmp="$(mktemp -d)"
|
||||
trap 'rm -rf "${tmp}"' EXIT
|
||||
|
||||
fail() { echo "RENDER-CHECK FAIL (container-workload-render): $*" >&2; exit 1; }
|
||||
|
||||
"${PYTHON}" "${RENDER}" "${DIR}/fixtures/svcdef.yaml" > "${tmp}/out.yaml"
|
||||
|
||||
# (1) golden match.
|
||||
diff -u "${DIR}/golden/expected.yaml" "${tmp}/out.yaml" || fail "render drifted from golden"
|
||||
|
||||
# (2) structural: a Deployment (image) + Service (port) + the required pod labels.
|
||||
"${PYTHON}" - "${tmp}/out.yaml" <<'PY' || fail "container render is missing the PaaS workload shape (R-PLAT-02)"
|
||||
import sys, yaml
|
||||
docs = [d for d in yaml.safe_load_all(open(sys.argv[1])) if d]
|
||||
by = {}
|
||||
for d in docs:
|
||||
by.setdefault(d.get("kind"), []).append(d)
|
||||
assert by.get("Deployment"), "expected a Deployment (the container workload)"
|
||||
dep = by["Deployment"][0]
|
||||
c = dep["spec"]["template"]["spec"]["containers"][0]
|
||||
assert c["image"] == "nginx:1.27", f"unexpected image {c['image']!r}"
|
||||
labels = dep["spec"]["template"]["metadata"]["labels"]
|
||||
L = "homelab.keithjackson.dev"
|
||||
assert labels.get(f"{L}/egress-class") in ("internet", "vpn"), "pod missing egress-class label"
|
||||
assert labels.get(f"{L}/auth-mode"), "pod missing auth-mode label"
|
||||
assert by.get("Service"), "expected a Service for the exposed port"
|
||||
ports = [p["port"] for p in by["Service"][0]["spec"]["ports"]]
|
||||
assert 8080 in ports, f"Service does not expose port 8080 ({ports})"
|
||||
PY
|
||||
|
||||
echo "render-check OK: container-workload-render (R-PLAT-02)"
|
||||
Reference in new issue
Block a user