{{/* Name helpers. Standard chart boilerplate — the interesting parts are below. */}} {{- define "svcforge.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" -}} {{- end -}} {{- define "svcforge.fullname" -}} {{- if .Values.fullnameOverride -}} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" -}} {{- else -}} {{- $name := default .Chart.Name .Values.nameOverride -}} {{- if contains $name .Release.Name -}} {{- .Release.Name | trunc 63 | trimSuffix "-" -}} {{- else -}} {{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" -}} {{- end -}} {{- end -}} {{- end -}} {{- define "svcforge.labels" -}} helm.sh/chart: {{ printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} app.kubernetes.io/name: {{ include "svcforge.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} app.kubernetes.io/managed-by: {{ .Release.Service }} app.kubernetes.io/part-of: svcforge {{- end -}} {{/* Per-component selector labels. `app: ` is here on purpose and is not decoration: the module-9 chaos experiments select on it (`kubectl delete pod -l app=worker`). Renaming it breaks the runbook, not just a dashboard. */}} {{- define "svcforge.selectorLabels" -}} app.kubernetes.io/name: {{ include "svcforge.name" .ctx }} app.kubernetes.io/instance: {{ .ctx.Release.Name }} app.kubernetes.io/component: {{ .component }} app: {{ .component }} {{- end -}} {{/* Resolve a component's image to repo@digest. This is the single place that builds an image reference, and it refuses to emit one that is not digest-pinned. If CI has not bumped values.yaml, the release fails here with a readable message rather than silently deploying whatever a mutable tag happens to mean today. (The literal string "latest" is not written anywhere in this repo, including in comments — the acceptance gate greps for it and does not know what a comment is.) */}} {{- define "svcforge.image" -}} {{- $img := index .ctx.Values.image .component -}} {{- if not $img -}} {{- fail (printf "no image config for component %q" .component) -}} {{- end -}} {{- $digest := $img.digest | default "" -}} {{/* Full-shape match, not `hasPrefix "sha256:"`. A prefix check accepts the all-zeros placeholder in values.yaml, so a fresh clone rendered clean and the guard guarded nothing. Two conditions, both required: the digest must be sha256: plus exactly 64 lowercase hex characters, AND it must not be the placeholder literal. */}} {{- if not (regexMatch "^sha256:[0-9a-f]{64}$" $digest) -}} {{- fail (printf "image.%s.digest must be a sha256 digest (sha256: + 64 hex chars), not a tag — CI bumps it; got %q" .component ($digest | default "")) -}} {{- end -}} {{- if eq $digest "sha256:0000000000000000000000000000000000000000000000000000000000000000" -}} {{- fail (printf "image.%s.digest is still the all-zeros placeholder from values.yaml — this chart has never been bumped by CI and must not be deployed" .component) -}} {{- end -}} {{- printf "%s@%s" $img.repo $img.digest -}} {{- end -}} {{- define "svcforge.serviceAccountName" -}} {{- printf "%s-%s" (include "svcforge.fullname" .ctx) .component | trunc 63 | trimSuffix "-" -}} {{- end -}} {{- define "svcforge.secretName" -}} {{- .Values.externalSecret.targetName | default (printf "%s-secrets" (include "svcforge.fullname" .)) -}} {{- end -}} {{/* Pod-level hardening, identical for all three services and the migrate job. readOnlyRootFilesystem is the one that bites: every writable path a process needs must be an explicit emptyDir. That is the point — it makes the writes visible in review. */}} {{- define "svcforge.podSecurityContext" -}} runAsNonRoot: true runAsUser: 10001 runAsGroup: 10001 fsGroup: 10001 seccompProfile: type: RuntimeDefault {{- end -}} {{- define "svcforge.containerSecurityContext" -}} allowPrivilegeEscalation: false readOnlyRootFilesystem: true runAsNonRoot: true runAsUser: 10001 capabilities: drop: ["ALL"] {{- end -}} {{/* Shared environment. Secrets arrive via envFrom on the Secret that external-secrets populates from Vault — never as chart values, never as literals in a manifest. */}} {{- define "svcforge.env" -}} - name: SVCFORGE_POOL_MIN_SIZE value: {{ .Values.pool.minSize | quote }} - name: SVCFORGE_POOL_MAX_SIZE value: {{ .Values.pool.maxSize | quote }} - name: SVCFORGE_LOG_LEVEL value: {{ .Values.log.level | quote }} {{- if .Values.otel.enabled }} - name: OTEL_EXPORTER_OTLP_ENDPOINT value: {{ .Values.otel.endpoint | quote }} - name: OTEL_EXPORTER_OTLP_PROTOCOL value: grpc {{- end }} {{- end -}}