From db18ef9ac3aa70a4bfc01b056e2ae17b467a92e6 Mon Sep 17 00:00:00 2001 From: girdharshubham Date: Tue, 17 Feb 2026 17:28:30 +0530 Subject: [PATCH 1/2] fix: respect -preserve-ns flag in Certificate, Issuer, and ServiceAccount processors Certificate and Issuer processors used hardcoded templates that never included the namespace field, even when -preserve-ns was set. Refactored both to use ProcessObjMeta for metadata generation, which already handles namespace preservation. Cert-manager hook annotations are injected onto the object before calling ProcessObjMeta when -cert-manager-as-subchart is enabled. ServiceAccount retains its custom template but now conditionally injects the namespace when -preserve-ns is set. Also fixed a pre-existing bug where Issuer processor was not passing values to issResult, causing webhook.enabled to be silently dropped. Fixes arttor/helmify#192 --- pkg/processor/rbac/serviceaccount.go | 8 +++++- pkg/processor/webhook/cert.go | 39 ++++++++++++---------------- pkg/processor/webhook/issuer.go | 39 ++++++++++------------------ 3 files changed, 36 insertions(+), 50 deletions(-) diff --git a/pkg/processor/rbac/serviceaccount.go b/pkg/processor/rbac/serviceaccount.go index 2009f4a3..96cd864c 100644 --- a/pkg/processor/rbac/serviceaccount.go +++ b/pkg/processor/rbac/serviceaccount.go @@ -21,6 +21,7 @@ metadata: annotations: {{- toYaml . | nindent 4 }} {{- end }} + %[2]s automountServiceAccountToken: {{ .Values.serviceAccount.automount }} {{- end }}` ) @@ -56,7 +57,12 @@ func (sa serviceAccount) Process(appMeta helmify.AppMetadata, obj *unstructured. return true, nil, err } tmpl := saTempl - meta := fmt.Sprintf(tmpl, appMeta.ChartName()) + namespace := "" + if obj.GetNamespace() != "" && appMeta.Config().PreserveNs { + namespace = fmt.Sprintf("namespace: %s", obj.GetNamespace()) + } + + meta := fmt.Sprintf(tmpl, appMeta.ChartName(), namespace) return true, &saResult{ data: []byte(meta), diff --git a/pkg/processor/webhook/cert.go b/pkg/processor/webhook/cert.go index f09a61e3..6083ed33 100644 --- a/pkg/processor/webhook/cert.go +++ b/pkg/processor/webhook/cert.go @@ -8,6 +8,7 @@ import ( "github.com/arttor/helmify/pkg/cluster" "github.com/arttor/helmify/pkg/helmify" + "github.com/arttor/helmify/pkg/processor" yamlformat "github.com/arttor/helmify/pkg/yaml" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" @@ -17,25 +18,6 @@ import ( const ( WebhookHeader = `{{- if .Values.webhook.enabled }}` WebhookFooter = `{{- end }}` - certTempl = `apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: {{ include "%[1]s.fullname" . }}-%[2]s - labels: - {{- include "%[1]s.labels" . | nindent 4 }} -spec: -%[3]s` - certTemplWithAnno = `apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: {{ include "%[1]s.fullname" . }}-%[2]s - annotations: - "helm.sh/hook": post-install,post-upgrade - "helm.sh/hook-weight": "2" - labels: - {{- include "%[1]s.labels" . | nindent 4 }} -spec: -%[3]s` ) var certGVC = schema.GroupVersionKind{ @@ -89,11 +71,22 @@ func (c cert) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructure spec = yamlformat.Indent(spec, 2) spec = bytes.TrimRight(spec, "\n ") tmpl := "" + if appMeta.Config().CertManagerAsSubchart { - tmpl = certTemplWithAnno - } else { - tmpl = certTempl + annotations := obj.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) + } + annotations["helm.sh/hook"] = "post-install,post-upgrade" + annotations["helm.sh/hook-weight"] = "2" + obj.SetAnnotations(annotations) } + + tmpl, err = processor.ProcessObjMeta(appMeta, obj) + if err != nil { + return true, nil, err + } + values := helmify.Values{} if appMeta.Config().AddWebhookOption { // Add webhook.enabled value to values.yaml @@ -101,7 +94,7 @@ func (c cert) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructure tmpl = fmt.Sprintf("%s\n%s\n%s", WebhookHeader, tmpl, WebhookFooter) } - res := fmt.Sprintf(tmpl, appMeta.ChartName(), name, string(spec)) + res := tmpl + "\nspec:\n" + string(spec) return true, &certResult{ name: name, data: []byte(res), diff --git a/pkg/processor/webhook/issuer.go b/pkg/processor/webhook/issuer.go index 6e5d5b67..82dee23e 100644 --- a/pkg/processor/webhook/issuer.go +++ b/pkg/processor/webhook/issuer.go @@ -6,34 +6,13 @@ import ( "io" "github.com/arttor/helmify/pkg/helmify" + "github.com/arttor/helmify/pkg/processor" yamlformat "github.com/arttor/helmify/pkg/yaml" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime/schema" "sigs.k8s.io/yaml" ) -const ( - issuerTempl = `apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - name: {{ include "%[1]s.fullname" . }}-%[2]s - labels: - {{- include "%[1]s.labels" . | nindent 4 }} -spec: -%[3]s` - issuerTemplWithAnno = `apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - name: {{ include "%[1]s.fullname" . }}-%[2]s - annotations: - "helm.sh/hook": post-install,post-upgrade - "helm.sh/hook-weight": "1" - labels: - {{- include "%[1]s.labels" . | nindent 4 }} -spec: -%[3]s` -) - var issuerGVC = schema.GroupVersionKind{ Group: "cert-manager.io", Version: "v1", @@ -59,9 +38,17 @@ func (i issuer) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructu spec = bytes.TrimRight(spec, "\n ") tmpl := "" if appMeta.Config().CertManagerAsSubchart { - tmpl = issuerTemplWithAnno - } else { - tmpl = issuerTempl + annotations := obj.GetAnnotations() + if annotations == nil { + annotations = make(map[string]string) + } + annotations["helm.sh/hook"] = "post-install,post-upgrade" + annotations["helm.sh/hook-weight"] = "1" + obj.SetAnnotations(annotations) + } + tmpl, err := processor.ProcessObjMeta(appMeta, obj) + if err != nil { + return true, nil, err } values := helmify.Values{} if appMeta.Config().AddWebhookOption { @@ -70,7 +57,7 @@ func (i issuer) Process(appMeta helmify.AppMetadata, obj *unstructured.Unstructu tmpl = fmt.Sprintf("%s\n%s\n%s", WebhookHeader, tmpl, WebhookFooter) } - res := fmt.Sprintf(tmpl, appMeta.ChartName(), name, string(spec)) + res := tmpl + "\nspec:\n" + string(spec) return true, &issResult{ name: name, data: []byte(res), From 9d6f48a89f5bfa54344b82c4eac3a7b5de8b4e3e Mon Sep 17 00:00:00 2001 From: girdharshubham Date: Tue, 17 Feb 2026 17:39:22 +0530 Subject: [PATCH 2/2] fix: preserve original subject namespace in RoleBinding and ClusterRoleBinding with -preserve-ns When -preserve-ns is set, keep the original namespace on binding subjects instead of replacing it with {{ .Release.Namespace }}. This ensures correct ServiceAccount references in umbrella chart deployments where the release namespace differs from the target namespace. --- .gitignore | 3 +++ pkg/processor/rbac/clusterrolebinding.go | 4 +++- pkg/processor/rbac/rolebinding.go | 4 +++- 3 files changed, 9 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index bc702547..12800472 100644 --- a/.gitignore +++ b/.gitignore @@ -29,3 +29,6 @@ testbin/* dist/ vendor/ + +# Binary +helmify diff --git a/pkg/processor/rbac/clusterrolebinding.go b/pkg/processor/rbac/clusterrolebinding.go index 5356f43a..6023a26b 100644 --- a/pkg/processor/rbac/clusterrolebinding.go +++ b/pkg/processor/rbac/clusterrolebinding.go @@ -59,7 +59,9 @@ func (r clusterRoleBinding) Process(appMeta helmify.AppMetadata, obj *unstructur } for i, s := range rb.Subjects { - s.Namespace = "{{ .Release.Namespace }}" + if !(appMeta.Config().PreserveNs && s.Namespace != "") { + s.Namespace = "{{ .Release.Namespace }}" + } s.Name = fmt.Sprintf("{{ include \"%s.serviceAccountName\" . }}", appMeta.ChartName()) rb.Subjects[i] = s } diff --git a/pkg/processor/rbac/rolebinding.go b/pkg/processor/rbac/rolebinding.go index 42872947..a8cb2256 100644 --- a/pkg/processor/rbac/rolebinding.go +++ b/pkg/processor/rbac/rolebinding.go @@ -57,7 +57,9 @@ func (r roleBinding) Process(appMeta helmify.AppMetadata, obj *unstructured.Unst } for i, s := range rb.Subjects { - s.Namespace = "{{ .Release.Namespace }}" + if !(appMeta.Config().PreserveNs && s.Namespace != "") { + s.Namespace = "{{ .Release.Namespace }}" + } s.Name = fmt.Sprintf("{{ include \"%s.serviceAccountName\" . }}", appMeta.ChartName()) rb.Subjects[i] = s }