T-0.4 Chainsaw conformance harness + coverage + behavior scaffolds (R-TEST-01..06)
Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
This commit is contained in:
1 parent
a778d923b6
commit
9d8e03892f
31 files changed
+2445
-2
No files matched your search
@@ -0,0 +1,32 @@
|
||||
# In-cluster conformance Job (L2/L3)
|
||||
|
||||
Per **ADR-0011** and **docs/14 "Where each layer runs"**: L0/L1 run in CI without a
|
||||
cluster, but **L2 behavioral and L3 fault-injection run as an in-cluster Chainsaw Job**
|
||||
on the dev/test cluster that **CI triggers and waits for**. CI holds **no** cluster
|
||||
credentials — the Job's own scoped ServiceAccount does (R-SEC-02).
|
||||
|
||||
## Files
|
||||
- `rbac.yaml` — the `conformance` namespace, the `conformance-runner` ServiceAccount, and
|
||||
a **least-privilege ClusterRole skeleton**. Rules start **empty** and are added — each
|
||||
commented with the test/requirement that needs it — only as the matching fault-injection
|
||||
test lands. This is a **privileged test identity**: never reused by workloads, its own
|
||||
audit item.
|
||||
- `chainsaw-job.yaml` — the Job skeleton. Single entrypoint `./run.sh`; CI sets `args` to
|
||||
`--requirements <touched IDs>` (merge gate) or `--layer all --strict-coverage` (phase
|
||||
exit). Image pinned to `versions.env` `CHAINSAW_IMAGE`.
|
||||
|
||||
## Flow
|
||||
1. CI (Gitea Actions, delivered in T-6.1b) creates a Job from `chainsaw-job.yaml` (via a
|
||||
trigger with narrow rights: create Job + read its logs/status — *not* a broad
|
||||
kubeconfig).
|
||||
2. The Job clones/loads the impl repo at the commit under test into `/workspace` and runs
|
||||
`./run.sh` under `conformance-runner`.
|
||||
3. `run.sh` runs Chainsaw over the selected tests and writes `report.junit.xml` +
|
||||
`coverage/` to the results volume (`/workspace/conformance/_artifacts`).
|
||||
4. CI collects those artifacts (results volume or the terminating log stream, which echoes
|
||||
their paths) and enforces the merge / phase-exit gate on them.
|
||||
|
||||
## Phase 0
|
||||
No cluster exists, so nothing here is applied yet. These manifests are reconciled by Flux
|
||||
from Phase 1+ and first exercised when L2/L3 can run (T-3.0 onward). In Phase 0 the same
|
||||
`run.sh` runs in CI's no-cluster stage and emits a **skipped** JUnit + the coverage report.
|
||||
@@ -0,0 +1,74 @@
|
||||
# =============================================================================
|
||||
# In-cluster Chainsaw conformance Job — SKELETON (ADR-0011; docs/14)
|
||||
#
|
||||
# CI (Gitea Actions, T-6.1b) TRIGGERS this Job and waits for it; CI holds NO
|
||||
# cluster credentials. The Job runs L1/L2/L3 under the scoped `conformance-runner`
|
||||
# ServiceAccount (rbac.yaml), writes JUnit + coverage to a results location that
|
||||
# CI collects, and enforces the merge / phase-exit gate on the outcome.
|
||||
#
|
||||
# Two invocation shapes (CI sets args):
|
||||
# * MERGE GATE: ./run.sh --requirements <touched R-IDs> (subset)
|
||||
# * PHASE EXIT: ./run.sh --layer all (full suite)
|
||||
#
|
||||
# Results artifact mechanism (delegated to WS-TEST by docs/14 open items):
|
||||
# Chosen = a shared results emptyDir + a terminating log stream, with an
|
||||
# OPTIONAL results PVC for durability across CI collection. For Phase 0 there is
|
||||
# no cluster; this manifest is reconciled by Flux from Phase 1+ and exercised
|
||||
# once L2/L3 can run. CI collects `report.junit.xml` + `coverage/` from the
|
||||
# results volume (or scrapes the pod log, which echoes their paths).
|
||||
#
|
||||
# NOT applied in Phase 0 (no cluster). Image tag pinned to versions.env CHAINSAW_IMAGE.
|
||||
# =============================================================================
|
||||
apiVersion: batch/v1
|
||||
kind: Job
|
||||
metadata:
|
||||
generateName: conformance-run-
|
||||
namespace: conformance
|
||||
labels:
|
||||
app.kubernetes.io/part-of: conformance
|
||||
spec:
|
||||
backoffLimit: 0 # deterministic: a failed conformance run is a failure, no retry
|
||||
ttlSecondsAfterFinished: 3600
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/part-of: conformance
|
||||
spec:
|
||||
serviceAccountName: conformance-runner # scoped test identity (rbac.yaml)
|
||||
restartPolicy: Never
|
||||
securityContext:
|
||||
runAsNonRoot: true
|
||||
seccompProfile: { type: RuntimeDefault }
|
||||
containers:
|
||||
- name: chainsaw
|
||||
# PIN to versions.env CHAINSAW_IMAGE (ghcr.io/kyverno/chainsaw:v0.2.12).
|
||||
image: ghcr.io/kyverno/chainsaw:v0.2.12
|
||||
workingDir: /workspace/conformance
|
||||
# The suite repo is delivered into /workspace (git clone init-container or
|
||||
# a Flux-populated volume). run.sh is the single entrypoint (identical to local).
|
||||
command: ["./run.sh"]
|
||||
# CI overrides args per invocation shape:
|
||||
# ["--requirements", "R-NET-01,R-NET-06"] (merge gate) OR
|
||||
# ["--layer", "all", "--strict-coverage"] (phase exit)
|
||||
args: ["--layer", "all", "--strict-coverage"]
|
||||
env:
|
||||
- name: CONF_ROOT
|
||||
value: /workspace/conformance
|
||||
volumeMounts:
|
||||
- { name: workspace, mountPath: /workspace }
|
||||
- { name: results, mountPath: /workspace/conformance/_artifacts }
|
||||
securityContext:
|
||||
allowPrivilegeEscalation: false
|
||||
readOnlyRootFilesystem: true
|
||||
capabilities: { drop: ["ALL"] }
|
||||
runAsNonRoot: true
|
||||
volumes:
|
||||
# Suite source: populated by an init-container git clone of the impl repo at
|
||||
# the commit under test, OR a Flux-synced source volume. (Init container
|
||||
# omitted from this skeleton; wire in T-6.1b.)
|
||||
- name: workspace
|
||||
emptyDir: {}
|
||||
# Results: CI collects report.junit.xml + coverage/ from here. Swap emptyDir
|
||||
# for a results PVC if durability beyond pod lifetime is required.
|
||||
- name: results
|
||||
emptyDir: {}
|
||||
@@ -0,0 +1,114 @@
|
||||
# =============================================================================
|
||||
# Scoped test ServiceAccount + RBAC for the in-cluster Chainsaw conformance Job
|
||||
# (ADR-0011; docs/14 "Where each layer runs"; R-SEC-02 least-privilege).
|
||||
#
|
||||
# SKELETON / PLACEHOLDER — RBAC is deliberately LEFT EMPTY here and authored
|
||||
# least-privilege as each fault-injection capability actually lands (T-3.0+). The
|
||||
# rule is: grant nothing until a committed test needs it, then grant exactly that
|
||||
# verb/resource, with a comment naming the test that requires it. This SA is a
|
||||
# PRIVILEGED TEST IDENTITY — it is its own audit item (R-SEC-02): it MUST NOT be
|
||||
# reused by workloads, and every widening is reviewed.
|
||||
#
|
||||
# CI holds NO cluster credentials. CI only triggers the Job (T-6.1b via Gitea
|
||||
# Actions); the Job runs under THIS ServiceAccount, so cluster access stays
|
||||
# in-cluster and scoped (docs/14, R-SEC-02).
|
||||
#
|
||||
# Deployment: reconciled by Flux into the `conformance` namespace once the cluster
|
||||
# exists (Phase 1+). Not applied in Phase 0 (no cluster).
|
||||
# =============================================================================
|
||||
apiVersion: v1
|
||||
kind: Namespace
|
||||
metadata:
|
||||
name: conformance
|
||||
labels:
|
||||
app.kubernetes.io/part-of: conformance
|
||||
# The test namespace itself is exempt from nothing: the platform default-deny
|
||||
# baseline still applies; the Job opens only the egress it declares.
|
||||
pod-security.kubernetes.io/enforce: restricted
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: conformance-runner
|
||||
namespace: conformance
|
||||
labels:
|
||||
app.kubernetes.io/part-of: conformance
|
||||
annotations:
|
||||
conformance.home.k3s/audit: >-
|
||||
PRIVILEGED TEST IDENTITY. Scoped least-privilege for fault-injection only.
|
||||
Never reuse for workloads. Widen only alongside the test that needs it.
|
||||
---
|
||||
# ClusterRole — the union of what BEHAVIOR + FAULT tests need. Every rule is
|
||||
# commented with the test/requirement that justifies it. Start empty; uncomment /
|
||||
# add rules as the corresponding L2/L3 tests are delivered (do NOT pre-grant).
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: conformance-runner
|
||||
labels:
|
||||
app.kubernetes.io/part-of: conformance
|
||||
rules: []
|
||||
# ---- Hermetic fixtures (all L2/L3): create/delete throwaway test namespaces + fixtures
|
||||
# - apiGroups: [""]
|
||||
# resources: [namespaces]
|
||||
# verbs: [create, delete, get, list, watch]
|
||||
# - apiGroups: [""]
|
||||
# resources: [pods, services, configmaps, secrets, persistentvolumeclaims]
|
||||
# verbs: [create, delete, get, list, watch, patch]
|
||||
# - apiGroups: [""]
|
||||
# resources: [pods/exec]
|
||||
# verbs: [create] # probe exec for behavior assertions
|
||||
#
|
||||
# ---- R-NET-01/02 (default-deny, deny-undeclared): apply/patch/delete allow policies
|
||||
# - apiGroups: [cilium.io]
|
||||
# resources: [ciliumnetworkpolicies]
|
||||
# verbs: [create, delete, get, list, patch]
|
||||
#
|
||||
# ---- R-NET-06 (VPN kill-switch): force the shared Gluetun gateway down/up
|
||||
# - apiGroups: [apps]
|
||||
# resources: [deployments, deployments/scale]
|
||||
# resourceNames: [gluetun-gateway] # SCOPE to the gateway only
|
||||
# verbs: [get, patch, update]
|
||||
#
|
||||
# ---- R-STATE-02 (node loss): cordon/drain/uncordon a node (NOT in WS-TEST scope yet)
|
||||
# - apiGroups: [""]
|
||||
# resources: [nodes]
|
||||
# verbs: [get, list, patch] # cordon = patch spec.unschedulable
|
||||
# - apiGroups: [""]
|
||||
# resources: [pods/eviction]
|
||||
# verbs: [create] # drain
|
||||
#
|
||||
# ---- R-UPD-02 (bad-update auto-rollback): inject a bad image tag via automation
|
||||
# - apiGroups: [image.toolkit.fluxcd.io]
|
||||
# resources: [imagepolicies, imagerepositories]
|
||||
# verbs: [get, patch]
|
||||
#
|
||||
# ---- R-STATE-03 (DR restore): trigger a Velero restore
|
||||
# - apiGroups: [velero.io]
|
||||
# resources: [restores, backups]
|
||||
# verbs: [create, get, list, watch]
|
||||
#
|
||||
# ---- R-GITOPS-03 (drift): mutate a reconciled resource + observe Flux revert
|
||||
# - apiGroups: [kustomize.toolkit.fluxcd.io]
|
||||
# resources: [kustomizations]
|
||||
# verbs: [get, list, watch] # observe only; do NOT grant reconcile suspension
|
||||
#
|
||||
# ---- R-PLAT-03 (VM lifecycle): create/start/stop/destroy VMs
|
||||
# - apiGroups: [kubevirt.io]
|
||||
# resources: [virtualmachines, virtualmachineinstances]
|
||||
# verbs: [create, delete, get, list, patch]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRoleBinding
|
||||
metadata:
|
||||
name: conformance-runner
|
||||
labels:
|
||||
app.kubernetes.io/part-of: conformance
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: conformance-runner
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: conformance-runner
|
||||
namespace: conformance
|
||||
Reference in new issue
Block a user