Files
register-referentie/infra/helm/big-reference/templates/_helpers.tpl
T
notandClaude Opus 5 56cba9c340 feat(k8s): terminate TLS in the cluster for a public domain (refs #177)
`public.domain` is the whole switch. Empty — the default, and what compose, CI
and a laptop cluster use — renders nothing new and leaves every manifest as it
was. Set it and templates/edge.yaml adds a Caddy deployment that gets its own
certificates from Let's Encrypt and proxies the five browser-facing hostnames to
the ClusterIP services, so a public deployment doesn't use their NodePorts at
all.

Caddy rather than an ingress controller because the four portals already run
caddy:2-alpine (ADR-0034, whose ceiling note called exactly this out): no new
dependency, no cert-manager, no CRDs, no Ingress objects for five hostnames that
never change. The Fedora host keeps only a layer-4 forward of 80/443, because
the public IP is there and nothing in the cluster can claim it.

KC_HOSTNAME and the portals' config.json now both come from `big.keycloakUrl`,
so the issuer a token carries and the authority the BFF discovers are one string
by construction (ADR-0010) rather than by two templates agreeing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-18 16:30:38 +02:00

157 lines
4.9 KiB
Smarty

{{/*
One pod spec for every workload, Deployment and Job alike. The chart is
values-driven on purpose: `.Values.workloads` is a near-literal transcription of
infra/docker-compose.yml, so the two stacks can be diffed by eye instead of by
archaeology. Adding a service is a values edit, not a template edit.
Called as: include "big.podspec" (dict "root" $ "name" $name "w" $w)
*/}}
{{- define "big.podspec" -}}
{{- $root := .root -}}
{{- $name := .name -}}
{{- $w := .w -}}
{{- with $root.Values.imagePullSecrets }}
imagePullSecrets:
{{- toYaml . | nindent 2 }}
{{- end }}
{{- with $w.waitFor }}
initContainers:
- name: wait-for-deps
image: {{ $root.Values.images.busybox }}
command:
- sh
- -c
- |
for t in {{ join " " . }}; do
echo "waiting for $t"
until nc -z "${t%:*}" "${t#*:}"; do sleep 2; done
done
{{- end }}
containers:
- name: {{ $name }}
image: {{ include "big.image" (dict "root" $root "name" $name "w" $w) }}
# Only this repo's images get the configured policy: their `dev` tag is mutable.
# Upstream tags are pinned, so IfNotPresent keeps them out of pod-template diffs —
# which matters because a changed template makes a Job unpatchable (immutable).
imagePullPolicy: {{ if $w.own }}{{ $root.Values.images.pullPolicy }}{{ else }}IfNotPresent{{ end }}
{{- if $w.command }}
{{- fail (printf "workload %s: use `args`, not `command` — compose's `command:` replaces CMD, but Kubernetes' `command:` replaces the image ENTRYPOINT (postgres would run as root, keycloak would exec `start-dev`)" $name) }}
{{- end }}
{{- with $w.args }}
args:
{{- toYaml . | nindent 6 }}
{{- end }}
{{- with $w.envFrom }}
envFrom:
{{- range . }}
- configMapRef:
# optional: an env group whose feature is disabled (e.g. otel) simply
# isn't rendered, and the pod must still start.
name: {{ printf "%s-env" . }}
optional: true
{{- end }}
{{- end }}
{{- with $w.env }}
env:
{{- include "big.env" (list $root .) | nindent 6 }}
{{- end }}
{{- with $w.ports }}
ports:
{{- range . }}
- name: {{ .name }}
containerPort: {{ .targetPort | default .port }}
{{- end }}
{{- end }}
{{- with $w.probe }}
readinessProbe:
{{- toYaml . | nindent 6 }}
{{- end }}
{{- with $w.resources }}
resources:
{{- toYaml . | nindent 6 }}
{{- end }}
{{- if or $w.files $w.data }}
volumeMounts:
{{- range $w.files }}
- name: {{ .configMap }}
mountPath: {{ .mountPath }}
{{- with .subPath }}
subPath: {{ . }}
{{- end }}
readOnly: true
{{- end }}
{{- with $w.data }}
- name: data
mountPath: {{ .mountPath }}
{{- end }}
{{- end }}
{{- if or $w.files $w.data }}
volumes:
{{- range $w.files }}
- name: {{ .configMap }}
configMap:
name: {{ .configMap }}
{{- with .defaultMode }}
defaultMode: {{ . }}
{{- end }}
{{- end }}
{{- with $w.data }}
- name: data
{{- if $root.Values.persistence.storageClass }}
persistentVolumeClaim:
claimName: {{ $name }}-data
{{- else }}
# No StorageClass configured: the databases are emptyDir, so the stack needs
# no CSI driver to come up. Data then lives as long as the pod does — see
# docs/runbooks/kubernetes-talos.md for switching on local-path.
emptyDir: {}
{{- end }}
{{- end }}
{{- end }}
{{- end -}}
{{/* Image ref: `own: true` workloads are built from this repo, everything else is upstream. */}}
{{- define "big.image" -}}
{{- $root := .root -}}
{{- $w := .w -}}
{{- if $w.own -}}
{{- $ref := printf "%s/%s:%s" $root.Values.images.repositoryPrefix .name $root.Values.images.tag -}}
{{- with $root.Values.images.registry }}{{ printf "%s/%s" . $ref }}{{ else }}{{ $ref }}{{ end }}
{{- else -}}
{{- $w.image -}}
{{- end -}}
{{- end -}}
{{/*
Env list from a map. Every value is run through `tpl`, so values.yaml can name
cluster-internal hosts ({{ .Release.Namespace }}) and the node address
({{ .Values.host }}) without the chart hard-coding either.
*/}}
{{- define "big.env" -}}
{{- $root := index . 0 -}}
{{- range $k, $v := index . 1 }}
- name: {{ $k }}
value: {{ tpl (toString $v) $root | quote }}
{{- end }}
{{- end -}}
{{/*
The origin a browser reaches Keycloak on, and so the issuer its tokens carry and
the authority the portals are configured with (ADR-0010). With a public edge that
is the `auth` hostname on `public.domain` which must stay in step with the `auth`
key in `public.routes`; without one it is the node address plus Keycloak's NodePort.
*/}}
{{- define "big.keycloakUrl" -}}
{{- if .Values.public.domain -}}
https://auth.{{ .Values.public.domain }}
{{- else -}}
http://{{ .Values.host }}:{{ index .Values.nodePorts "keycloak" }}
{{- end -}}
{{- end -}}
{{- define "big.labels" -}}
app.kubernetes.io/name: {{ .name }}
app.kubernetes.io/instance: {{ .root.Release.Name }}
app.kubernetes.io/managed-by: Helm
{{- end -}}