From 215e33e8978cb0f80f70982db2578e963fa68270 Mon Sep 17 00:00:00 2001 From: David Sabatie Date: Thu, 25 Sep 2025 18:24:46 +0200 Subject: [PATCH 1/4] feat: add missing fields to values modify serviceAccount to be able to use externally created SA Signed-off-by: David Sabatie --- examples/app/templates/_helpers.tpl | 9 ++- examples/app/templates/batch-job.yaml | 6 ++ examples/app/templates/cron-job.yaml | 6 ++ examples/app/templates/daemonset.yaml | 9 ++- examples/app/templates/deployment.yaml | 4 + examples/app/templates/statefulset.yaml | 6 ++ examples/app/values.yaml | 17 +++++ examples/operator/templates/deployment.yaml | 13 ++-- .../templates/leader-election-rbac.yaml | 2 +- examples/operator/templates/manager-rbac.yaml | 2 +- examples/operator/templates/proxy-rbac.yaml | 2 +- .../operator/templates/serviceaccount.yaml | 6 +- examples/operator/values.yaml | 16 +++- pkg/helm/init.go | 9 ++- pkg/processor/daemonset/daemonset.go | 5 +- pkg/processor/deployment/deployment.go | 2 +- pkg/processor/job/cron.go | 9 ++- pkg/processor/job/job.go | 9 ++- pkg/processor/pod/pod.go | 75 +++++++++++++++---- pkg/processor/pod/pod_test.go | 47 ++++++++++-- pkg/processor/rbac/clusterrolebinding.go | 2 +- pkg/processor/rbac/rolebinding.go | 2 +- pkg/processor/rbac/serviceaccount.go | 28 ++++++- .../container_security_context.go | 16 ++-- .../container_security_context_test.go | 4 +- pkg/processor/statefulset/statefulset.go | 5 +- test_data/sample-app.yaml | 58 +++++++------- 27 files changed, 267 insertions(+), 102 deletions(-) diff --git a/examples/app/templates/_helpers.tpl b/examples/app/templates/_helpers.tpl index 6e13b3a0..4aabe0d7 100644 --- a/examples/app/templates/_helpers.tpl +++ b/examples/app/templates/_helpers.tpl @@ -54,9 +54,12 @@ app.kubernetes.io/instance: {{ .Release.Name }} Create the name of the service account to use */}} {{- define "app.serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include "app.fullname" .) .Values.serviceAccount.name }} +{{- $default := (include "app.fullname" .) }} +{{- with .Values.serviceAccount }} +{{- if .create }} +{{- default $default .name }} {{- else }} -{{- default "default" .Values.serviceAccount.name }} +{{- default "default" .name }} +{{- end }} {{- end }} {{- end }} diff --git a/examples/app/templates/batch-job.yaml b/examples/app/templates/batch-job.yaml index c5aeb49c..40dfa099 100644 --- a/examples/app/templates/batch-job.yaml +++ b/examples/app/templates/batch-job.yaml @@ -21,4 +21,10 @@ spec: | default .Chart.AppVersion }} name: pi resources: {} + nodeSelector: {{- toYaml .Values.batchJob.nodeSelector | nindent 8 }} restartPolicy: Never + serviceAccountName: {{ default "default" (include "app.serviceAccountName" + .) }} + tolerations: {{- toYaml .Values.batchJob.tolerations | nindent 8 }} + topologySpreadConstraints: {{- toYaml .Values.batchJob.topologySpreadConstraints + | nindent 8 }} diff --git a/examples/app/templates/cron-job.yaml b/examples/app/templates/cron-job.yaml index bd0f073a..74957427 100644 --- a/examples/app/templates/cron-job.yaml +++ b/examples/app/templates/cron-job.yaml @@ -22,5 +22,11 @@ spec: imagePullPolicy: {{ .Values.cronJob.hello.imagePullPolicy }} name: hello resources: {} + nodeSelector: {{- toYaml .Values.cronJob.nodeSelector | nindent 12 }} restartPolicy: OnFailure + serviceAccountName: {{ default "default" (include "app.serviceAccountName" + .) }} + tolerations: {{- toYaml .Values.cronJob.tolerations | nindent 12 }} + topologySpreadConstraints: {{- toYaml .Values.cronJob.topologySpreadConstraints + | nindent 12 }} schedule: {{ .Values.cronJob.schedule | quote }} diff --git a/examples/app/templates/daemonset.yaml b/examples/app/templates/daemonset.yaml index e26e64b8..1b5533cc 100644 --- a/examples/app/templates/daemonset.yaml +++ b/examples/app/templates/daemonset.yaml @@ -32,11 +32,12 @@ spec: - mountPath: /var/lib/docker/containers name: varlibdockercontainers readOnly: true + nodeSelector: {{- toYaml .Values.fluentdElasticsearch.nodeSelector | nindent 8 }} + serviceAccountName: {{ default "default" (include "app.serviceAccountName" .) }} terminationGracePeriodSeconds: 30 - tolerations: - - effect: NoSchedule - key: node-role.kubernetes.io/master - operator: Exists + tolerations: {{- toYaml .Values.fluentdElasticsearch.tolerations | nindent 8 }} + topologySpreadConstraints: {{- toYaml .Values.fluentdElasticsearch.topologySpreadConstraints + | nindent 8 }} volumes: - hostPath: path: /var/log diff --git a/examples/app/templates/deployment.yaml b/examples/app/templates/deployment.yaml index 4d691eb3..57aa8369 100644 --- a/examples/app/templates/deployment.yaml +++ b/examples/app/templates/deployment.yaml @@ -96,7 +96,11 @@ spec: resources: {} nodeSelector: {{- toYaml .Values.myapp.nodeSelector | nindent 8 }} securityContext: {{- toYaml .Values.myapp.podSecurityContext | nindent 8 }} + serviceAccountName: {{ default "default" (include "app.serviceAccountName" .) }} terminationGracePeriodSeconds: 10 + tolerations: {{- toYaml .Values.myapp.tolerations | nindent 8 }} + topologySpreadConstraints: {{- toYaml .Values.myapp.topologySpreadConstraints | nindent + 8 }} volumes: - configMap: name: {{ include "app.fullname" . }}-my-config diff --git a/examples/app/templates/statefulset.yaml b/examples/app/templates/statefulset.yaml index 418c0d28..a9f43fb6 100644 --- a/examples/app/templates/statefulset.yaml +++ b/examples/app/templates/statefulset.yaml @@ -29,6 +29,12 @@ spec: volumeMounts: - mountPath: /usr/share/nginx/html name: www + nodeSelector: {{- toYaml .Values.web.nodeSelector | nindent 8 }} + serviceAccountName: {{ default "default" (include "app.serviceAccountName" .) + }} + tolerations: {{- toYaml .Values.web.tolerations | nindent 8 }} + topologySpreadConstraints: {{- toYaml .Values.web.topologySpreadConstraints | + nindent 8 }} updateStrategy: {} volumeClaimTemplates: - metadata: diff --git a/examples/app/values.yaml b/examples/app/values.yaml index 2d23d620..0023b067 100644 --- a/examples/app/values.yaml +++ b/examples/app/values.yaml @@ -1,16 +1,22 @@ batchJob: backoffLimit: 4 + nodeSelector: {} pi: image: repository: perl tag: 5.34.0 + tolerations: [] + topologySpreadConstraints: [] cronJob: hello: image: repository: busybox tag: "1.28" imagePullPolicy: IfNotPresent + nodeSelector: {} schedule: '* * * * *' + tolerations: [] + topologySpreadConstraints: [] fluentdElasticsearch: fluentdElasticsearch: image: @@ -22,6 +28,12 @@ fluentdElasticsearch: requests: cpu: 100m memory: 200Mi + nodeSelector: {} + tolerations: + - effect: NoSchedule + key: node-role.kubernetes.io/master + operator: Exists + topologySpreadConstraints: [] kubernetesClusterDomain: cluster.local myConfig: dummyconfigmapkey: dummyconfigmapvalue @@ -89,6 +101,8 @@ myapp: tag: v0.8.0 replicas: 3 revisionHistoryLimit: 5 + tolerations: [] + topologySpreadConstraints: [] myappLbService: loadBalancerSourceRanges: - 10.0.0.0/8 @@ -121,7 +135,10 @@ web: image: repository: registry.k8s.io/nginx-slim tag: "0.8" + nodeSelector: {} replicas: 2 + tolerations: [] + topologySpreadConstraints: [] volumeClaims: www: requests: diff --git a/examples/operator/templates/deployment.yaml b/examples/operator/templates/deployment.yaml index 67400bb0..3af0fe9c 100644 --- a/examples/operator/templates/deployment.yaml +++ b/examples/operator/templates/deployment.yaml @@ -96,15 +96,12 @@ spec: nodeSelector: {{- toYaml .Values.controllerManager.nodeSelector | nindent 8 }} securityContext: {{- toYaml .Values.controllerManager.podSecurityContext | nindent 8 }} - serviceAccountName: {{ include "operator.fullname" . }}-controller-manager + serviceAccountName: {{ default "default" (include "operator.serviceAccountName" .) + }} terminationGracePeriodSeconds: 10 - topologySpreadConstraints: - - matchLabelKeys: - - app - - pod-template-hash - maxSkew: 1 - topologyKey: kubernetes.io/hostname - whenUnsatisfiable: DoNotSchedule + tolerations: {{- toYaml .Values.controllerManager.tolerations | nindent 8 }} + topologySpreadConstraints: {{- toYaml .Values.controllerManager.topologySpreadConstraints + | nindent 8 }} volumes: - configMap: name: {{ include "operator.fullname" . }}-manager-config diff --git a/examples/operator/templates/leader-election-rbac.yaml b/examples/operator/templates/leader-election-rbac.yaml index 2f1ed8f9..c163656f 100644 --- a/examples/operator/templates/leader-election-rbac.yaml +++ b/examples/operator/templates/leader-election-rbac.yaml @@ -49,5 +49,5 @@ roleRef: name: '{{ include "operator.fullname" . }}-leader-election-role' subjects: - kind: ServiceAccount - name: '{{ include "operator.fullname" . }}-controller-manager' + name: '{{ include "operator.serviceAccountName" . }}' namespace: '{{ .Release.Namespace }}' diff --git a/examples/operator/templates/manager-rbac.yaml b/examples/operator/templates/manager-rbac.yaml index 8a59b24d..10aee772 100644 --- a/examples/operator/templates/manager-rbac.yaml +++ b/examples/operator/templates/manager-rbac.yaml @@ -98,5 +98,5 @@ roleRef: name: '{{ include "operator.fullname" . }}-manager-role' subjects: - kind: ServiceAccount - name: '{{ include "operator.fullname" . }}-controller-manager' + name: '{{ include "operator.serviceAccountName" . }}' namespace: '{{ .Release.Namespace }}' diff --git a/examples/operator/templates/proxy-rbac.yaml b/examples/operator/templates/proxy-rbac.yaml index 6bc9aa81..d9b76f0f 100644 --- a/examples/operator/templates/proxy-rbac.yaml +++ b/examples/operator/templates/proxy-rbac.yaml @@ -30,5 +30,5 @@ roleRef: name: '{{ include "operator.fullname" . }}-proxy-role' subjects: - kind: ServiceAccount - name: '{{ include "operator.fullname" . }}-controller-manager' + name: '{{ include "operator.serviceAccountName" . }}' namespace: '{{ .Release.Namespace }}' diff --git a/examples/operator/templates/serviceaccount.yaml b/examples/operator/templates/serviceaccount.yaml index c06f9ead..a8fdf5bd 100644 --- a/examples/operator/templates/serviceaccount.yaml +++ b/examples/operator/templates/serviceaccount.yaml @@ -1,8 +1,10 @@ +{{ if .Values.serviceAccount.create }} apiVersion: v1 kind: ServiceAccount metadata: - name: {{ include "operator.fullname" . }}-controller-manager + name: {{ include "operator.serviceAccountName" . }} labels: {{- include "operator.labels" . | nindent 4 }} annotations: - {{- toYaml .Values.controllerManager.serviceAccount.annotations | nindent 4 }} + {{- toYaml .Values.serviceAccount.annotations | nindent 4 }} +{{- end }} diff --git a/examples/operator/values.yaml b/examples/operator/values.yaml index a0c2c881..05ccd039 100644 --- a/examples/operator/values.yaml +++ b/examples/operator/values.yaml @@ -46,14 +46,19 @@ controllerManager: podSecurityContext: runAsNonRoot: true replicas: 1 - serviceAccount: - annotations: - k8s.acme.org/some-meta-data: ACME Inc. strategy: rollingUpdate: maxSurge: 25% maxUnavailable: 25% type: RollingUpdate + tolerations: [] + topologySpreadConstraints: + - matchLabelKeys: + - app + - pod-template-hash + maxSkew: 1 + topologyKey: kubernetes.io/hostname + whenUnsatisfiable: DoNotSchedule kubernetesClusterDomain: cluster.local managerConfig: controllerManagerConfigYaml: |- @@ -89,6 +94,11 @@ secretRegistryCredentials: secretVars: var1: "" var2: "" +serviceAccount: + annotations: + k8s.acme.org/some-meta-data: ACME Inc. + create: true + name: "" webhookService: ports: - port: 443 diff --git a/pkg/helm/init.go b/pkg/helm/init.go index f7e5b439..2d2a2618 100644 --- a/pkg/helm/init.go +++ b/pkg/helm/init.go @@ -91,10 +91,13 @@ app.kubernetes.io/instance: {{ .Release.Name }} Create the name of the service account to use */}} {{- define ".serviceAccountName" -}} -{{- if .Values.serviceAccount.create }} -{{- default (include ".fullname" .) .Values.serviceAccount.name }} +{{- $default := (include ".fullname" .) }} +{{- with .Values.serviceAccount }} +{{- if .create }} +{{- default $default .name }} {{- else }} -{{- default "default" .Values.serviceAccount.name }} +{{- default "default" .name }} +{{- end }} {{- end }} {{- end }} ` diff --git a/pkg/processor/daemonset/daemonset.go b/pkg/processor/daemonset/daemonset.go index 851abe36..7caac534 100644 --- a/pkg/processor/daemonset/daemonset.go +++ b/pkg/processor/daemonset/daemonset.go @@ -2,11 +2,12 @@ package daemonset import ( "fmt" - "github.com/arttor/helmify/pkg/processor/pod" "io" "strings" "text/template" + "github.com/arttor/helmify/pkg/processor/pod" + "github.com/arttor/helmify/pkg/helmify" "github.com/arttor/helmify/pkg/processor" yamlformat "github.com/arttor/helmify/pkg/yaml" @@ -98,7 +99,7 @@ func (d daemonset) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstru } nameCamel := strcase.ToLowerCamel(name) - specMap, podValues, err := pod.ProcessSpec(nameCamel, appMeta, dae.Spec.Template.Spec) + specMap, podValues, err := pod.ProcessSpec(nameCamel, appMeta, dae.Spec.Template.Spec, 0) if err != nil { return true, nil, err } diff --git a/pkg/processor/deployment/deployment.go b/pkg/processor/deployment/deployment.go index b41cc235..ad7c9234 100644 --- a/pkg/processor/deployment/deployment.go +++ b/pkg/processor/deployment/deployment.go @@ -125,7 +125,7 @@ func (d deployment) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstr } nameCamel := strcase.ToLowerCamel(name) - specMap, podValues, err := pod.ProcessSpec(nameCamel, appMeta, depl.Spec.Template.Spec) + specMap, podValues, err := pod.ProcessSpec(nameCamel, appMeta, depl.Spec.Template.Spec, 0) if err != nil { return true, nil, err } diff --git a/pkg/processor/job/cron.go b/pkg/processor/job/cron.go index 1baf186e..44063eab 100644 --- a/pkg/processor/job/cron.go +++ b/pkg/processor/job/cron.go @@ -2,18 +2,19 @@ package job import ( "fmt" + "io" + "strings" + "text/template" + "github.com/arttor/helmify/pkg/helmify" "github.com/arttor/helmify/pkg/processor" "github.com/arttor/helmify/pkg/processor/pod" yamlformat "github.com/arttor/helmify/pkg/yaml" "github.com/iancoleman/strcase" - "io" batchv1 "k8s.io/api/batch/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "strings" - "text/template" ) var cronTempl, _ = template.New("cron").Parse( @@ -105,7 +106,7 @@ func (p cron) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructure } // process job pod template: - podSpecMap, podValues, err := pod.ProcessSpec(nameCamelCase, appMeta, jobObj.Spec.JobTemplate.Spec.Template.Spec) + podSpecMap, podValues, err := pod.ProcessSpec(nameCamelCase, appMeta, jobObj.Spec.JobTemplate.Spec.Template.Spec, 4) if err != nil { return true, nil, err } diff --git a/pkg/processor/job/job.go b/pkg/processor/job/job.go index 5f1ec53e..f879a737 100644 --- a/pkg/processor/job/job.go +++ b/pkg/processor/job/job.go @@ -2,18 +2,19 @@ package job import ( "fmt" + "io" + "strings" + "text/template" + "github.com/arttor/helmify/pkg/helmify" "github.com/arttor/helmify/pkg/processor" "github.com/arttor/helmify/pkg/processor/pod" yamlformat "github.com/arttor/helmify/pkg/yaml" "github.com/iancoleman/strcase" - "io" batchv1 "k8s.io/api/batch/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" - "strings" - "text/template" ) var jobTempl, _ = template.New("job").Parse( @@ -104,7 +105,7 @@ func (p job) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructured } } // process job pod template: - podSpecMap, podValues, err := pod.ProcessSpec(nameCamelCase, appMeta, jobObj.Spec.Template.Spec) + podSpecMap, podValues, err := pod.ProcessSpec(nameCamelCase, appMeta, jobObj.Spec.Template.Spec, 0) if err != nil { return true, nil, err } diff --git a/pkg/processor/pod/pod.go b/pkg/processor/pod/pod.go index 88adb825..b9aabaa4 100644 --- a/pkg/processor/pod/pod.go +++ b/pkg/processor/pod/pod.go @@ -1,6 +1,7 @@ package pod import ( + "encoding/json" "fmt" "strings" @@ -15,8 +16,11 @@ import ( const imagePullPolicyTemplate = "{{ .Values.%[1]s.%[2]s.imagePullPolicy }}" const envValue = "{{ quote .Values.%[1]s.%[2]s.%[3]s.%[4]s }}" +const baseIndent = 8 + +func ProcessSpec(objName string, appMeta helmify.AppMetadata, spec corev1.PodSpec, addIndent int) (map[string]interface{}, helmify.Values, error) { + nindent := baseIndent + addIndent -func ProcessSpec(objName string, appMeta helmify.AppMetadata, spec corev1.PodSpec) (map[string]interface{}, helmify.Values, error) { values, err := processPodSpec(objName, appMeta, &spec) if err != nil { return nil, nil, err @@ -39,12 +43,12 @@ func ProcessSpec(objName string, appMeta helmify.AppMetadata, spec corev1.PodSpe return nil, nil, fmt.Errorf("%w: unable to convert podSpec to map", err) } - specMap, values, err = processNestedContainers(specMap, objName, values, "containers") + specMap, values, err = processNestedContainers(specMap, objName, values, "containers", nindent) if err != nil { return nil, nil, err } - specMap, values, err = processNestedContainers(specMap, objName, values, "initContainers") + specMap, values, err = processNestedContainers(specMap, objName, values, "initContainers", nindent) if err != nil { return nil, nil, err } @@ -56,7 +60,7 @@ func ProcessSpec(objName string, appMeta helmify.AppMetadata, spec corev1.PodSpe } } - err = securityContext.ProcessContainerSecurityContext(objName, specMap, &values) + err = securityContext.ProcessContainerSecurityContext(objName, specMap, &values, nindent) if err != nil { return nil, nil, err } @@ -66,7 +70,7 @@ func ProcessSpec(objName string, appMeta helmify.AppMetadata, spec corev1.PodSpe return nil, nil, err } if len(securityContextMap) > 0 { - err = unstructured.SetNestedField(specMap, fmt.Sprintf(`{{- toYaml .Values.%[1]s.podSecurityContext | nindent 8 }}`, objName), "securityContext") + err = unstructured.SetNestedField(specMap, fmt.Sprintf(`{{- toYaml .Values.%[1]s.podSecurityContext | nindent %d }}`, objName, nindent), "securityContext") if err != nil { return nil, nil, err } @@ -79,12 +83,57 @@ func ProcessSpec(objName string, appMeta helmify.AppMetadata, spec corev1.PodSpe } // process nodeSelector if presented: + err = unstructured.SetNestedField(specMap, fmt.Sprintf(`{{- toYaml .Values.%s.nodeSelector | nindent %d }}`, objName, nindent), "nodeSelector") + if err != nil { + return nil, nil, err + } if spec.NodeSelector != nil { - err = unstructured.SetNestedField(specMap, fmt.Sprintf(`{{- toYaml .Values.%s.nodeSelector | nindent 8 }}`, objName), "nodeSelector") + err = unstructured.SetNestedStringMap(values, spec.NodeSelector, objName, "nodeSelector") if err != nil { return nil, nil, err } - err = unstructured.SetNestedStringMap(values, spec.NodeSelector, objName, "nodeSelector") + } else { + err = unstructured.SetNestedField(values, map[string]interface{}{}, objName, "nodeSelector") + if err != nil { + return nil, nil, err + } + } + + // process tolerations if presented: + err = unstructured.SetNestedField(specMap, fmt.Sprintf(`{{- toYaml .Values.%s.tolerations | nindent %d }}`, objName, nindent), "tolerations") + if err != nil { + return nil, nil, err + } + if spec.Tolerations != nil { + tolerations := make([]any, len(spec.Tolerations)) + inrec, _ := json.Marshal(spec.Tolerations) + json.Unmarshal(inrec, &tolerations) + err = unstructured.SetNestedSlice(values, tolerations, objName, "tolerations") + if err != nil { + return nil, nil, err + } + } else { + err = unstructured.SetNestedSlice(values, []any{}, objName, "tolerations") + if err != nil { + return nil, nil, err + } + } + + // process topologySpreadConstraints if presented: + err = unstructured.SetNestedField(specMap, fmt.Sprintf(`{{- toYaml .Values.%s.topologySpreadConstraints | nindent %d }}`, objName, nindent), "topologySpreadConstraints") + if err != nil { + return nil, nil, err + } + if spec.TopologySpreadConstraints != nil { + topologySpreadConstraints := make([]any, len(spec.TopologySpreadConstraints)) + inrec, _ := json.Marshal(spec.TopologySpreadConstraints) + json.Unmarshal(inrec, &topologySpreadConstraints) + err = unstructured.SetNestedSlice(values, topologySpreadConstraints, objName, "topologySpreadConstraints") + if err != nil { + return nil, nil, err + } + } else { + err = unstructured.SetNestedSlice(values, []any{}, objName, "topologySpreadConstraints") if err != nil { return nil, nil, err } @@ -93,14 +142,14 @@ func ProcessSpec(objName string, appMeta helmify.AppMetadata, spec corev1.PodSpe return specMap, values, nil } -func processNestedContainers(specMap map[string]interface{}, objName string, values map[string]interface{}, containerKey string) (map[string]interface{}, map[string]interface{}, error) { +func processNestedContainers(specMap map[string]interface{}, objName string, values map[string]interface{}, containerKey string, nindent int) (map[string]interface{}, map[string]interface{}, error) { containers, _, err := unstructured.NestedSlice(specMap, containerKey) if err != nil { return nil, nil, err } if len(containers) > 0 { - containers, values, err = processContainers(objName, values, containerKey, containers) + containers, values, err = processContainers(objName, values, containerKey, containers, nindent) if err != nil { return nil, nil, err } @@ -114,7 +163,7 @@ func processNestedContainers(specMap map[string]interface{}, objName string, val return specMap, values, nil } -func processContainers(objName string, values helmify.Values, containerType string, containers []interface{}) ([]interface{}, helmify.Values, error) { +func processContainers(objName string, values helmify.Values, containerType string, containers []interface{}, nindent int) ([]interface{}, helmify.Values, error) { for i := range containers { containerName := strcase.ToLowerCamel((containers[i].(map[string]interface{})["name"]).(string)) res, exists, err := unstructured.NestedMap(values, objName, containerName, "resources") @@ -122,7 +171,7 @@ func processContainers(objName string, values helmify.Values, containerType stri return nil, nil, err } if exists && len(res) > 0 { - err = unstructured.SetNestedField(containers[i].(map[string]interface{}), fmt.Sprintf(`{{- toYaml .Values.%s.%s.resources | nindent 10 }}`, objName, containerName), "resources") + err = unstructured.SetNestedField(containers[i].(map[string]interface{}), fmt.Sprintf(`{{- toYaml .Values.%s.%s.resources | nindent %d }}`, objName, containerName, nindent+2), "resources") if err != nil { return nil, nil, err } @@ -133,7 +182,7 @@ func processContainers(objName string, values helmify.Values, containerType stri return nil, nil, err } if exists && len(args) > 0 { - err = unstructured.SetNestedField(containers[i].(map[string]interface{}), fmt.Sprintf(`{{- toYaml .Values.%[1]s.%[2]s.args | nindent 8 }}`, objName, containerName), "args") + err = unstructured.SetNestedField(containers[i].(map[string]interface{}), fmt.Sprintf(`{{- toYaml .Values.%[1]s.%[2]s.args | nindent %d }}`, objName, containerName, nindent), "args") if err != nil { return nil, nil, err } @@ -173,7 +222,7 @@ func processPodSpec(name string, appMeta helmify.AppMetadata, pod *corev1.PodSpe v.Secret.SecretName = appMeta.TemplatedName(v.Secret.SecretName) } } - pod.ServiceAccountName = appMeta.TemplatedName(pod.ServiceAccountName) + pod.ServiceAccountName = fmt.Sprintf("{{ default \"default\" (include \"%s.serviceAccountName\" .) }}", appMeta.ChartName()) for i, s := range pod.ImagePullSecrets { pod.ImagePullSecrets[i].Name = appMeta.TemplatedName(s.Name) diff --git a/pkg/processor/pod/pod_test.go b/pkg/processor/pod/pod_test.go index 5a2fd721..81d20d96 100644 --- a/pkg/processor/pod/pod_test.go +++ b/pkg/processor/pod/pod_test.go @@ -145,7 +145,7 @@ func Test_pod_Process(t *testing.T) { var deploy appsv1.Deployment obj := internal.GenerateObj(strDeployment) err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &deploy) - specMap, tmpl, err := ProcessSpec("nginx", &metadata.Service{}, deploy.Spec.Template.Spec) + specMap, tmpl, err := ProcessSpec("nginx", &metadata.Service{}, deploy.Spec.Template.Spec, 0) assert.NoError(t, err) assert.Equal(t, map[string]interface{}{ @@ -167,6 +167,10 @@ func Test_pod_Process(t *testing.T) { "resources": map[string]interface{}{}, }, }, + "tolerations": "{{- toYaml .Values.nginx.tolerations | nindent 8 }}", + "topologySpreadConstraints": "{{- toYaml .Values.nginx.topologySpreadConstraints | nindent 8 }}", + "nodeSelector": "{{- toYaml .Values.nginx.nodeSelector | nindent 8 }}", + "serviceAccountName": "{{ default \"default\" (include \".serviceAccountName\" .) }}", }, specMap) assert.Equal(t, helmify.Values{ @@ -181,6 +185,9 @@ func Test_pod_Process(t *testing.T) { "--arg", }, }, + "nodeSelector": map[string]interface{}{}, + "tolerations": []interface{}{}, + "topologySpreadConstraints": []interface{}{}, }, }, tmpl) }) @@ -189,7 +196,7 @@ func Test_pod_Process(t *testing.T) { var deploy appsv1.Deployment obj := internal.GenerateObj(strDeploymentWithNoArgs) err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &deploy) - specMap, tmpl, err := ProcessSpec("nginx", &metadata.Service{}, deploy.Spec.Template.Spec) + specMap, tmpl, err := ProcessSpec("nginx", &metadata.Service{}, deploy.Spec.Template.Spec, 0) assert.NoError(t, err) assert.Equal(t, map[string]interface{}{ @@ -210,6 +217,10 @@ func Test_pod_Process(t *testing.T) { "resources": map[string]interface{}{}, }, }, + "nodeSelector": "{{- toYaml .Values.nginx.nodeSelector | nindent 8 }}", + "serviceAccountName": "{{ default \"default\" (include \".serviceAccountName\" .) }}", + "tolerations": "{{- toYaml .Values.nginx.tolerations | nindent 8 }}", + "topologySpreadConstraints": "{{- toYaml .Values.nginx.topologySpreadConstraints | nindent 8 }}", }, specMap) assert.Equal(t, helmify.Values{ @@ -220,6 +231,9 @@ func Test_pod_Process(t *testing.T) { "tag": "1.14.2", }, }, + "nodeSelector": map[string]interface{}{}, + "tolerations": []interface{}{}, + "topologySpreadConstraints": []interface{}{}, }, }, tmpl) }) @@ -228,7 +242,7 @@ func Test_pod_Process(t *testing.T) { var deploy appsv1.Deployment obj := internal.GenerateObj(strDeploymentWithTagAndDigest) err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &deploy) - specMap, tmpl, err := ProcessSpec("nginx", &metadata.Service{}, deploy.Spec.Template.Spec) + specMap, tmpl, err := ProcessSpec("nginx", &metadata.Service{}, deploy.Spec.Template.Spec, 0) assert.NoError(t, err) assert.Equal(t, map[string]interface{}{ @@ -249,6 +263,10 @@ func Test_pod_Process(t *testing.T) { "resources": map[string]interface{}{}, }, }, + "nodeSelector": "{{- toYaml .Values.nginx.nodeSelector | nindent 8 }}", + "serviceAccountName": "{{ default \"default\" (include \".serviceAccountName\" .) }}", + "tolerations": "{{- toYaml .Values.nginx.tolerations | nindent 8 }}", + "topologySpreadConstraints": "{{- toYaml .Values.nginx.topologySpreadConstraints | nindent 8 }}", }, specMap) assert.Equal(t, helmify.Values{ @@ -259,6 +277,9 @@ func Test_pod_Process(t *testing.T) { "tag": "1.14.2@sha256:cb5c1bddd1b5665e1867a7fa1b5fa843a47ee433bbb75d4293888b71def53229", }, }, + "nodeSelector": map[string]interface{}{}, + "tolerations": []interface{}{}, + "topologySpreadConstraints": []interface{}{}, }, }, tmpl) }) @@ -267,7 +288,7 @@ func Test_pod_Process(t *testing.T) { var deploy appsv1.Deployment obj := internal.GenerateObj(strDeploymentWithPort) err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &deploy) - specMap, tmpl, err := ProcessSpec("nginx", &metadata.Service{}, deploy.Spec.Template.Spec) + specMap, tmpl, err := ProcessSpec("nginx", &metadata.Service{}, deploy.Spec.Template.Spec, 0) assert.NoError(t, err) assert.Equal(t, map[string]interface{}{ @@ -288,6 +309,10 @@ func Test_pod_Process(t *testing.T) { "resources": map[string]interface{}{}, }, }, + "nodeSelector": "{{- toYaml .Values.nginx.nodeSelector | nindent 8 }}", + "serviceAccountName": "{{ default \"default\" (include \".serviceAccountName\" .) }}", + "tolerations": "{{- toYaml .Values.nginx.tolerations | nindent 8 }}", + "topologySpreadConstraints": "{{- toYaml .Values.nginx.topologySpreadConstraints | nindent 8 }}", }, specMap) assert.Equal(t, helmify.Values{ @@ -298,6 +323,9 @@ func Test_pod_Process(t *testing.T) { "tag": "latest", }, }, + "nodeSelector": map[string]interface{}{}, + "tolerations": []interface{}{}, + "topologySpreadConstraints": []interface{}{}, }, }, tmpl) }) @@ -305,7 +333,7 @@ func Test_pod_Process(t *testing.T) { var deploy appsv1.Deployment obj := internal.GenerateObj(strDeploymentWithPodSecurityContext) err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj.Object, &deploy) - specMap, tmpl, err := ProcessSpec("nginx", &metadata.Service{}, deploy.Spec.Template.Spec) + specMap, tmpl, err := ProcessSpec("nginx", &metadata.Service{}, deploy.Spec.Template.Spec, 0) assert.NoError(t, err) assert.Equal(t, map[string]interface{}{ "containers": []interface{}{ @@ -321,7 +349,11 @@ func Test_pod_Process(t *testing.T) { "resources": map[string]interface{}{}, }, }, - "securityContext": "{{- toYaml .Values.nginx.podSecurityContext | nindent 8 }}", + "securityContext": "{{- toYaml .Values.nginx.podSecurityContext | nindent 8 }}", + "nodeSelector": "{{- toYaml .Values.nginx.nodeSelector | nindent 8 }}", + "serviceAccountName": "{{ default \"default\" (include \".serviceAccountName\" .) }}", + "tolerations": "{{- toYaml .Values.nginx.tolerations | nindent 8 }}", + "topologySpreadConstraints": "{{- toYaml .Values.nginx.topologySpreadConstraints | nindent 8 }}", }, specMap) assert.Equal(t, helmify.Values{ @@ -338,6 +370,9 @@ func Test_pod_Process(t *testing.T) { "tag": "latest", }, }, + "nodeSelector": map[string]interface{}{}, + "tolerations": []interface{}{}, + "topologySpreadConstraints": []interface{}{}, }, }, tmpl) }) diff --git a/pkg/processor/rbac/clusterrolebinding.go b/pkg/processor/rbac/clusterrolebinding.go index 5b97b9fe..5356f43a 100644 --- a/pkg/processor/rbac/clusterrolebinding.go +++ b/pkg/processor/rbac/clusterrolebinding.go @@ -60,7 +60,7 @@ func (r clusterRoleBinding) Process(appMeta helmify.AppMetadata, obj *unstructur for i, s := range rb.Subjects { s.Namespace = "{{ .Release.Namespace }}" - s.Name = appMeta.TemplatedName(s.Name) + s.Name = fmt.Sprintf("{{ include \"%s.serviceAccountName\" . }}", appMeta.ChartName()) rb.Subjects[i] = s } subjects, err := yamlformat.Marshal(map[string]interface{}{"subjects": &rb.Subjects}, 0) diff --git a/pkg/processor/rbac/rolebinding.go b/pkg/processor/rbac/rolebinding.go index 41a6d827..42872947 100644 --- a/pkg/processor/rbac/rolebinding.go +++ b/pkg/processor/rbac/rolebinding.go @@ -58,7 +58,7 @@ func (r roleBinding) Process(appMeta helmify.AppMetadata, obj *unstructured.Unst for i, s := range rb.Subjects { s.Namespace = "{{ .Release.Namespace }}" - s.Name = appMeta.TemplatedName(s.Name) + s.Name = fmt.Sprintf("{{ include \"%s.serviceAccountName\" . }}", appMeta.ChartName()) rb.Subjects[i] = s } subjects, err := yamlformat.Marshal(map[string]interface{}{"subjects": &rb.Subjects}, 0) diff --git a/pkg/processor/rbac/serviceaccount.go b/pkg/processor/rbac/serviceaccount.go index bfb4e5ee..75817701 100644 --- a/pkg/processor/rbac/serviceaccount.go +++ b/pkg/processor/rbac/serviceaccount.go @@ -1,13 +1,27 @@ package rbac import ( - "github.com/arttor/helmify/pkg/helmify" - "github.com/arttor/helmify/pkg/processor" + "fmt" "io" + + "github.com/arttor/helmify/pkg/helmify" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" ) +const ( + saTempl = `{{ if .Values.serviceAccount.create }} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "%[1]s.serviceAccountName" . }} + labels: + {{- include "%[1]s.labels" . | nindent 4 }} + annotations: + {{- toYaml .Values.serviceAccount.annotations | nindent 4 }} +{{- end }}` +) + var serviceAccountGVC = schema.GroupVersionKind{ Group: "", Version: "v1", @@ -27,10 +41,18 @@ func (sa serviceAccount) Process(appMeta helmify.AppMetadata, obj *unstructured. return false, nil, nil } values := helmify.Values{} - meta, err := processor.ProcessObjMeta(appMeta, obj, processor.WithAnnotations(values)) + _, _ = values.Add(true, "serviceAccount", "create") + _, _ = values.Add("", "serviceAccount", "name") + valuesAnnotations := make(map[string]interface{}) + for k, v := range obj.GetAnnotations() { + valuesAnnotations[k] = v + } + err := unstructured.SetNestedField(values, valuesAnnotations, "serviceAccount", "annotations") if err != nil { return true, nil, err } + tmpl := saTempl + meta := fmt.Sprintf(tmpl, appMeta.ChartName()) return true, &saResult{ data: []byte(meta), diff --git a/pkg/processor/security-context/container_security_context.go b/pkg/processor/security-context/container_security_context.go index 08420e8b..76303602 100644 --- a/pkg/processor/security-context/container_security_context.go +++ b/pkg/processor/security-context/container_security_context.go @@ -11,17 +11,17 @@ import ( const ( sc = "securityContext" cscValueName = "containerSecurityContext" - helmTemplate = "{{- toYaml .Values.%[1]s.%[2]s.containerSecurityContext | nindent 10 }}" + helmTemplate = "{{- toYaml .Values.%[1]s.%[2]s.containerSecurityContext | nindent %[3]d }}" ) // ProcessContainerSecurityContext adds 'securityContext' to the podSpec in specMap, if it doesn't have one already defined. -func ProcessContainerSecurityContext(nameCamel string, specMap map[string]interface{}, values *helmify.Values) error { - err := processSecurityContext(nameCamel, "containers", specMap, values) +func ProcessContainerSecurityContext(nameCamel string, specMap map[string]interface{}, values *helmify.Values, nindent int) error { + err := processSecurityContext(nameCamel, "containers", specMap, values, nindent) if err != nil { return err } - err = processSecurityContext(nameCamel, "initContainers", specMap, values) + err = processSecurityContext(nameCamel, "initContainers", specMap, values, nindent) if err != nil { return err } @@ -29,13 +29,13 @@ func ProcessContainerSecurityContext(nameCamel string, specMap map[string]interf return nil } -func processSecurityContext(nameCamel string, containerType string, specMap map[string]interface{}, values *helmify.Values) error { +func processSecurityContext(nameCamel string, containerType string, specMap map[string]interface{}, values *helmify.Values, nindent int) error { if containers, defined := specMap[containerType]; defined { for _, container := range containers.([]interface{}) { castedContainer := container.(map[string]interface{}) containerName := strcase.ToLowerCamel(castedContainer["name"].(string)) if _, defined2 := castedContainer["securityContext"]; defined2 { - err := setSecContextValue(nameCamel, containerName, castedContainer, values) + err := setSecContextValue(nameCamel, containerName, castedContainer, values, nindent) if err != nil { return err } @@ -49,14 +49,14 @@ func processSecurityContext(nameCamel string, containerType string, specMap map[ return nil } -func setSecContextValue(resourceName string, containerName string, castedContainer map[string]interface{}, values *helmify.Values) error { +func setSecContextValue(resourceName string, containerName string, castedContainer map[string]interface{}, values *helmify.Values, nindent int) error { if castedContainer["securityContext"] != nil { err := unstructured.SetNestedField(*values, castedContainer["securityContext"], resourceName, containerName, cscValueName) if err != nil { return err } - valueString := fmt.Sprintf(helmTemplate, resourceName, containerName) + valueString := fmt.Sprintf(helmTemplate, resourceName, containerName, nindent+2) err = unstructured.SetNestedField(castedContainer, valueString, sc) if err != nil { diff --git a/pkg/processor/security-context/container_security_context_test.go b/pkg/processor/security-context/container_security_context_test.go index 8f072f38..db237a9e 100644 --- a/pkg/processor/security-context/container_security_context_test.go +++ b/pkg/processor/security-context/container_security_context_test.go @@ -93,7 +93,7 @@ func TestProcessContainerSecurityContext(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - ProcessContainerSecurityContext(tt.args.nameCamel, tt.args.specMap, tt.args.values) + ProcessContainerSecurityContext(tt.args.nameCamel, tt.args.specMap, tt.args.values, 8) assert.Equal(t, tt.want, tt.args.values) }) } @@ -140,7 +140,7 @@ func Test_setSecContextValue(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - setSecContextValue(tt.args.resourceName, tt.args.containerName, tt.args.castedContainer, tt.args.values) + setSecContextValue(tt.args.resourceName, tt.args.containerName, tt.args.castedContainer, tt.args.values, 8) assert.Equal(t, tt.want, tt.args.values) }) } diff --git a/pkg/processor/statefulset/statefulset.go b/pkg/processor/statefulset/statefulset.go index 1091151a..7306da27 100644 --- a/pkg/processor/statefulset/statefulset.go +++ b/pkg/processor/statefulset/statefulset.go @@ -2,11 +2,12 @@ package statefulset import ( "fmt" - "github.com/arttor/helmify/pkg/processor/pod" "io" "strings" "text/template" + "github.com/arttor/helmify/pkg/processor/pod" + "github.com/arttor/helmify/pkg/helmify" "github.com/arttor/helmify/pkg/processor" yamlformat "github.com/arttor/helmify/pkg/yaml" @@ -108,7 +109,7 @@ func (d statefulset) Process(appMeta helmify.AppMetadata, obj *unstructured.Unst } // process pod spec: - podSpecMap, podValues, err := pod.ProcessSpec(nameCamel, appMeta, ssSpec.Template.Spec) + podSpecMap, podValues, err := pod.ProcessSpec(nameCamel, appMeta, ssSpec.Template.Spec, 0) if err != nil { return true, nil, err } diff --git a/test_data/sample-app.yaml b/test_data/sample-app.yaml index d2399c81..4e754939 100644 --- a/test_data/sample-app.yaml +++ b/test_data/sample-app.yaml @@ -154,7 +154,7 @@ spec: app: myapp type: LoadBalancer loadBalancerSourceRanges: - - 10.0.0.0/8 + - 10.0.0.0/8 --- apiVersion: networking.k8s.io/v1 kind: Ingress @@ -233,7 +233,7 @@ data: name: default-oneperhost-pod-template spec: templates: - podTemplates: + podTemplates: - name: default-oneperhost-pod-template distribution: "OnePerHost" --- @@ -254,34 +254,34 @@ spec: name: fluentd-elasticsearch spec: tolerations: - # this toleration is to have the daemonset runnable on master nodes - # remove it if your masters can't run pods - - key: node-role.kubernetes.io/master - operator: Exists - effect: NoSchedule + # this toleration is to have the daemonset runnable on master nodes + # remove it if your masters can't run pods + - key: node-role.kubernetes.io/master + operator: Exists + effect: NoSchedule containers: - - name: fluentd-elasticsearch - image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2 - resources: - limits: - memory: 200Mi - requests: - cpu: 100m - memory: 200Mi - volumeMounts: - - name: varlog - mountPath: /var/log - - name: varlibdockercontainers - mountPath: /var/lib/docker/containers - readOnly: true + - name: fluentd-elasticsearch + image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2 + resources: + limits: + memory: 200Mi + requests: + cpu: 100m + memory: 200Mi + volumeMounts: + - name: varlog + mountPath: /var/log + - name: varlibdockercontainers + mountPath: /var/lib/docker/containers + readOnly: true terminationGracePeriodSeconds: 30 volumes: - - name: varlog - hostPath: - path: /var/log - - name: varlibdockercontainers - hostPath: - path: /var/lib/docker/containers + - name: varlog + hostPath: + path: /var/log + - name: varlibdockercontainers + hostPath: + path: /var/lib/docker/containers --- apiVersion: batch/v1 kind: Job @@ -293,7 +293,7 @@ spec: containers: - name: pi image: perl:5.34.0 - command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] + command: ["perl", "-Mbignum=bpi", "-wle", "print bpi(2000)"] restartPolicy: Never backoffLimit: 4 --- @@ -359,7 +359,7 @@ spec: - metadata: name: www spec: - accessModes: [ "ReadWriteOnce" ] + accessModes: ["ReadWriteOnce"] resources: requests: storage: 1Gi From 8779976d71e02b151496b81cd968f2185ce40b22 Mon Sep 17 00:00:00 2001 From: David Sabatie Date: Fri, 24 Oct 2025 23:11:00 +0200 Subject: [PATCH 2/4] fix: backport serviceAccount manifest from helm.sh default Signed-off-by: David Sabatie --- pkg/processor/pod/pod.go | 2 +- pkg/processor/pod/pod_test.go | 10 +++++----- pkg/processor/rbac/serviceaccount.go | 6 +++++- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/pkg/processor/pod/pod.go b/pkg/processor/pod/pod.go index b9aabaa4..1f3a016f 100644 --- a/pkg/processor/pod/pod.go +++ b/pkg/processor/pod/pod.go @@ -222,7 +222,7 @@ func processPodSpec(name string, appMeta helmify.AppMetadata, pod *corev1.PodSpe v.Secret.SecretName = appMeta.TemplatedName(v.Secret.SecretName) } } - pod.ServiceAccountName = fmt.Sprintf("{{ default \"default\" (include \"%s.serviceAccountName\" .) }}", appMeta.ChartName()) + pod.ServiceAccountName = fmt.Sprintf(`{{ include "%s.serviceAccountName" . }}`, appMeta.ChartName()) for i, s := range pod.ImagePullSecrets { pod.ImagePullSecrets[i].Name = appMeta.TemplatedName(s.Name) diff --git a/pkg/processor/pod/pod_test.go b/pkg/processor/pod/pod_test.go index 81d20d96..5ac49c01 100644 --- a/pkg/processor/pod/pod_test.go +++ b/pkg/processor/pod/pod_test.go @@ -170,7 +170,7 @@ func Test_pod_Process(t *testing.T) { "tolerations": "{{- toYaml .Values.nginx.tolerations | nindent 8 }}", "topologySpreadConstraints": "{{- toYaml .Values.nginx.topologySpreadConstraints | nindent 8 }}", "nodeSelector": "{{- toYaml .Values.nginx.nodeSelector | nindent 8 }}", - "serviceAccountName": "{{ default \"default\" (include \".serviceAccountName\" .) }}", + "serviceAccountName": `{{ include ".serviceAccountName" . }}`, }, specMap) assert.Equal(t, helmify.Values{ @@ -218,7 +218,7 @@ func Test_pod_Process(t *testing.T) { }, }, "nodeSelector": "{{- toYaml .Values.nginx.nodeSelector | nindent 8 }}", - "serviceAccountName": "{{ default \"default\" (include \".serviceAccountName\" .) }}", + "serviceAccountName": `{{ include ".serviceAccountName" . }}`, "tolerations": "{{- toYaml .Values.nginx.tolerations | nindent 8 }}", "topologySpreadConstraints": "{{- toYaml .Values.nginx.topologySpreadConstraints | nindent 8 }}", }, specMap) @@ -264,7 +264,7 @@ func Test_pod_Process(t *testing.T) { }, }, "nodeSelector": "{{- toYaml .Values.nginx.nodeSelector | nindent 8 }}", - "serviceAccountName": "{{ default \"default\" (include \".serviceAccountName\" .) }}", + "serviceAccountName": `{{ include ".serviceAccountName" . }}`, "tolerations": "{{- toYaml .Values.nginx.tolerations | nindent 8 }}", "topologySpreadConstraints": "{{- toYaml .Values.nginx.topologySpreadConstraints | nindent 8 }}", }, specMap) @@ -310,7 +310,7 @@ func Test_pod_Process(t *testing.T) { }, }, "nodeSelector": "{{- toYaml .Values.nginx.nodeSelector | nindent 8 }}", - "serviceAccountName": "{{ default \"default\" (include \".serviceAccountName\" .) }}", + "serviceAccountName": `{{ include ".serviceAccountName" . }}`, "tolerations": "{{- toYaml .Values.nginx.tolerations | nindent 8 }}", "topologySpreadConstraints": "{{- toYaml .Values.nginx.topologySpreadConstraints | nindent 8 }}", }, specMap) @@ -351,7 +351,7 @@ func Test_pod_Process(t *testing.T) { }, "securityContext": "{{- toYaml .Values.nginx.podSecurityContext | nindent 8 }}", "nodeSelector": "{{- toYaml .Values.nginx.nodeSelector | nindent 8 }}", - "serviceAccountName": "{{ default \"default\" (include \".serviceAccountName\" .) }}", + "serviceAccountName": `{{ include ".serviceAccountName" . }}`, "tolerations": "{{- toYaml .Values.nginx.tolerations | nindent 8 }}", "topologySpreadConstraints": "{{- toYaml .Values.nginx.topologySpreadConstraints | nindent 8 }}", }, specMap) diff --git a/pkg/processor/rbac/serviceaccount.go b/pkg/processor/rbac/serviceaccount.go index 75817701..2009f4a3 100644 --- a/pkg/processor/rbac/serviceaccount.go +++ b/pkg/processor/rbac/serviceaccount.go @@ -17,8 +17,11 @@ metadata: name: {{ include "%[1]s.serviceAccountName" . }} labels: {{- include "%[1]s.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} annotations: - {{- toYaml .Values.serviceAccount.annotations | nindent 4 }} + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} {{- end }}` ) @@ -43,6 +46,7 @@ func (sa serviceAccount) Process(appMeta helmify.AppMetadata, obj *unstructured. values := helmify.Values{} _, _ = values.Add(true, "serviceAccount", "create") _, _ = values.Add("", "serviceAccount", "name") + _, _ = values.Add(true, "serviceAccount", "automount") valuesAnnotations := make(map[string]interface{}) for k, v := range obj.GetAnnotations() { valuesAnnotations[k] = v From 8664cdd0276e0f5c67f40eea1fd23d4b32e75515 Mon Sep 17 00:00:00 2001 From: David Sabatie Date: Sat, 1 Nov 2025 11:28:13 +0100 Subject: [PATCH 3/4] fix: linter Signed-off-by: David Sabatie --- pkg/processor/pod/pod.go | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/pkg/processor/pod/pod.go b/pkg/processor/pod/pod.go index 1f3a016f..08d6bd4e 100644 --- a/pkg/processor/pod/pod.go +++ b/pkg/processor/pod/pod.go @@ -106,8 +106,14 @@ func ProcessSpec(objName string, appMeta helmify.AppMetadata, spec corev1.PodSpe } if spec.Tolerations != nil { tolerations := make([]any, len(spec.Tolerations)) - inrec, _ := json.Marshal(spec.Tolerations) - json.Unmarshal(inrec, &tolerations) + inrec, err := json.Marshal(spec.Tolerations) + if err != nil { + return nil, nil, err + } + err = json.Unmarshal(inrec, &tolerations) + if err != nil { + return nil, nil, err + } err = unstructured.SetNestedSlice(values, tolerations, objName, "tolerations") if err != nil { return nil, nil, err @@ -126,8 +132,14 @@ func ProcessSpec(objName string, appMeta helmify.AppMetadata, spec corev1.PodSpe } if spec.TopologySpreadConstraints != nil { topologySpreadConstraints := make([]any, len(spec.TopologySpreadConstraints)) - inrec, _ := json.Marshal(spec.TopologySpreadConstraints) - json.Unmarshal(inrec, &topologySpreadConstraints) + inrec, err := json.Marshal(spec.TopologySpreadConstraints) + if err != nil { + return nil, nil, err + } + err = json.Unmarshal(inrec, &topologySpreadConstraints) + if err != nil { + return nil, nil, err + } err = unstructured.SetNestedSlice(values, topologySpreadConstraints, objName, "topologySpreadConstraints") if err != nil { return nil, nil, err From 0f5c8d6d111c2ec963fa9cb51dc25937299cd0fa Mon Sep 17 00:00:00 2001 From: David Sabatie Date: Sat, 1 Nov 2025 23:17:27 +0100 Subject: [PATCH 4/4] fix: examples Signed-off-by: David Sabatie --- examples/app/templates/batch-job.yaml | 3 +-- examples/app/templates/cron-job.yaml | 3 +-- examples/app/templates/daemonset.yaml | 2 +- examples/app/templates/deployment.yaml | 2 +- examples/app/templates/statefulset.yaml | 3 +-- examples/operator/templates/deployment.yaml | 3 +-- examples/operator/templates/serviceaccount.yaml | 5 ++++- examples/operator/values.yaml | 1 + 8 files changed, 11 insertions(+), 11 deletions(-) diff --git a/examples/app/templates/batch-job.yaml b/examples/app/templates/batch-job.yaml index 40dfa099..d14b8d3a 100644 --- a/examples/app/templates/batch-job.yaml +++ b/examples/app/templates/batch-job.yaml @@ -23,8 +23,7 @@ spec: resources: {} nodeSelector: {{- toYaml .Values.batchJob.nodeSelector | nindent 8 }} restartPolicy: Never - serviceAccountName: {{ default "default" (include "app.serviceAccountName" - .) }} + serviceAccountName: {{ include "app.serviceAccountName" . }} tolerations: {{- toYaml .Values.batchJob.tolerations | nindent 8 }} topologySpreadConstraints: {{- toYaml .Values.batchJob.topologySpreadConstraints | nindent 8 }} diff --git a/examples/app/templates/cron-job.yaml b/examples/app/templates/cron-job.yaml index 74957427..3025af6f 100644 --- a/examples/app/templates/cron-job.yaml +++ b/examples/app/templates/cron-job.yaml @@ -24,8 +24,7 @@ spec: resources: {} nodeSelector: {{- toYaml .Values.cronJob.nodeSelector | nindent 12 }} restartPolicy: OnFailure - serviceAccountName: {{ default "default" (include "app.serviceAccountName" - .) }} + serviceAccountName: {{ include "app.serviceAccountName" . }} tolerations: {{- toYaml .Values.cronJob.tolerations | nindent 12 }} topologySpreadConstraints: {{- toYaml .Values.cronJob.topologySpreadConstraints | nindent 12 }} diff --git a/examples/app/templates/daemonset.yaml b/examples/app/templates/daemonset.yaml index 1b5533cc..06eedd41 100644 --- a/examples/app/templates/daemonset.yaml +++ b/examples/app/templates/daemonset.yaml @@ -33,7 +33,7 @@ spec: name: varlibdockercontainers readOnly: true nodeSelector: {{- toYaml .Values.fluentdElasticsearch.nodeSelector | nindent 8 }} - serviceAccountName: {{ default "default" (include "app.serviceAccountName" .) }} + serviceAccountName: {{ include "app.serviceAccountName" . }} terminationGracePeriodSeconds: 30 tolerations: {{- toYaml .Values.fluentdElasticsearch.tolerations | nindent 8 }} topologySpreadConstraints: {{- toYaml .Values.fluentdElasticsearch.topologySpreadConstraints diff --git a/examples/app/templates/deployment.yaml b/examples/app/templates/deployment.yaml index 57aa8369..3567b1e5 100644 --- a/examples/app/templates/deployment.yaml +++ b/examples/app/templates/deployment.yaml @@ -96,7 +96,7 @@ spec: resources: {} nodeSelector: {{- toYaml .Values.myapp.nodeSelector | nindent 8 }} securityContext: {{- toYaml .Values.myapp.podSecurityContext | nindent 8 }} - serviceAccountName: {{ default "default" (include "app.serviceAccountName" .) }} + serviceAccountName: {{ include "app.serviceAccountName" . }} terminationGracePeriodSeconds: 10 tolerations: {{- toYaml .Values.myapp.tolerations | nindent 8 }} topologySpreadConstraints: {{- toYaml .Values.myapp.topologySpreadConstraints | nindent diff --git a/examples/app/templates/statefulset.yaml b/examples/app/templates/statefulset.yaml index a9f43fb6..641cc376 100644 --- a/examples/app/templates/statefulset.yaml +++ b/examples/app/templates/statefulset.yaml @@ -30,8 +30,7 @@ spec: - mountPath: /usr/share/nginx/html name: www nodeSelector: {{- toYaml .Values.web.nodeSelector | nindent 8 }} - serviceAccountName: {{ default "default" (include "app.serviceAccountName" .) - }} + serviceAccountName: {{ include "app.serviceAccountName" . }} tolerations: {{- toYaml .Values.web.tolerations | nindent 8 }} topologySpreadConstraints: {{- toYaml .Values.web.topologySpreadConstraints | nindent 8 }} diff --git a/examples/operator/templates/deployment.yaml b/examples/operator/templates/deployment.yaml index 3af0fe9c..5f2fc173 100644 --- a/examples/operator/templates/deployment.yaml +++ b/examples/operator/templates/deployment.yaml @@ -96,8 +96,7 @@ spec: nodeSelector: {{- toYaml .Values.controllerManager.nodeSelector | nindent 8 }} securityContext: {{- toYaml .Values.controllerManager.podSecurityContext | nindent 8 }} - serviceAccountName: {{ default "default" (include "operator.serviceAccountName" .) - }} + serviceAccountName: {{ include "operator.serviceAccountName" . }} terminationGracePeriodSeconds: 10 tolerations: {{- toYaml .Values.controllerManager.tolerations | nindent 8 }} topologySpreadConstraints: {{- toYaml .Values.controllerManager.topologySpreadConstraints diff --git a/examples/operator/templates/serviceaccount.yaml b/examples/operator/templates/serviceaccount.yaml index a8fdf5bd..02d22cc5 100644 --- a/examples/operator/templates/serviceaccount.yaml +++ b/examples/operator/templates/serviceaccount.yaml @@ -5,6 +5,9 @@ metadata: name: {{ include "operator.serviceAccountName" . }} labels: {{- include "operator.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} annotations: - {{- toYaml .Values.serviceAccount.annotations | nindent 4 }} + {{- toYaml . | nindent 4 }} + {{- end }} +automountServiceAccountToken: {{ .Values.serviceAccount.automount }} {{- end }} diff --git a/examples/operator/values.yaml b/examples/operator/values.yaml index 05ccd039..495b1641 100644 --- a/examples/operator/values.yaml +++ b/examples/operator/values.yaml @@ -97,6 +97,7 @@ secretVars: serviceAccount: annotations: k8s.acme.org/some-meta-data: ACME Inc. + automount: true create: true name: "" webhookService: