From e0f92976b72053598b1856adf7db801862a97e72 Mon Sep 17 00:00:00 2001 From: Robin Date: Wed, 11 Mar 2026 14:09:21 +0100 Subject: [PATCH] refactor(app): use StatefulSet with volumeClaimTemplates for persistence MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace standalone PVC + Deployment approach with conditional resource: - persistence disabled → Deployment (stateless, any replica count) - persistence enabled → StatefulSet with volumeClaimTemplates (per-pod PVCs) Extract shared pod template into _helpers.tpl to avoid duplication. Delete pvc.yaml — StatefulSet manages PVC lifecycle automatically. Bump chart version to 1.2.0. --- app/Chart.yaml | 2 +- app/templates/_helpers.tpl | 124 +++++++++++++++++++++++++++++++ app/templates/deployment.yaml | 129 +-------------------------------- app/templates/pvc.yaml | 20 ----- app/templates/statefulset.yaml | 32 ++++++++ 5 files changed, 160 insertions(+), 147 deletions(-) delete mode 100644 app/templates/pvc.yaml create mode 100644 app/templates/statefulset.yaml diff --git a/app/Chart.yaml b/app/Chart.yaml index a509153..2c349d3 100644 --- a/app/Chart.yaml +++ b/app/Chart.yaml @@ -2,7 +2,7 @@ apiVersion: v2 name: app description: Generic Helm chart for deploying Docker containers type: application -version: 1.1.0 +version: 1.2.0 appVersion: 'latest' annotations: artifacthub.io/images: | diff --git a/app/templates/_helpers.tpl b/app/templates/_helpers.tpl index 6e13b3a..7017340 100644 --- a/app/templates/_helpers.tpl +++ b/app/templates/_helpers.tpl @@ -60,3 +60,127 @@ Create the name of the service account to use {{- default "default" .Values.serviceAccount.name }} {{- end }} {{- end }} + +{{/* +Pod template spec — shared between Deployment and StatefulSet. +Includes everything inside spec.template. +*/}} +{{- define "app.podTemplate" -}} +metadata: + {{- with .Values.podAnnotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} + labels: + {{- include "app.labels" . | nindent 4 }} + {{- with .Values.podLabels }} + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- with .Values.imagePullSecrets }} + imagePullSecrets: + {{- toYaml . | nindent 4 }} + {{- end }} + serviceAccountName: {{ include "app.serviceAccountName" . }} + {{- with .Values.podSecurityContext }} + securityContext: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.initContainers }} + initContainers: + {{- toYaml . | nindent 4 }} + {{- end }} + containers: + - name: {{ .Chart.Name }} + {{- with .Values.securityContext }} + securityContext: + {{- toYaml . | nindent 8 }} + {{- end }} + image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.image.pullPolicy }} + {{- with .Values.container.command }} + command: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.container.args }} + args: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.container.ports }} + ports: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- with .Values.container.env }} + env: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.healthChecks.liveness.enabled }} + livenessProbe: + {{- if eq .Values.healthChecks.liveness.type "httpGet" }} + httpGet: + {{- toYaml .Values.healthChecks.liveness.httpGet | nindent 10 }} + {{- else if eq .Values.healthChecks.liveness.type "tcpSocket" }} + tcpSocket: + {{- toYaml .Values.healthChecks.liveness.tcpSocket | nindent 10 }} + {{- else if eq .Values.healthChecks.liveness.type "exec" }} + exec: + {{- toYaml .Values.healthChecks.liveness.exec | nindent 10 }} + {{- end }} + initialDelaySeconds: {{ .Values.healthChecks.liveness.initialDelaySeconds }} + periodSeconds: {{ .Values.healthChecks.liveness.periodSeconds }} + timeoutSeconds: {{ .Values.healthChecks.liveness.timeoutSeconds }} + failureThreshold: {{ .Values.healthChecks.liveness.failureThreshold }} + {{- end }} + {{- if .Values.healthChecks.readiness.enabled }} + readinessProbe: + {{- if eq .Values.healthChecks.readiness.type "httpGet" }} + httpGet: + {{- toYaml .Values.healthChecks.readiness.httpGet | nindent 10 }} + {{- else if eq .Values.healthChecks.readiness.type "tcpSocket" }} + tcpSocket: + {{- toYaml .Values.healthChecks.readiness.tcpSocket | nindent 10 }} + {{- else if eq .Values.healthChecks.readiness.type "exec" }} + exec: + {{- toYaml .Values.healthChecks.readiness.exec | nindent 10 }} + {{- end }} + initialDelaySeconds: {{ .Values.healthChecks.readiness.initialDelaySeconds }} + periodSeconds: {{ .Values.healthChecks.readiness.periodSeconds }} + timeoutSeconds: {{ .Values.healthChecks.readiness.timeoutSeconds }} + failureThreshold: {{ .Values.healthChecks.readiness.failureThreshold }} + {{- end }} + {{- with .Values.resources }} + resources: + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if or .Values.volumeMounts (and .Values.persistence.enabled .Values.persistence.volumes) }} + volumeMounts: + {{- with .Values.volumeMounts }} + {{- toYaml . | nindent 8 }} + {{- end }} + {{- if .Values.persistence.enabled }} + {{- range .Values.persistence.volumes }} + - name: {{ .name }} + mountPath: {{ .mountPath }} + {{- end }} + {{- end }} + {{- end }} + {{- with .Values.sidecars }} + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.volumes }} + volumes: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.nodeSelector }} + nodeSelector: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.affinity }} + affinity: + {{- toYaml . | nindent 4 }} + {{- end }} + {{- with .Values.tolerations }} + tolerations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/app/templates/deployment.yaml b/app/templates/deployment.yaml index 1cd23fc..281a4a3 100644 --- a/app/templates/deployment.yaml +++ b/app/templates/deployment.yaml @@ -1,3 +1,4 @@ +{{- if not .Values.persistence.enabled }} apiVersion: apps/v1 kind: Deployment metadata: @@ -12,129 +13,5 @@ spec: matchLabels: {{- include "app.selectorLabels" . | nindent 6 }} template: - metadata: - {{- with .Values.podAnnotations }} - annotations: - {{- toYaml . | nindent 8 }} - {{- end }} - labels: - {{- include "app.labels" . | nindent 8 }} - {{- with .Values.podLabels }} - {{- toYaml . | nindent 8 }} - {{- end }} - spec: - {{- with .Values.imagePullSecrets }} - imagePullSecrets: - {{- toYaml . | nindent 8 }} - {{- end }} - serviceAccountName: {{ include "app.serviceAccountName" . }} - {{- with .Values.podSecurityContext }} - securityContext: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.initContainers }} - initContainers: - {{- toYaml . | nindent 8 }} - {{- end }} - containers: - - name: {{ .Chart.Name }} - {{- with .Values.securityContext }} - securityContext: - {{- toYaml . | nindent 12 }} - {{- end }} - image: "{{ .Values.image.repository }}:{{ .Values.image.tag | default .Chart.AppVersion }}" - imagePullPolicy: {{ .Values.image.pullPolicy }} - {{- with .Values.container.command }} - command: - {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.container.args }} - args: - {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.container.ports }} - ports: - {{- toYaml . | nindent 12 }} - {{- end }} - {{- with .Values.container.env }} - env: - {{- toYaml . | nindent 12 }} - {{- end }} - {{- if .Values.healthChecks.liveness.enabled }} - livenessProbe: - {{- if eq .Values.healthChecks.liveness.type "httpGet" }} - httpGet: - {{- toYaml .Values.healthChecks.liveness.httpGet | nindent 14 }} - {{- else if eq .Values.healthChecks.liveness.type "tcpSocket" }} - tcpSocket: - {{- toYaml .Values.healthChecks.liveness.tcpSocket | nindent 14 }} - {{- else if eq .Values.healthChecks.liveness.type "exec" }} - exec: - {{- toYaml .Values.healthChecks.liveness.exec | nindent 14 }} - {{- end }} - initialDelaySeconds: {{ .Values.healthChecks.liveness.initialDelaySeconds }} - periodSeconds: {{ .Values.healthChecks.liveness.periodSeconds }} - timeoutSeconds: {{ .Values.healthChecks.liveness.timeoutSeconds }} - failureThreshold: {{ .Values.healthChecks.liveness.failureThreshold }} - {{- end }} - {{- if .Values.healthChecks.readiness.enabled }} - readinessProbe: - {{- if eq .Values.healthChecks.readiness.type "httpGet" }} - httpGet: - {{- toYaml .Values.healthChecks.readiness.httpGet | nindent 14 }} - {{- else if eq .Values.healthChecks.readiness.type "tcpSocket" }} - tcpSocket: - {{- toYaml .Values.healthChecks.readiness.tcpSocket | nindent 14 }} - {{- else if eq .Values.healthChecks.readiness.type "exec" }} - exec: - {{- toYaml .Values.healthChecks.readiness.exec | nindent 14 }} - {{- end }} - initialDelaySeconds: {{ .Values.healthChecks.readiness.initialDelaySeconds }} - periodSeconds: {{ .Values.healthChecks.readiness.periodSeconds }} - timeoutSeconds: {{ .Values.healthChecks.readiness.timeoutSeconds }} - failureThreshold: {{ .Values.healthChecks.readiness.failureThreshold }} - {{- end }} - {{- with .Values.resources }} - resources: - {{- toYaml . | nindent 12 }} - {{- end }} - {{- if or .Values.volumeMounts (and .Values.persistence.enabled .Values.persistence.volumes) }} - volumeMounts: - {{- with .Values.volumeMounts }} - {{- toYaml . | nindent 12 }} - {{- end }} - {{- if .Values.persistence.enabled }} - {{- range .Values.persistence.volumes }} - - name: {{ .name }} - mountPath: {{ .mountPath }} - {{- end }} - {{- end }} - {{- end }} - {{- with .Values.sidecars }} - {{- toYaml . | nindent 8 }} - {{- end }} - {{- if or .Values.volumes (and .Values.persistence.enabled .Values.persistence.volumes) }} - volumes: - {{- with .Values.volumes }} - {{- toYaml . | nindent 8 }} - {{- end }} - {{- if .Values.persistence.enabled }} - {{- range .Values.persistence.volumes }} - - name: {{ .name }} - persistentVolumeClaim: - claimName: {{ include "app.fullname" $ }}-{{ .name }} - {{- end }} - {{- end }} - {{- end }} - {{- with .Values.nodeSelector }} - nodeSelector: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.affinity }} - affinity: - {{- toYaml . | nindent 8 }} - {{- end }} - {{- with .Values.tolerations }} - tolerations: - {{- toYaml . | nindent 8 }} - {{- end }} + {{- include "app.podTemplate" . | nindent 4 }} +{{- end }} diff --git a/app/templates/pvc.yaml b/app/templates/pvc.yaml deleted file mode 100644 index 2ab1cd6..0000000 --- a/app/templates/pvc.yaml +++ /dev/null @@ -1,20 +0,0 @@ -{{- if .Values.persistence.enabled }} -{{- range .Values.persistence.volumes }} ---- -apiVersion: v1 -kind: PersistentVolumeClaim -metadata: - name: {{ include "app.fullname" $ }}-{{ .name }} - labels: - {{- include "app.labels" $ | nindent 4 }} -spec: - accessModes: - - {{ .accessMode | default "ReadWriteOnce" }} - {{- if .storageClassName }} - storageClassName: {{ .storageClassName }} - {{- end }} - resources: - requests: - storage: {{ .size | default "1Gi" }} -{{- end }} -{{- end }} diff --git a/app/templates/statefulset.yaml b/app/templates/statefulset.yaml new file mode 100644 index 0000000..c30a6c1 --- /dev/null +++ b/app/templates/statefulset.yaml @@ -0,0 +1,32 @@ +{{- if .Values.persistence.enabled }} +apiVersion: apps/v1 +kind: StatefulSet +metadata: + name: {{ include "app.fullname" . }} + labels: + {{- include "app.labels" . | nindent 4 }} +spec: + serviceName: {{ include "app.fullname" . }} + {{- if not .Values.autoscaling.enabled }} + replicas: {{ .Values.replicaCount }} + {{- end }} + selector: + matchLabels: + {{- include "app.selectorLabels" . | nindent 6 }} + template: + {{- include "app.podTemplate" . | nindent 4 }} + volumeClaimTemplates: + {{- range .Values.persistence.volumes }} + - metadata: + name: {{ .name }} + spec: + accessModes: + - {{ .accessMode | default "ReadWriteOnce" }} + {{- if .storageClassName }} + storageClassName: {{ .storageClassName }} + {{- end }} + resources: + requests: + storage: {{ .size | default "1Gi" }} + {{- end }} +{{- end }}