Validating Example YAML¶
make lint-examples validates every example YAML and every applyable
doc YAML block against the live CRD schemas via
kubectl apply --dry-run=server. Drift between examples and the
current *_types.go definitions surfaces as a CI failure.
What gets checked¶
| Source | Selection rule |
|---|---|
examples/**/*.yaml |
Every file (multi-document files are applied as one stream so forward references resolve) |
docs/**/*.md |
Every ```yaml fenced block that (a) contains apiVersion: + kind:, AND (b) sits inside a === "YAML" Material for MkDocs tab — i.e. is authored as applyable |
Blocks lacking apiVersion: (schema fragments, response payloads,
front-matter examples, partial snippets) are skipped automatically.
Cluster prerequisites¶
The lint needs a cluster with our CRDs installed:
hack/setup-kind.sh # one-time: create the local kind cluster
hack/install-crds.sh # apply config/crd/bases/ to the cluster
If no cluster is reachable, the lint skips with a warning and
exits 0, so make verify stays green on machines without a kind
cluster. Skip messages name the kubeconfig context that was probed, so
a wrong-context skip is diagnosable. CI (see
.github/workflows/lint-examples.yml) sets up Kind and enforces
LINT_EXAMPLES_REQUIRE_CLUSTER=1 so a missing cluster is a failure
there.
If the cluster's CRDs are older than config/crd/bases/, examples
using newer fields fail with unknown field errors. Rerun
hack/install-crds.sh (or make install) to refresh them.
Opt-out marker¶
Some YAML blocks intentionally show invalid or aspirational shapes (reference snippets with placeholders, third-party CRDs not installed in the lint cluster, documented anti-patterns). Mark these so the lint skips them:
| Source | Marker |
|---|---|
YAML file in examples/ |
# lint-examples:allow: <reason or issue link> on any line in the first 20 |
| Doc YAML block | <!-- lint-examples:allow: <reason or issue link> --> on a line within four lines above the fenced block |
The marker must state a reason or link a tracking issue. It is an exception for teaching snippets, and it is no blanket waiver. Every opt-out is an admission that the lint cannot enforce the contract at that location, and should be revisited when the underlying constraint changes.
Examples currently in use:
- Reference snippet with
...placeholders (docs/STYLE-GUIDE.md) - Third-party CRD not present in the lint cluster (
examples/newark-plant/19-historian-cnpg-cluster.yaml) - Stale schema with a tracked migration issue (
docs/control-modules.md,docs/recipes.md)
When schema validation passes but resource references don't¶
kubectl --dry-run=server validates the schema before checking
cross-resource references (namespace existence, RBAC, webhooks). A
"namespace not found" error from the server means the schema
validation already succeeded. The lint treats it as a pass, because
the goal here is CRD schema drift detection, with transitive resource
graph validation out of scope. The doc text is expected to direct the
reader to apply prerequisites in order.
Writing new examples¶
- Use pharma-realistic naming (
granulator-1,site-newark-plant,reactor-temp-high), per STYLE-GUIDE.md. - For multi-resource walkthroughs, keep each resource in its own
numbered file under
examples/<scenario>/so apply order is obvious. - Cross-check against
config/crd/bases/<group>_<plural>.yamlafter any kubebuilder marker change. The lint will catch drift on the next push, but it's cheaper to catch it locally. - New CRD types: add a minimal example under
examples/(per CLAUDE.md), and that example becomes the lint's reference shape.
Running locally¶
make lint-examples # full check (skips if no cluster)
LINT_EXAMPLES_VERBOSE=1 make lint-examples # show skipped blocks + reasons
LINT_EXAMPLES_ONLY_FILES=1 make lint-examples # skip the doc-block scan
LINT_EXAMPLES_ONLY_DOCS=1 make lint-examples # skip the examples/ scan
The script and Python helper live at scripts/lint-examples.sh and
hack/lint-examples.py.