# Course VIII - Helm: Go templates, values contexts, one chart for a fleet

**Series position:** third course of the delivery arc. VI taught how change travels,
VII how images are named; this course teaches how ONE definition serves MANY clusters.
**Safety gate:** public site - technologies only. Claims source from [[helm-charts]],
[[yaml-manifest-style]], [[kustomize-inflate-helm]] (the ArgoCD render caveat), the
one-value-many-enforcers post (live on the blog - cross-link it), Helm/Go public docs.

## Title (thesis)
**A chart is a function. The values file is the cluster speaking.**

## Hook - the uncomfortable default
The default way to run one app on five clusters is five copies of the YAML, and the
default result is five slightly different apps - each copy drifting on its own schedule,
each "quick fix" applied to some of them. The inversion: stop copying outputs and ship
the FUNCTION instead. A Helm chart is a template with holes; each cluster supplies one
small values file that fills them; the rendered manifests are build artefacts nobody
hand-edits, ever.

## Move 1 - It's Go under the braces
Helm templates are Go text/template: `{{ .Values.camera.address }}` is a pipeline
walking a values object, `_helpers.tpl` holds named template functions (labels, names)
every manifest calls, and `include`/`toYaml`/`default` compose them. The mental shift:
you are not writing YAML, you are writing a program whose OUTPUT is YAML - so render
locally (`helm template`), read the output, and lint what came out, not what went in.
Say it like you mean it: **review the render, not just the template.**
FIELD NOTE (amber): ArgoCD deploys charts by running `helm template` - a pure render.
Anything that needs a live cluster or an install lifecycle (`lookup`, hooks) is silently
dead there. A chart that works under `helm install` can behave differently under GitOps -
render the way your deployer renders.

## Move 2 - Contexts: the cluster's whole voice is one small file
The fleet pattern: the chart owns EVERYTHING structural - resources, probes, security
contexts, policy - and each cluster owns one values file (its context): names, addresses,
sizes, feature flags. The context is deliberately values-only; the moment a context
carries its own manifests, there are two owners for one object and they will disagree.
One value can feed many rendered artefacts - an address appearing in the app config, the
network attachment and two policies renders from ONE field, so the copies cannot diverge
(the blog post "Consistency by construction beats consistency by discipline" is this
move, lived).
Say it like you mean it: **the chart owns the shape; the context owns the numbers.**

## Move 3 - Make the template refuse
A template that renders whatever it is given just moves the failure downstream. The
grown-up chart carries a values.schema.json: required fields, types, enums - and a
cluster whose context omits a required value FAILS AT RENDER TIME with a message naming
the field, in the pipeline, before anything ships. Same discipline inside the template:
`required "camera.address is required" .Values.camera.address` beats a silent empty
string that becomes a policy pointed at nothing.
FIELD NOTE: the failure you want is the render that refuses - it costs a red pipeline.
The failure you get otherwise is enforcement aimed at an address nothing lives at,
months later, reporting healthy the whole time.

## Close
Fleet consistency is not a discipline problem, it is a construction problem. Ship one
function, let each cluster speak one small file of facts, and make the function refuse
to run on missing facts. Divergence stops being something you police and becomes
something the tooling cannot express.

## Legend terms (DOM layer)
chart / template / Go text-template / pipeline / _helpers.tpl / values / context /
values.schema.json / required / render (helm template) / one source many renders

## Scene concept (hero + slices, the v3 mechanism)
Exploded press-works, centre-out: an engraved PRESS PLATE (the chart - dark slab, its
face carrying an abstract lattice of rectangular voids, the "holes" clearly empty) ->
three small VALUES CRYSTALS (per-cluster contexts - compact faceted gems, cyan, one
subtly larger) feeding toward it from the left -> on the right, three thin RENDERED
SHEETS fanned apart, identical framework but each carrying a different accent pattern
(same shape, different numbers) -> below, a GATE WEDGE (the schema - amber-white edge,
Course III's gatekeeper silhouette re-used) with one dim REJECTED SHEET stopped at it.
7 parts, generous void, no connectors. Slice plan: collapse draws crystals + sheets into
the press (one machine at rest); the rejected sheet collapses BEHIND the gate, never
past it - the visual argument for Move 3.

## Page footer - pre-reads + further reading (site convention: every course carries this)
Pre-reads: Course II (desired state - a chart renders it); working with Kubernetes
objects (https://kubernetes.io/docs/concepts/overview/working-with-objects/).
Further reading: Helm docs (https://helm.sh/docs/) and the chart template guide
(https://helm.sh/docs/chart_template_guide/); Go text/template
(https://pkg.go.dev/text/template); Helm on OpenShift (via
https://docs.redhat.com/en/documentation/openshift_container_platform).
