Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
59 lines
2.4 KiB
Markdown
59 lines
2.4 KiB
Markdown
# ServiceDefinition L0 validation (R-GITOPS-02)
|
||
|
||
Validates the shape of each `catalog/` **ServiceDefinition** against the canonical
|
||
schema and invariants in the read-only spec `docs/13-service-catalog.md` (with
|
||
`auth` from `docs/15`, `egress` from `docs/03`). Runs in CI **without a cluster**.
|
||
|
||
## Why kyverno-json (not the resource CLI)
|
||
|
||
A ServiceDefinition is **arbitrary YAML** — its own `kind:` field means
|
||
`container | vm`, not a Kubernetes Kind — so it cannot be validated by
|
||
`kyverno apply`/`kyverno test` (which expect Kubernetes resources). Per
|
||
[ADR-0011] the Kyverno-ecosystem tool for arbitrary YAML/JSON is **kyverno-json**.
|
||
|
||
## Invariants enforced (`validate-servicedefinition.yaml`)
|
||
|
||
- `name` is a DNS-1123 label (→ `<name>.home.keithjackson.dev`).
|
||
- `kind` ∈ {`container`, `vm`}; container ⇒ `source.image`, vm ⇒ `source.vmTemplate`.
|
||
- `exposure` ∈ {`internal`, `public`}.
|
||
- `egress` ∈ {`internet`, `vpn`}.
|
||
- `auth.mode` ∈ {`oidc`, `forward-auth`, `native`, `none`}.
|
||
- **`public` exposure ⇒ `auth.mode` ≠ `none`** (no unauthenticated public UI, docs/15).
|
||
- each `dependencies[]` has `to`, `port` ∈ 1–65535, `protocol` ∈ {`tcp`, `udp`}.
|
||
- each `storage[].class` ∈ {`longhorn`, `bulk`}.
|
||
|
||
## Fixtures
|
||
|
||
`payloads/valid-*.yaml` must PASS; `payloads/invalid-*.yaml` must FAIL — one failure
|
||
reason each (bad egress, public+none auth, missing image, bad dependency).
|
||
|
||
## Run (CI entrypoint)
|
||
|
||
```bash
|
||
# Valid payloads: scan must exit 0
|
||
kyverno-json scan \
|
||
--policy validate-servicedefinition.yaml \
|
||
--payload payloads/valid-plex.yaml \
|
||
--payload payloads/valid-sonarr.yaml
|
||
|
||
# Invalid payloads: each scan must exit NON-zero (loop asserts failure)
|
||
for f in payloads/invalid-*.yaml; do
|
||
if kyverno-json scan --policy validate-servicedefinition.yaml --payload "$f"; then
|
||
echo "FAIL: expected $f to be rejected"; exit 1
|
||
fi
|
||
done
|
||
```
|
||
|
||
The `scripts/run-l0.sh` wrapper (see `../README.md`) runs both halves and is what CI
|
||
invokes.
|
||
|
||
## Caveat (escalated in the T-0.5 report)
|
||
|
||
kyverno-json is **pre-1.0**; its assertion syntax evolves. The **invariants above are
|
||
the normative content**. The CI agent must pin an exact kyverno-json version and
|
||
confirm the `check:` expressions parse under it; adjust syntax (never the invariants)
|
||
if the pinned release differs. The overlapping JSON Schema from WS-GITOPS (T-0.3) is
|
||
the complementary structural contract.
|
||
|
||
[ADR-0011]: ../../../../home-network-k3s/decisions/adr-0011-test-tooling-kyverno-chainsaw.md
|