diff --git a/.changes/unreleased/charts-redpanda-Fixed-20250908-172418.yaml b/.changes/unreleased/charts-redpanda-Fixed-20250908-172418.yaml new file mode 100644 index 000000000..5e9bd377c --- /dev/null +++ b/.changes/unreleased/charts-redpanda-Fixed-20250908-172418.yaml @@ -0,0 +1,4 @@ +project: charts/redpanda +kind: Fixed +body: '`statefulset.podTemplate.spec.volumes` can now be used to override chart generated volumes.' +time: 2025-09-08T17:24:18.8483-04:00 diff --git a/charts/redpanda/helpers.go b/charts/redpanda/helpers.go index b591aea33..8be2335fc 100644 --- a/charts/redpanda/helpers.go +++ b/charts/redpanda/helpers.go @@ -540,7 +540,9 @@ func mergeEnvVar(original corev1.EnvVar, overrides applycorev1.EnvVarApplyConfig } func mergeVolume(original corev1.Volume, override applycorev1.VolumeApplyConfiguration) corev1.Volume { - return helmette.MergeTo[corev1.Volume](override, original) + // Similar to the above, if a volume is being overridden, it's likely to + // change the VolumeSource. Don't merge, just accept the override. + return helmette.MergeTo[corev1.Volume](override) } func mergeVolumeMount(original corev1.VolumeMount, override applycorev1.VolumeMountApplyConfiguration) corev1.VolumeMount { diff --git a/charts/redpanda/helpers_test.go b/charts/redpanda/helpers_test.go index 82fbf13ed..0f1b10efe 100644 --- a/charts/redpanda/helpers_test.go +++ b/charts/redpanda/helpers_test.go @@ -247,6 +247,55 @@ func TestStrategicMergePatch(t *testing.T) { }, }, }, + { + Name: "volumes", + Override: redpanda.PodTemplate{ + Spec: &applycorev1.PodSpecApplyConfiguration{ + Volumes: []applycorev1.VolumeApplyConfiguration{ + { + Name: ptr.To("certs-volume-mount"), + VolumeSourceApplyConfiguration: applycorev1.VolumeSourceApplyConfiguration{ + Secret: nil, + EmptyDir: &applycorev1.EmptyDirVolumeSourceApplyConfiguration{}, + }, + }, + }, + }, + }, + Original: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Volumes: []corev1.Volume{ + { + Name: "certs-volume-mount", + VolumeSource: corev1.VolumeSource{ + Secret: &corev1.SecretVolumeSource{ + SecretName: "some-secret", + }, + }, + }, + }, + }, + }, + Expected: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{}, + Annotations: map[string]string{}, + }, + Spec: corev1.PodSpec{ + NodeSelector: map[string]string{}, + Tolerations: []corev1.Toleration{}, + ImagePullSecrets: []corev1.LocalObjectReference{}, + Volumes: []corev1.Volume{ + { + Name: "certs-volume-mount", + VolumeSource: corev1.VolumeSource{ + EmptyDir: &corev1.EmptyDirVolumeSource{}, + }, + }, + }, + }, + }, + }, } for _, tc := range cases { diff --git a/charts/redpanda/templates/_helpers.go.tpl b/charts/redpanda/templates/_helpers.go.tpl index f5f1d8ab0..0347b6931 100644 --- a/charts/redpanda/templates/_helpers.go.tpl +++ b/charts/redpanda/templates/_helpers.go.tpl @@ -568,7 +568,7 @@ {{- range $_ := (list 1) -}} {{- $_is_returning := false -}} {{- $_is_returning = true -}} -{{- (dict "r" (merge (dict) $override $original)) | toJson -}} +{{- (dict "r" (merge (dict) $override)) | toJson -}} {{- break -}} {{- end -}} {{- end -}} diff --git a/charts/redpanda/testdata/template-cases.txtar b/charts/redpanda/testdata/template-cases.txtar index 9015b97cf..11327b418 100644 --- a/charts/redpanda/testdata/template-cases.txtar +++ b/charts/redpanda/testdata/template-cases.txtar @@ -1163,3 +1163,59 @@ statefulset: enabled: false pvcUnbinder: enabled: false + +-- jit-certificates -- +# ASSERT-NO-ERROR +# ASSERT-FIELD-EQUALS ["apps/v1/StatefulSet", "default/redpanda", "{.spec.template.spec.volumes[?(@.name == \"redpanda-external-cert\")]}", {"name": "redpanda-external-cert", "emptyDir": {}}] +# This case demonstrates how to provide "Just In Time" certificates via an +# initContainer by using podTemplate to overwrite the auto generated volume. +tls: + certs: + external: + # Uncomment this block to disable the generation of cert-manager Certificates. + # secretRef: + # name: "set-to-disable-cert-manager" + + # Controls whether or not the chart expects a ca.crt key to + # exist in the volume we create with the below init + # container. If set to false, the trustStore feature can + # continue to be used as is with the strategy. + caEnabled: true + +statefulset: + podTemplate: + spec: + initContainers: + - name: cert-minter + image: debian:latest + command: + - bash + - -c + - 'cp -L -r /original/.' + # Provide the rest of your initContainer implementation here. + # This runs with the redpanda ServiceAccount. + volumeMounts: + # autoMountServiceAccountToken is set to false but we do mount it. To + # mount it to your init container, specify this volume: + - name: "kube-api-access" + readOnly: true + mountPath: "/var/run/secrets/kubernetes.io/serviceaccount" + # Mount the empty dir volume that will be used to pass certs through to redpanda. + - name: "redpanda-external-cert" + mountPath: "/certs" + # Unique to this example, we're just stealing the certs from the original. + - name: "3rd-party-certs" + mountPath: "/original" + + volumes: + # Here's where the "magic" is. We're going to use podTemplate + # to override the standard certificate mount that the chart + # generates with an emptyDir. The initContainer will then + # populate it with a tls.crt, tls.key, and (optionally) ca.crt + # which makes it look like a standard TLS Secret mount. + - name: "redpanda-external-cert" # "{{ nameOverride }}-{{ cert }}-cert" + emptyDir: {} + # Unique to this example, we're just stealing the certs from the original. + - name: "3rd-party-certs" + secret: + secretName: "redpanda-external-cert"