Diff failing when diffing helm hook jobs with --take-ownership flag #782

Description

@blaskoa

Problem description

When a helm chart contains helm hooks defined as Jobs, then helm diff is not behaving as expected when used with --take-ownership flag:

1. When no changes are made on the chart, the chart is marked as having changes anyway

Correct behavior without --take-ownership flag (empty)

helm diff upgrade helm-diff-repro . --install --debug
Executing helm version
Executing helm get manifest helm-diff-repro --namespace default
Executing helm get values helm-diff-repro --output yaml --all
Executing helm version
Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values2739512397 --validate --is-upgrade --dry-run=client
Executing helm get hooks helm-diff-repro --namespace default

Incorrect behavior with --take-ownership flag

helm diff upgrade helm-diff-repro . --install --take-ownership --debug
Executing helm version
Executing helm get manifest helm-diff-repro --namespace default
Executing helm get values helm-diff-repro --output yaml --all
Executing helm version
Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values3415737044 --take-ownership --validate --is-upgrade --dry-run=client
default, helm-diff-repro-hook, Job (batch) changed ownership:
- + default/helm-diff-repro

We are using helmfile, and the problem causes creation of new release for all helm charts which contain any helm hooks, even when they did not change

2. When the helm hook contains any changes, then helm diff command fails

Correct behavior without --take-ownership flag

helm diff upgrade helm-diff-repro . --install --debug
Executing helm version
Executing helm get manifest helm-diff-repro --namespace default
Executing helm get values helm-diff-repro --output yaml --all
Executing helm version
Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values3415473100 --validate --is-upgrade --dry-run=client
Executing helm get hooks helm-diff-repro --namespace default
default, helm-diff-repro-hook, Job (batch) has changed:
# Source: test-chart/templates/deployment.yaml
kind: Job
apiVersion: batch/v1
metadata:
name: helm-diff-repro-hook
annotations:
"helm.sh/hook": pre-install,pre-upgrade
spec:
template:
spec:
containers:
- name: helm-diff-repro-hook
image: nginx
- command: ["/bin/sh", "-c", "echo 'Hello, World!'"]
+ command: ["/bin/sh", "-c", "echo 'Hello, World! 1'"]
restartPolicy: Never

Incorrect behavior (crash) with --take-ownership flag

helm diff upgrade helm-diff-repro . --install --take-ownership --debug
Executing helm version
Executing helm get manifest helm-diff-repro --namespace default
Executing helm get values helm-diff-repro --output yaml --all
Executing helm version
Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values854516463 --take-ownership --validate --is-upgrade --dry-run=client
Error: unable to generate manifests: cannot patch "helm-diff-repro-hook" with kind Job: Job.batch "helm-diff-repro-hook" is invalid: spec.template: Invalid value: core.PodTemplateSpec{ObjectMeta:v1.ObjectMeta{Name:"", GenerateName:"", Namespace:"", SelfLink:"", UID:"", ResourceVersion:"", Generation:0, CreationTimestamp:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), DeletionTimestamp:<nil>, DeletionGracePeriodSeconds:(*int64)(nil), Labels:map[string]string{"batch.kubernetes.io/controller-uid":"eb0265ed-b7eb-4e12-b09f-7896f25fbec4", "batch.kubernetes.io/job-name":"helm-diff-repro-hook", "controller-uid":"eb0265ed-b7eb-4e12-b09f-7896f25fbec4", "job-name":"helm-diff-repro-hook"}, Annotations:map[string]string(nil), OwnerReferences:[]v1.OwnerReference(nil), Finalizers:[]string(nil), ManagedFields:[]v1.ManagedFieldsEntry(nil)}, Spec:core.PodSpec{Volumes:[]core.Volume(nil), InitContainers:[]core.Container(nil), Containers:[]core.Container{core.Container{Name:"helm-diff-repro-hook", Image:"nginx", Command:[]string{"/bin/sh", "-c", "echo 'Hello, World! 1'"}, Args:[]string(nil), WorkingDir:"", Ports:[]core.ContainerPort(nil), EnvFrom:[]core.EnvFromSource(nil), Env:[]core.EnvVar(nil), Resources:core.ResourceRequirements{Limits:core.ResourceList(nil), Requests:core.ResourceList(nil), Claims:[]core.ResourceClaim(nil)}, ResizePolicy:[]core.ContainerResizePolicy(nil), RestartPolicy:(*core.ContainerRestartPolicy)(nil), VolumeMounts:[]core.VolumeMount(nil), VolumeDevices:[]core.VolumeDevice(nil), LivenessProbe:(*core.Probe)(nil), ReadinessProbe:(*core.Probe)(nil), StartupProbe:(*core.Probe)(nil), Lifecycle:(*core.Lifecycle)(nil), TerminationMessagePath:"/dev/termination-log", TerminationMessagePolicy:"File", ImagePullPolicy:"Always", SecurityContext:(*core.SecurityContext)(nil), Stdin:false, StdinOnce:false, TTY:false}}, EphemeralContainers:[]core.EphemeralContainer(nil), RestartPolicy:"Never", TerminationGracePeriodSeconds:(*int64)(0x400ccecc50), ActiveDeadlineSeconds:(*int64)(nil), DNSPolicy:"ClusterFirst", NodeSelector:map[string]string(nil), ServiceAccountName:"", AutomountServiceAccountToken:(*bool)(nil), NodeName:"", SecurityContext:(*core.PodSecurityContext)(0x4014d12cf0), ImagePullSecrets:[]core.LocalObjectReference(nil), Hostname:"", Subdomain:"", SetHostnameAsFQDN:(*bool)(nil), Affinity:(*core.Affinity)(nil), SchedulerName:"default-scheduler", Tolerations:[]core.Toleration(nil), HostAliases:[]core.HostAlias(nil), PriorityClassName:"", Priority:(*int32)(nil), PreemptionPolicy:(*core.PreemptionPolicy)(nil), DNSConfig:(*core.PodDNSConfig)(nil), ReadinessGates:[]core.PodReadinessGate(nil), RuntimeClassName:(*string)(nil), Overhead:core.ResourceList(nil), EnableServiceLinks:(*bool)(nil), TopologySpreadConstraints:[]core.TopologySpreadConstraint(nil), OS:(*core.PodOS)(nil), SchedulingGates:[]core.PodSchedulingGate(nil), ResourceClaims:[]core.PodResourceClaim(nil)}}: field is immutable
Error: plugin "diff" exited with error
helm.go:86: 2025-05-19 11:25:53.904139 +0200 CEST m=+0.367769876 [debug] plugin "diff" exited with error

Workaround

Currently we implemented a workaround by using --no-hooks flag. Using this flag has its own implications, but it works for our use case

Repro steps

To reproduce the issue I used a simple helm chart containing only one yaml file:

apiVersion: apps/v1kind: Deploymentmetadata:
name: helm-diff-reprospec:
selector:
matchLabels:
app: helm-diff-reprotemplate:
metadata:
labels:
app: helm-diff-reprospec:
containers:
- name: helm-diff-reproimage: nginx
---
kind: JobapiVersion: batch/v1metadata:
name: helm-diff-repro-hookannotations:
"helm.sh/hook": pre-install,pre-upgradespec:
template:
spec:
containers:
- name: helm-diff-repro-hookimage: nginxcommand: ["/bin/sh", "-c", "echo 'Hello, World!'"]restartPolicy: Never

Then simply install this chart to a kubernetes cluster.
After that the Problem 1. will be reproducible.
To reproduce Problem 2. make any changes in the helm hook manifest - I modified the helm hook job command

Tested versions

helm version
version.BuildInfo{Version:"v3.17.3", GitCommit:"e4da49785aa6e6ee2b86efd5dd9e43400318262b", GitTreeState:"clean", GoVersion:"go1.24.2"}
helm diff version
3.11.0

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions

      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
       blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
      }
      } catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
      })();
      (function(){
      try {
      var __m = "github.com";
      var __re = new RegExp('^' + "github\\.com" + '
      
      Skip to content

      Diff failing when diffing helm hook jobs with --take-ownership flag #782

      Description

      @blaskoa

      Problem description

      When a helm chart contains helm hooks defined as Jobs, then helm diff is not behaving as expected when used with --take-ownership flag:

      1. When no changes are made on the chart, the chart is marked as having changes anyway

      Correct behavior without --take-ownership flag (empty)

      helm diff upgrade helm-diff-repro . --install --debug
      Executing helm version
      Executing helm get manifest helm-diff-repro --namespace default
      Executing helm get values helm-diff-repro --output yaml --all
      Executing helm version
      Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values2739512397 --validate --is-upgrade --dry-run=client
      Executing helm get hooks helm-diff-repro --namespace default
      

      Incorrect behavior with --take-ownership flag

      helm diff upgrade helm-diff-repro . --install --take-ownership --debug
      Executing helm version
      Executing helm get manifest helm-diff-repro --namespace default
      Executing helm get values helm-diff-repro --output yaml --all
      Executing helm version
      Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values3415737044 --take-ownership --validate --is-upgrade --dry-run=client
      default, helm-diff-repro-hook, Job (batch) changed ownership:
      - + default/helm-diff-repro
      

      We are using helmfile, and the problem causes creation of new release for all helm charts which contain any helm hooks, even when they did not change

      2. When the helm hook contains any changes, then helm diff command fails

      Correct behavior without --take-ownership flag

      helm diff upgrade helm-diff-repro . --install --debug
      Executing helm version
      Executing helm get manifest helm-diff-repro --namespace default
      Executing helm get values helm-diff-repro --output yaml --all
      Executing helm version
      Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values3415473100 --validate --is-upgrade --dry-run=client
      Executing helm get hooks helm-diff-repro --namespace default
      default, helm-diff-repro-hook, Job (batch) has changed:
      # Source: test-chart/templates/deployment.yaml
      kind: Job
      apiVersion: batch/v1
      metadata:
      name: helm-diff-repro-hook
      annotations:
      "helm.sh/hook": pre-install,pre-upgrade
      spec:
      template:
      spec:
      containers:
      - name: helm-diff-repro-hook
      image: nginx
      - command: ["/bin/sh", "-c", "echo 'Hello, World!'"]
      + command: ["/bin/sh", "-c", "echo 'Hello, World! 1'"]
      restartPolicy: Never
      

      Incorrect behavior (crash) with --take-ownership flag

      helm diff upgrade helm-diff-repro . --install --take-ownership --debug
      Executing helm version
      Executing helm get manifest helm-diff-repro --namespace default
      Executing helm get values helm-diff-repro --output yaml --all
      Executing helm version
      Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values854516463 --take-ownership --validate --is-upgrade --dry-run=client
      Error: unable to generate manifests: cannot patch "helm-diff-repro-hook" with kind Job: Job.batch "helm-diff-repro-hook" is invalid: spec.template: Invalid value: core.PodTemplateSpec{ObjectMeta:v1.ObjectMeta{Name:"", GenerateName:"", Namespace:"", SelfLink:"", UID:"", ResourceVersion:"", Generation:0, CreationTimestamp:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), DeletionTimestamp:<nil>, DeletionGracePeriodSeconds:(*int64)(nil), Labels:map[string]string{"batch.kubernetes.io/controller-uid":"eb0265ed-b7eb-4e12-b09f-7896f25fbec4", "batch.kubernetes.io/job-name":"helm-diff-repro-hook", "controller-uid":"eb0265ed-b7eb-4e12-b09f-7896f25fbec4", "job-name":"helm-diff-repro-hook"}, Annotations:map[string]string(nil), OwnerReferences:[]v1.OwnerReference(nil), Finalizers:[]string(nil), ManagedFields:[]v1.ManagedFieldsEntry(nil)}, Spec:core.PodSpec{Volumes:[]core.Volume(nil), InitContainers:[]core.Container(nil), Containers:[]core.Container{core.Container{Name:"helm-diff-repro-hook", Image:"nginx", Command:[]string{"/bin/sh", "-c", "echo 'Hello, World! 1'"}, Args:[]string(nil), WorkingDir:"", Ports:[]core.ContainerPort(nil), EnvFrom:[]core.EnvFromSource(nil), Env:[]core.EnvVar(nil), Resources:core.ResourceRequirements{Limits:core.ResourceList(nil), Requests:core.ResourceList(nil), Claims:[]core.ResourceClaim(nil)}, ResizePolicy:[]core.ContainerResizePolicy(nil), RestartPolicy:(*core.ContainerRestartPolicy)(nil), VolumeMounts:[]core.VolumeMount(nil), VolumeDevices:[]core.VolumeDevice(nil), LivenessProbe:(*core.Probe)(nil), ReadinessProbe:(*core.Probe)(nil), StartupProbe:(*core.Probe)(nil), Lifecycle:(*core.Lifecycle)(nil), TerminationMessagePath:"/dev/termination-log", TerminationMessagePolicy:"File", ImagePullPolicy:"Always", SecurityContext:(*core.SecurityContext)(nil), Stdin:false, StdinOnce:false, TTY:false}}, EphemeralContainers:[]core.EphemeralContainer(nil), RestartPolicy:"Never", TerminationGracePeriodSeconds:(*int64)(0x400ccecc50), ActiveDeadlineSeconds:(*int64)(nil), DNSPolicy:"ClusterFirst", NodeSelector:map[string]string(nil), ServiceAccountName:"", AutomountServiceAccountToken:(*bool)(nil), NodeName:"", SecurityContext:(*core.PodSecurityContext)(0x4014d12cf0), ImagePullSecrets:[]core.LocalObjectReference(nil), Hostname:"", Subdomain:"", SetHostnameAsFQDN:(*bool)(nil), Affinity:(*core.Affinity)(nil), SchedulerName:"default-scheduler", Tolerations:[]core.Toleration(nil), HostAliases:[]core.HostAlias(nil), PriorityClassName:"", Priority:(*int32)(nil), PreemptionPolicy:(*core.PreemptionPolicy)(nil), DNSConfig:(*core.PodDNSConfig)(nil), ReadinessGates:[]core.PodReadinessGate(nil), RuntimeClassName:(*string)(nil), Overhead:core.ResourceList(nil), EnableServiceLinks:(*bool)(nil), TopologySpreadConstraints:[]core.TopologySpreadConstraint(nil), OS:(*core.PodOS)(nil), SchedulingGates:[]core.PodSchedulingGate(nil), ResourceClaims:[]core.PodResourceClaim(nil)}}: field is immutable
      Error: plugin "diff" exited with error
      helm.go:86: 2025-05-19 11:25:53.904139 +0200 CEST m=+0.367769876 [debug] plugin "diff" exited with error
      

      Workaround

      Currently we implemented a workaround by using --no-hooks flag. Using this flag has its own implications, but it works for our use case

      Repro steps

      To reproduce the issue I used a simple helm chart containing only one yaml file:

      apiVersion: apps/v1kind: Deploymentmetadata:
      name: helm-diff-reprospec:
      selector:
      matchLabels:
      app: helm-diff-reprotemplate:
      metadata:
      labels:
      app: helm-diff-reprospec:
      containers:
      - name: helm-diff-reproimage: nginx
      ---
      kind: JobapiVersion: batch/v1metadata:
      name: helm-diff-repro-hookannotations:
      "helm.sh/hook": pre-install,pre-upgradespec:
      template:
      spec:
      containers:
      - name: helm-diff-repro-hookimage: nginxcommand: ["/bin/sh", "-c", "echo 'Hello, World!'"]restartPolicy: Never

      Then simply install this chart to a kubernetes cluster.
      After that the Problem 1. will be reproducible.
      To reproduce Problem 2. make any changes in the helm hook manifest - I modified the helm hook job command

      Tested versions

      helm version
      version.BuildInfo{Version:"v3.17.3", GitCommit:"e4da49785aa6e6ee2b86efd5dd9e43400318262b", GitTreeState:"clean", GoVersion:"go1.24.2"}
      
      helm diff version
      3.11.0
      

      Activity

      Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

      Metadata

      Metadata

      Assignees

      No one assigned

        Labels

        Projects

        No projects

          Milestone

          No milestone

          Relationships

          None yet

          Development

          No branches or pull requests

          Issue actions

          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
          Skip to content

          Diff failing when diffing helm hook jobs with --take-ownership flag #782

          Description

          @blaskoa

          Problem description

          When a helm chart contains helm hooks defined as Jobs, then helm diff is not behaving as expected when used with --take-ownership flag:

          1. When no changes are made on the chart, the chart is marked as having changes anyway

          Correct behavior without --take-ownership flag (empty)

          helm diff upgrade helm-diff-repro . --install --debug
          Executing helm version
          Executing helm get manifest helm-diff-repro --namespace default
          Executing helm get values helm-diff-repro --output yaml --all
          Executing helm version
          Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values2739512397 --validate --is-upgrade --dry-run=client
          Executing helm get hooks helm-diff-repro --namespace default
          

          Incorrect behavior with --take-ownership flag

          helm diff upgrade helm-diff-repro . --install --take-ownership --debug
          Executing helm version
          Executing helm get manifest helm-diff-repro --namespace default
          Executing helm get values helm-diff-repro --output yaml --all
          Executing helm version
          Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values3415737044 --take-ownership --validate --is-upgrade --dry-run=client
          default, helm-diff-repro-hook, Job (batch) changed ownership:
          - + default/helm-diff-repro
          

          We are using helmfile, and the problem causes creation of new release for all helm charts which contain any helm hooks, even when they did not change

          2. When the helm hook contains any changes, then helm diff command fails

          Correct behavior without --take-ownership flag

          helm diff upgrade helm-diff-repro . --install --debug
          Executing helm version
          Executing helm get manifest helm-diff-repro --namespace default
          Executing helm get values helm-diff-repro --output yaml --all
          Executing helm version
          Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values3415473100 --validate --is-upgrade --dry-run=client
          Executing helm get hooks helm-diff-repro --namespace default
          default, helm-diff-repro-hook, Job (batch) has changed:
          # Source: test-chart/templates/deployment.yaml
          kind: Job
          apiVersion: batch/v1
          metadata:
          name: helm-diff-repro-hook
          annotations:
          "helm.sh/hook": pre-install,pre-upgrade
          spec:
          template:
          spec:
          containers:
          - name: helm-diff-repro-hook
          image: nginx
          - command: ["/bin/sh", "-c", "echo 'Hello, World!'"]
          + command: ["/bin/sh", "-c", "echo 'Hello, World! 1'"]
          restartPolicy: Never
          

          Incorrect behavior (crash) with --take-ownership flag

          helm diff upgrade helm-diff-repro . --install --take-ownership --debug
          Executing helm version
          Executing helm get manifest helm-diff-repro --namespace default
          Executing helm get values helm-diff-repro --output yaml --all
          Executing helm version
          Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values854516463 --take-ownership --validate --is-upgrade --dry-run=client
          Error: unable to generate manifests: cannot patch "helm-diff-repro-hook" with kind Job: Job.batch "helm-diff-repro-hook" is invalid: spec.template: Invalid value: core.PodTemplateSpec{ObjectMeta:v1.ObjectMeta{Name:"", GenerateName:"", Namespace:"", SelfLink:"", UID:"", ResourceVersion:"", Generation:0, CreationTimestamp:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), DeletionTimestamp:<nil>, DeletionGracePeriodSeconds:(*int64)(nil), Labels:map[string]string{"batch.kubernetes.io/controller-uid":"eb0265ed-b7eb-4e12-b09f-7896f25fbec4", "batch.kubernetes.io/job-name":"helm-diff-repro-hook", "controller-uid":"eb0265ed-b7eb-4e12-b09f-7896f25fbec4", "job-name":"helm-diff-repro-hook"}, Annotations:map[string]string(nil), OwnerReferences:[]v1.OwnerReference(nil), Finalizers:[]string(nil), ManagedFields:[]v1.ManagedFieldsEntry(nil)}, Spec:core.PodSpec{Volumes:[]core.Volume(nil), InitContainers:[]core.Container(nil), Containers:[]core.Container{core.Container{Name:"helm-diff-repro-hook", Image:"nginx", Command:[]string{"/bin/sh", "-c", "echo 'Hello, World! 1'"}, Args:[]string(nil), WorkingDir:"", Ports:[]core.ContainerPort(nil), EnvFrom:[]core.EnvFromSource(nil), Env:[]core.EnvVar(nil), Resources:core.ResourceRequirements{Limits:core.ResourceList(nil), Requests:core.ResourceList(nil), Claims:[]core.ResourceClaim(nil)}, ResizePolicy:[]core.ContainerResizePolicy(nil), RestartPolicy:(*core.ContainerRestartPolicy)(nil), VolumeMounts:[]core.VolumeMount(nil), VolumeDevices:[]core.VolumeDevice(nil), LivenessProbe:(*core.Probe)(nil), ReadinessProbe:(*core.Probe)(nil), StartupProbe:(*core.Probe)(nil), Lifecycle:(*core.Lifecycle)(nil), TerminationMessagePath:"/dev/termination-log", TerminationMessagePolicy:"File", ImagePullPolicy:"Always", SecurityContext:(*core.SecurityContext)(nil), Stdin:false, StdinOnce:false, TTY:false}}, EphemeralContainers:[]core.EphemeralContainer(nil), RestartPolicy:"Never", TerminationGracePeriodSeconds:(*int64)(0x400ccecc50), ActiveDeadlineSeconds:(*int64)(nil), DNSPolicy:"ClusterFirst", NodeSelector:map[string]string(nil), ServiceAccountName:"", AutomountServiceAccountToken:(*bool)(nil), NodeName:"", SecurityContext:(*core.PodSecurityContext)(0x4014d12cf0), ImagePullSecrets:[]core.LocalObjectReference(nil), Hostname:"", Subdomain:"", SetHostnameAsFQDN:(*bool)(nil), Affinity:(*core.Affinity)(nil), SchedulerName:"default-scheduler", Tolerations:[]core.Toleration(nil), HostAliases:[]core.HostAlias(nil), PriorityClassName:"", Priority:(*int32)(nil), PreemptionPolicy:(*core.PreemptionPolicy)(nil), DNSConfig:(*core.PodDNSConfig)(nil), ReadinessGates:[]core.PodReadinessGate(nil), RuntimeClassName:(*string)(nil), Overhead:core.ResourceList(nil), EnableServiceLinks:(*bool)(nil), TopologySpreadConstraints:[]core.TopologySpreadConstraint(nil), OS:(*core.PodOS)(nil), SchedulingGates:[]core.PodSchedulingGate(nil), ResourceClaims:[]core.PodResourceClaim(nil)}}: field is immutable
          Error: plugin "diff" exited with error
          helm.go:86: 2025-05-19 11:25:53.904139 +0200 CEST m=+0.367769876 [debug] plugin "diff" exited with error
          

          Workaround

          Currently we implemented a workaround by using --no-hooks flag. Using this flag has its own implications, but it works for our use case

          Repro steps

          To reproduce the issue I used a simple helm chart containing only one yaml file:

          apiVersion: apps/v1kind: Deploymentmetadata:
          name: helm-diff-reprospec:
          selector:
          matchLabels:
          app: helm-diff-reprotemplate:
          metadata:
          labels:
          app: helm-diff-reprospec:
          containers:
          - name: helm-diff-reproimage: nginx
          ---
          kind: JobapiVersion: batch/v1metadata:
          name: helm-diff-repro-hookannotations:
          "helm.sh/hook": pre-install,pre-upgradespec:
          template:
          spec:
          containers:
          - name: helm-diff-repro-hookimage: nginxcommand: ["/bin/sh", "-c", "echo 'Hello, World!'"]restartPolicy: Never

          Then simply install this chart to a kubernetes cluster.
          After that the Problem 1. will be reproducible.
          To reproduce Problem 2. make any changes in the helm hook manifest - I modified the helm hook job command

          Tested versions

          helm version
          version.BuildInfo{Version:"v3.17.3", GitCommit:"e4da49785aa6e6ee2b86efd5dd9e43400318262b", GitTreeState:"clean", GoVersion:"go1.24.2"}
          
          helm diff version
          3.11.0
          

          Activity

          Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

          Metadata

          Metadata

          Assignees

          No one assigned

            Labels

            Projects

            No projects

              Milestone

              No milestone

              Relationships

              None yet

              Development

              No branches or pull requests

              Issue actions

              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
              Skip to content

              Diff failing when diffing helm hook jobs with --take-ownership flag #782

              Description

              @blaskoa

              Problem description

              When a helm chart contains helm hooks defined as Jobs, then helm diff is not behaving as expected when used with --take-ownership flag:

              1. When no changes are made on the chart, the chart is marked as having changes anyway

              Correct behavior without --take-ownership flag (empty)

              helm diff upgrade helm-diff-repro . --install --debug
              Executing helm version
              Executing helm get manifest helm-diff-repro --namespace default
              Executing helm get values helm-diff-repro --output yaml --all
              Executing helm version
              Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values2739512397 --validate --is-upgrade --dry-run=client
              Executing helm get hooks helm-diff-repro --namespace default
              

              Incorrect behavior with --take-ownership flag

              helm diff upgrade helm-diff-repro . --install --take-ownership --debug
              Executing helm version
              Executing helm get manifest helm-diff-repro --namespace default
              Executing helm get values helm-diff-repro --output yaml --all
              Executing helm version
              Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values3415737044 --take-ownership --validate --is-upgrade --dry-run=client
              default, helm-diff-repro-hook, Job (batch) changed ownership:
              - + default/helm-diff-repro
              

              We are using helmfile, and the problem causes creation of new release for all helm charts which contain any helm hooks, even when they did not change

              2. When the helm hook contains any changes, then helm diff command fails

              Correct behavior without --take-ownership flag

              helm diff upgrade helm-diff-repro . --install --debug
              Executing helm version
              Executing helm get manifest helm-diff-repro --namespace default
              Executing helm get values helm-diff-repro --output yaml --all
              Executing helm version
              Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values3415473100 --validate --is-upgrade --dry-run=client
              Executing helm get hooks helm-diff-repro --namespace default
              default, helm-diff-repro-hook, Job (batch) has changed:
              # Source: test-chart/templates/deployment.yaml
              kind: Job
              apiVersion: batch/v1
              metadata:
              name: helm-diff-repro-hook
              annotations:
              "helm.sh/hook": pre-install,pre-upgrade
              spec:
              template:
              spec:
              containers:
              - name: helm-diff-repro-hook
              image: nginx
              - command: ["/bin/sh", "-c", "echo 'Hello, World!'"]
              + command: ["/bin/sh", "-c", "echo 'Hello, World! 1'"]
              restartPolicy: Never
              

              Incorrect behavior (crash) with --take-ownership flag

              helm diff upgrade helm-diff-repro . --install --take-ownership --debug
              Executing helm version
              Executing helm get manifest helm-diff-repro --namespace default
              Executing helm get values helm-diff-repro --output yaml --all
              Executing helm version
              Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values854516463 --take-ownership --validate --is-upgrade --dry-run=client
              Error: unable to generate manifests: cannot patch "helm-diff-repro-hook" with kind Job: Job.batch "helm-diff-repro-hook" is invalid: spec.template: Invalid value: core.PodTemplateSpec{ObjectMeta:v1.ObjectMeta{Name:"", GenerateName:"", Namespace:"", SelfLink:"", UID:"", ResourceVersion:"", Generation:0, CreationTimestamp:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), DeletionTimestamp:<nil>, DeletionGracePeriodSeconds:(*int64)(nil), Labels:map[string]string{"batch.kubernetes.io/controller-uid":"eb0265ed-b7eb-4e12-b09f-7896f25fbec4", "batch.kubernetes.io/job-name":"helm-diff-repro-hook", "controller-uid":"eb0265ed-b7eb-4e12-b09f-7896f25fbec4", "job-name":"helm-diff-repro-hook"}, Annotations:map[string]string(nil), OwnerReferences:[]v1.OwnerReference(nil), Finalizers:[]string(nil), ManagedFields:[]v1.ManagedFieldsEntry(nil)}, Spec:core.PodSpec{Volumes:[]core.Volume(nil), InitContainers:[]core.Container(nil), Containers:[]core.Container{core.Container{Name:"helm-diff-repro-hook", Image:"nginx", Command:[]string{"/bin/sh", "-c", "echo 'Hello, World! 1'"}, Args:[]string(nil), WorkingDir:"", Ports:[]core.ContainerPort(nil), EnvFrom:[]core.EnvFromSource(nil), Env:[]core.EnvVar(nil), Resources:core.ResourceRequirements{Limits:core.ResourceList(nil), Requests:core.ResourceList(nil), Claims:[]core.ResourceClaim(nil)}, ResizePolicy:[]core.ContainerResizePolicy(nil), RestartPolicy:(*core.ContainerRestartPolicy)(nil), VolumeMounts:[]core.VolumeMount(nil), VolumeDevices:[]core.VolumeDevice(nil), LivenessProbe:(*core.Probe)(nil), ReadinessProbe:(*core.Probe)(nil), StartupProbe:(*core.Probe)(nil), Lifecycle:(*core.Lifecycle)(nil), TerminationMessagePath:"/dev/termination-log", TerminationMessagePolicy:"File", ImagePullPolicy:"Always", SecurityContext:(*core.SecurityContext)(nil), Stdin:false, StdinOnce:false, TTY:false}}, EphemeralContainers:[]core.EphemeralContainer(nil), RestartPolicy:"Never", TerminationGracePeriodSeconds:(*int64)(0x400ccecc50), ActiveDeadlineSeconds:(*int64)(nil), DNSPolicy:"ClusterFirst", NodeSelector:map[string]string(nil), ServiceAccountName:"", AutomountServiceAccountToken:(*bool)(nil), NodeName:"", SecurityContext:(*core.PodSecurityContext)(0x4014d12cf0), ImagePullSecrets:[]core.LocalObjectReference(nil), Hostname:"", Subdomain:"", SetHostnameAsFQDN:(*bool)(nil), Affinity:(*core.Affinity)(nil), SchedulerName:"default-scheduler", Tolerations:[]core.Toleration(nil), HostAliases:[]core.HostAlias(nil), PriorityClassName:"", Priority:(*int32)(nil), PreemptionPolicy:(*core.PreemptionPolicy)(nil), DNSConfig:(*core.PodDNSConfig)(nil), ReadinessGates:[]core.PodReadinessGate(nil), RuntimeClassName:(*string)(nil), Overhead:core.ResourceList(nil), EnableServiceLinks:(*bool)(nil), TopologySpreadConstraints:[]core.TopologySpreadConstraint(nil), OS:(*core.PodOS)(nil), SchedulingGates:[]core.PodSchedulingGate(nil), ResourceClaims:[]core.PodResourceClaim(nil)}}: field is immutable
              Error: plugin "diff" exited with error
              helm.go:86: 2025-05-19 11:25:53.904139 +0200 CEST m=+0.367769876 [debug] plugin "diff" exited with error
              

              Workaround

              Currently we implemented a workaround by using --no-hooks flag. Using this flag has its own implications, but it works for our use case

              Repro steps

              To reproduce the issue I used a simple helm chart containing only one yaml file:

              apiVersion: apps/v1kind: Deploymentmetadata:
              name: helm-diff-reprospec:
              selector:
              matchLabels:
              app: helm-diff-reprotemplate:
              metadata:
              labels:
              app: helm-diff-reprospec:
              containers:
              - name: helm-diff-reproimage: nginx
              ---
              kind: JobapiVersion: batch/v1metadata:
              name: helm-diff-repro-hookannotations:
              "helm.sh/hook": pre-install,pre-upgradespec:
              template:
              spec:
              containers:
              - name: helm-diff-repro-hookimage: nginxcommand: ["/bin/sh", "-c", "echo 'Hello, World!'"]restartPolicy: Never

              Then simply install this chart to a kubernetes cluster.
              After that the Problem 1. will be reproducible.
              To reproduce Problem 2. make any changes in the helm hook manifest - I modified the helm hook job command

              Tested versions

              helm version
              version.BuildInfo{Version:"v3.17.3", GitCommit:"e4da49785aa6e6ee2b86efd5dd9e43400318262b", GitTreeState:"clean", GoVersion:"go1.24.2"}
              
              helm diff version
              3.11.0
              

              Activity

              Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

              Metadata

              Metadata

              Assignees

              No one assigned

                Labels

                Projects

                No projects

                  Milestone

                  No milestone

                  Relationships

                  None yet

                  Development

                  No branches or pull requests

                  Issue actions

                  , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
                  Skip to content

                  Diff failing when diffing helm hook jobs with --take-ownership flag #782

                  Description

                  @blaskoa

                  Problem description

                  When a helm chart contains helm hooks defined as Jobs, then helm diff is not behaving as expected when used with --take-ownership flag:

                  1. When no changes are made on the chart, the chart is marked as having changes anyway

                  Correct behavior without --take-ownership flag (empty)

                  helm diff upgrade helm-diff-repro . --install --debug
                  Executing helm version
                  Executing helm get manifest helm-diff-repro --namespace default
                  Executing helm get values helm-diff-repro --output yaml --all
                  Executing helm version
                  Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values2739512397 --validate --is-upgrade --dry-run=client
                  Executing helm get hooks helm-diff-repro --namespace default
                  

                  Incorrect behavior with --take-ownership flag

                  helm diff upgrade helm-diff-repro . --install --take-ownership --debug
                  Executing helm version
                  Executing helm get manifest helm-diff-repro --namespace default
                  Executing helm get values helm-diff-repro --output yaml --all
                  Executing helm version
                  Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values3415737044 --take-ownership --validate --is-upgrade --dry-run=client
                  default, helm-diff-repro-hook, Job (batch) changed ownership:
                  - + default/helm-diff-repro
                  

                  We are using helmfile, and the problem causes creation of new release for all helm charts which contain any helm hooks, even when they did not change

                  2. When the helm hook contains any changes, then helm diff command fails

                  Correct behavior without --take-ownership flag

                  helm diff upgrade helm-diff-repro . --install --debug
                  Executing helm version
                  Executing helm get manifest helm-diff-repro --namespace default
                  Executing helm get values helm-diff-repro --output yaml --all
                  Executing helm version
                  Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values3415473100 --validate --is-upgrade --dry-run=client
                  Executing helm get hooks helm-diff-repro --namespace default
                  default, helm-diff-repro-hook, Job (batch) has changed:
                  # Source: test-chart/templates/deployment.yaml
                  kind: Job
                  apiVersion: batch/v1
                  metadata:
                  name: helm-diff-repro-hook
                  annotations:
                  "helm.sh/hook": pre-install,pre-upgrade
                  spec:
                  template:
                  spec:
                  containers:
                  - name: helm-diff-repro-hook
                  image: nginx
                  - command: ["/bin/sh", "-c", "echo 'Hello, World!'"]
                  + command: ["/bin/sh", "-c", "echo 'Hello, World! 1'"]
                  restartPolicy: Never
                  

                  Incorrect behavior (crash) with --take-ownership flag

                  helm diff upgrade helm-diff-repro . --install --take-ownership --debug
                  Executing helm version
                  Executing helm get manifest helm-diff-repro --namespace default
                  Executing helm get values helm-diff-repro --output yaml --all
                  Executing helm version
                  Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values854516463 --take-ownership --validate --is-upgrade --dry-run=client
                  Error: unable to generate manifests: cannot patch "helm-diff-repro-hook" with kind Job: Job.batch "helm-diff-repro-hook" is invalid: spec.template: Invalid value: core.PodTemplateSpec{ObjectMeta:v1.ObjectMeta{Name:"", GenerateName:"", Namespace:"", SelfLink:"", UID:"", ResourceVersion:"", Generation:0, CreationTimestamp:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), DeletionTimestamp:<nil>, DeletionGracePeriodSeconds:(*int64)(nil), Labels:map[string]string{"batch.kubernetes.io/controller-uid":"eb0265ed-b7eb-4e12-b09f-7896f25fbec4", "batch.kubernetes.io/job-name":"helm-diff-repro-hook", "controller-uid":"eb0265ed-b7eb-4e12-b09f-7896f25fbec4", "job-name":"helm-diff-repro-hook"}, Annotations:map[string]string(nil), OwnerReferences:[]v1.OwnerReference(nil), Finalizers:[]string(nil), ManagedFields:[]v1.ManagedFieldsEntry(nil)}, Spec:core.PodSpec{Volumes:[]core.Volume(nil), InitContainers:[]core.Container(nil), Containers:[]core.Container{core.Container{Name:"helm-diff-repro-hook", Image:"nginx", Command:[]string{"/bin/sh", "-c", "echo 'Hello, World! 1'"}, Args:[]string(nil), WorkingDir:"", Ports:[]core.ContainerPort(nil), EnvFrom:[]core.EnvFromSource(nil), Env:[]core.EnvVar(nil), Resources:core.ResourceRequirements{Limits:core.ResourceList(nil), Requests:core.ResourceList(nil), Claims:[]core.ResourceClaim(nil)}, ResizePolicy:[]core.ContainerResizePolicy(nil), RestartPolicy:(*core.ContainerRestartPolicy)(nil), VolumeMounts:[]core.VolumeMount(nil), VolumeDevices:[]core.VolumeDevice(nil), LivenessProbe:(*core.Probe)(nil), ReadinessProbe:(*core.Probe)(nil), StartupProbe:(*core.Probe)(nil), Lifecycle:(*core.Lifecycle)(nil), TerminationMessagePath:"/dev/termination-log", TerminationMessagePolicy:"File", ImagePullPolicy:"Always", SecurityContext:(*core.SecurityContext)(nil), Stdin:false, StdinOnce:false, TTY:false}}, EphemeralContainers:[]core.EphemeralContainer(nil), RestartPolicy:"Never", TerminationGracePeriodSeconds:(*int64)(0x400ccecc50), ActiveDeadlineSeconds:(*int64)(nil), DNSPolicy:"ClusterFirst", NodeSelector:map[string]string(nil), ServiceAccountName:"", AutomountServiceAccountToken:(*bool)(nil), NodeName:"", SecurityContext:(*core.PodSecurityContext)(0x4014d12cf0), ImagePullSecrets:[]core.LocalObjectReference(nil), Hostname:"", Subdomain:"", SetHostnameAsFQDN:(*bool)(nil), Affinity:(*core.Affinity)(nil), SchedulerName:"default-scheduler", Tolerations:[]core.Toleration(nil), HostAliases:[]core.HostAlias(nil), PriorityClassName:"", Priority:(*int32)(nil), PreemptionPolicy:(*core.PreemptionPolicy)(nil), DNSConfig:(*core.PodDNSConfig)(nil), ReadinessGates:[]core.PodReadinessGate(nil), RuntimeClassName:(*string)(nil), Overhead:core.ResourceList(nil), EnableServiceLinks:(*bool)(nil), TopologySpreadConstraints:[]core.TopologySpreadConstraint(nil), OS:(*core.PodOS)(nil), SchedulingGates:[]core.PodSchedulingGate(nil), ResourceClaims:[]core.PodResourceClaim(nil)}}: field is immutable
                  Error: plugin "diff" exited with error
                  helm.go:86: 2025-05-19 11:25:53.904139 +0200 CEST m=+0.367769876 [debug] plugin "diff" exited with error
                  

                  Workaround

                  Currently we implemented a workaround by using --no-hooks flag. Using this flag has its own implications, but it works for our use case

                  Repro steps

                  To reproduce the issue I used a simple helm chart containing only one yaml file:

                  apiVersion: apps/v1kind: Deploymentmetadata:
                  name: helm-diff-reprospec:
                  selector:
                  matchLabels:
                  app: helm-diff-reprotemplate:
                  metadata:
                  labels:
                  app: helm-diff-reprospec:
                  containers:
                  - name: helm-diff-reproimage: nginx
                  ---
                  kind: JobapiVersion: batch/v1metadata:
                  name: helm-diff-repro-hookannotations:
                  "helm.sh/hook": pre-install,pre-upgradespec:
                  template:
                  spec:
                  containers:
                  - name: helm-diff-repro-hookimage: nginxcommand: ["/bin/sh", "-c", "echo 'Hello, World!'"]restartPolicy: Never

                  Then simply install this chart to a kubernetes cluster.
                  After that the Problem 1. will be reproducible.
                  To reproduce Problem 2. make any changes in the helm hook manifest - I modified the helm hook job command

                  Tested versions

                  helm version
                  version.BuildInfo{Version:"v3.17.3", GitCommit:"e4da49785aa6e6ee2b86efd5dd9e43400318262b", GitTreeState:"clean", GoVersion:"go1.24.2"}
                  
                  helm diff version
                  3.11.0
                  

                  Activity

                  Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                  Metadata

                  Metadata

                  Assignees

                  No one assigned

                    Labels

                    Projects

                    No projects

                      Milestone

                      No milestone

                      Relationships

                      None yet

                      Development

                      No branches or pull requests

                      Issue actions

                      , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                      Skip to content

                      Diff failing when diffing helm hook jobs with --take-ownership flag #782

                      Description

                      @blaskoa

                      Problem description

                      When a helm chart contains helm hooks defined as Jobs, then helm diff is not behaving as expected when used with --take-ownership flag:

                      1. When no changes are made on the chart, the chart is marked as having changes anyway

                      Correct behavior without --take-ownership flag (empty)

                      helm diff upgrade helm-diff-repro . --install --debug
                      Executing helm version
                      Executing helm get manifest helm-diff-repro --namespace default
                      Executing helm get values helm-diff-repro --output yaml --all
                      Executing helm version
                      Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values2739512397 --validate --is-upgrade --dry-run=client
                      Executing helm get hooks helm-diff-repro --namespace default
                      

                      Incorrect behavior with --take-ownership flag

                      helm diff upgrade helm-diff-repro . --install --take-ownership --debug
                      Executing helm version
                      Executing helm get manifest helm-diff-repro --namespace default
                      Executing helm get values helm-diff-repro --output yaml --all
                      Executing helm version
                      Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values3415737044 --take-ownership --validate --is-upgrade --dry-run=client
                      default, helm-diff-repro-hook, Job (batch) changed ownership:
                      - + default/helm-diff-repro
                      

                      We are using helmfile, and the problem causes creation of new release for all helm charts which contain any helm hooks, even when they did not change

                      2. When the helm hook contains any changes, then helm diff command fails

                      Correct behavior without --take-ownership flag

                      helm diff upgrade helm-diff-repro . --install --debug
                      Executing helm version
                      Executing helm get manifest helm-diff-repro --namespace default
                      Executing helm get values helm-diff-repro --output yaml --all
                      Executing helm version
                      Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values3415473100 --validate --is-upgrade --dry-run=client
                      Executing helm get hooks helm-diff-repro --namespace default
                      default, helm-diff-repro-hook, Job (batch) has changed:
                      # Source: test-chart/templates/deployment.yaml
                      kind: Job
                      apiVersion: batch/v1
                      metadata:
                      name: helm-diff-repro-hook
                      annotations:
                      "helm.sh/hook": pre-install,pre-upgrade
                      spec:
                      template:
                      spec:
                      containers:
                      - name: helm-diff-repro-hook
                      image: nginx
                      - command: ["/bin/sh", "-c", "echo 'Hello, World!'"]
                      + command: ["/bin/sh", "-c", "echo 'Hello, World! 1'"]
                      restartPolicy: Never
                      

                      Incorrect behavior (crash) with --take-ownership flag

                      helm diff upgrade helm-diff-repro . --install --take-ownership --debug
                      Executing helm version
                      Executing helm get manifest helm-diff-repro --namespace default
                      Executing helm get values helm-diff-repro --output yaml --all
                      Executing helm version
                      Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values854516463 --take-ownership --validate --is-upgrade --dry-run=client
                      Error: unable to generate manifests: cannot patch "helm-diff-repro-hook" with kind Job: Job.batch "helm-diff-repro-hook" is invalid: spec.template: Invalid value: core.PodTemplateSpec{ObjectMeta:v1.ObjectMeta{Name:"", GenerateName:"", Namespace:"", SelfLink:"", UID:"", ResourceVersion:"", Generation:0, CreationTimestamp:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), DeletionTimestamp:<nil>, DeletionGracePeriodSeconds:(*int64)(nil), Labels:map[string]string{"batch.kubernetes.io/controller-uid":"eb0265ed-b7eb-4e12-b09f-7896f25fbec4", "batch.kubernetes.io/job-name":"helm-diff-repro-hook", "controller-uid":"eb0265ed-b7eb-4e12-b09f-7896f25fbec4", "job-name":"helm-diff-repro-hook"}, Annotations:map[string]string(nil), OwnerReferences:[]v1.OwnerReference(nil), Finalizers:[]string(nil), ManagedFields:[]v1.ManagedFieldsEntry(nil)}, Spec:core.PodSpec{Volumes:[]core.Volume(nil), InitContainers:[]core.Container(nil), Containers:[]core.Container{core.Container{Name:"helm-diff-repro-hook", Image:"nginx", Command:[]string{"/bin/sh", "-c", "echo 'Hello, World! 1'"}, Args:[]string(nil), WorkingDir:"", Ports:[]core.ContainerPort(nil), EnvFrom:[]core.EnvFromSource(nil), Env:[]core.EnvVar(nil), Resources:core.ResourceRequirements{Limits:core.ResourceList(nil), Requests:core.ResourceList(nil), Claims:[]core.ResourceClaim(nil)}, ResizePolicy:[]core.ContainerResizePolicy(nil), RestartPolicy:(*core.ContainerRestartPolicy)(nil), VolumeMounts:[]core.VolumeMount(nil), VolumeDevices:[]core.VolumeDevice(nil), LivenessProbe:(*core.Probe)(nil), ReadinessProbe:(*core.Probe)(nil), StartupProbe:(*core.Probe)(nil), Lifecycle:(*core.Lifecycle)(nil), TerminationMessagePath:"/dev/termination-log", TerminationMessagePolicy:"File", ImagePullPolicy:"Always", SecurityContext:(*core.SecurityContext)(nil), Stdin:false, StdinOnce:false, TTY:false}}, EphemeralContainers:[]core.EphemeralContainer(nil), RestartPolicy:"Never", TerminationGracePeriodSeconds:(*int64)(0x400ccecc50), ActiveDeadlineSeconds:(*int64)(nil), DNSPolicy:"ClusterFirst", NodeSelector:map[string]string(nil), ServiceAccountName:"", AutomountServiceAccountToken:(*bool)(nil), NodeName:"", SecurityContext:(*core.PodSecurityContext)(0x4014d12cf0), ImagePullSecrets:[]core.LocalObjectReference(nil), Hostname:"", Subdomain:"", SetHostnameAsFQDN:(*bool)(nil), Affinity:(*core.Affinity)(nil), SchedulerName:"default-scheduler", Tolerations:[]core.Toleration(nil), HostAliases:[]core.HostAlias(nil), PriorityClassName:"", Priority:(*int32)(nil), PreemptionPolicy:(*core.PreemptionPolicy)(nil), DNSConfig:(*core.PodDNSConfig)(nil), ReadinessGates:[]core.PodReadinessGate(nil), RuntimeClassName:(*string)(nil), Overhead:core.ResourceList(nil), EnableServiceLinks:(*bool)(nil), TopologySpreadConstraints:[]core.TopologySpreadConstraint(nil), OS:(*core.PodOS)(nil), SchedulingGates:[]core.PodSchedulingGate(nil), ResourceClaims:[]core.PodResourceClaim(nil)}}: field is immutable
                      Error: plugin "diff" exited with error
                      helm.go:86: 2025-05-19 11:25:53.904139 +0200 CEST m=+0.367769876 [debug] plugin "diff" exited with error
                      

                      Workaround

                      Currently we implemented a workaround by using --no-hooks flag. Using this flag has its own implications, but it works for our use case

                      Repro steps

                      To reproduce the issue I used a simple helm chart containing only one yaml file:

                      apiVersion: apps/v1kind: Deploymentmetadata:
                      name: helm-diff-reprospec:
                      selector:
                      matchLabels:
                      app: helm-diff-reprotemplate:
                      metadata:
                      labels:
                      app: helm-diff-reprospec:
                      containers:
                      - name: helm-diff-reproimage: nginx
                      ---
                      kind: JobapiVersion: batch/v1metadata:
                      name: helm-diff-repro-hookannotations:
                      "helm.sh/hook": pre-install,pre-upgradespec:
                      template:
                      spec:
                      containers:
                      - name: helm-diff-repro-hookimage: nginxcommand: ["/bin/sh", "-c", "echo 'Hello, World!'"]restartPolicy: Never

                      Then simply install this chart to a kubernetes cluster.
                      After that the Problem 1. will be reproducible.
                      To reproduce Problem 2. make any changes in the helm hook manifest - I modified the helm hook job command

                      Tested versions

                      helm version
                      version.BuildInfo{Version:"v3.17.3", GitCommit:"e4da49785aa6e6ee2b86efd5dd9e43400318262b", GitTreeState:"clean", GoVersion:"go1.24.2"}
                      
                      helm diff version
                      3.11.0
                      

                      Activity

                      Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                      Metadata

                      Metadata

                      Assignees

                      No one assigned

                        Labels

                        Projects

                        No projects

                          Milestone

                          No milestone

                          Relationships

                          None yet

                          Development

                          No branches or pull requests

                          Issue actions

                          , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
                          Skip to content

                          Diff failing when diffing helm hook jobs with --take-ownership flag #782

                          Description

                          @blaskoa

                          Problem description

                          When a helm chart contains helm hooks defined as Jobs, then helm diff is not behaving as expected when used with --take-ownership flag:

                          1. When no changes are made on the chart, the chart is marked as having changes anyway

                          Correct behavior without --take-ownership flag (empty)

                          helm diff upgrade helm-diff-repro . --install --debug
                          Executing helm version
                          Executing helm get manifest helm-diff-repro --namespace default
                          Executing helm get values helm-diff-repro --output yaml --all
                          Executing helm version
                          Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values2739512397 --validate --is-upgrade --dry-run=client
                          Executing helm get hooks helm-diff-repro --namespace default
                          

                          Incorrect behavior with --take-ownership flag

                          helm diff upgrade helm-diff-repro . --install --take-ownership --debug
                          Executing helm version
                          Executing helm get manifest helm-diff-repro --namespace default
                          Executing helm get values helm-diff-repro --output yaml --all
                          Executing helm version
                          Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values3415737044 --take-ownership --validate --is-upgrade --dry-run=client
                          default, helm-diff-repro-hook, Job (batch) changed ownership:
                          - + default/helm-diff-repro
                          

                          We are using helmfile, and the problem causes creation of new release for all helm charts which contain any helm hooks, even when they did not change

                          2. When the helm hook contains any changes, then helm diff command fails

                          Correct behavior without --take-ownership flag

                          helm diff upgrade helm-diff-repro . --install --debug
                          Executing helm version
                          Executing helm get manifest helm-diff-repro --namespace default
                          Executing helm get values helm-diff-repro --output yaml --all
                          Executing helm version
                          Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values3415473100 --validate --is-upgrade --dry-run=client
                          Executing helm get hooks helm-diff-repro --namespace default
                          default, helm-diff-repro-hook, Job (batch) has changed:
                          # Source: test-chart/templates/deployment.yaml
                          kind: Job
                          apiVersion: batch/v1
                          metadata:
                          name: helm-diff-repro-hook
                          annotations:
                          "helm.sh/hook": pre-install,pre-upgrade
                          spec:
                          template:
                          spec:
                          containers:
                          - name: helm-diff-repro-hook
                          image: nginx
                          - command: ["/bin/sh", "-c", "echo 'Hello, World!'"]
                          + command: ["/bin/sh", "-c", "echo 'Hello, World! 1'"]
                          restartPolicy: Never
                          

                          Incorrect behavior (crash) with --take-ownership flag

                          helm diff upgrade helm-diff-repro . --install --take-ownership --debug
                          Executing helm version
                          Executing helm get manifest helm-diff-repro --namespace default
                          Executing helm get values helm-diff-repro --output yaml --all
                          Executing helm version
                          Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values854516463 --take-ownership --validate --is-upgrade --dry-run=client
                          Error: unable to generate manifests: cannot patch "helm-diff-repro-hook" with kind Job: Job.batch "helm-diff-repro-hook" is invalid: spec.template: Invalid value: core.PodTemplateSpec{ObjectMeta:v1.ObjectMeta{Name:"", GenerateName:"", Namespace:"", SelfLink:"", UID:"", ResourceVersion:"", Generation:0, CreationTimestamp:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), DeletionTimestamp:<nil>, DeletionGracePeriodSeconds:(*int64)(nil), Labels:map[string]string{"batch.kubernetes.io/controller-uid":"eb0265ed-b7eb-4e12-b09f-7896f25fbec4", "batch.kubernetes.io/job-name":"helm-diff-repro-hook", "controller-uid":"eb0265ed-b7eb-4e12-b09f-7896f25fbec4", "job-name":"helm-diff-repro-hook"}, Annotations:map[string]string(nil), OwnerReferences:[]v1.OwnerReference(nil), Finalizers:[]string(nil), ManagedFields:[]v1.ManagedFieldsEntry(nil)}, Spec:core.PodSpec{Volumes:[]core.Volume(nil), InitContainers:[]core.Container(nil), Containers:[]core.Container{core.Container{Name:"helm-diff-repro-hook", Image:"nginx", Command:[]string{"/bin/sh", "-c", "echo 'Hello, World! 1'"}, Args:[]string(nil), WorkingDir:"", Ports:[]core.ContainerPort(nil), EnvFrom:[]core.EnvFromSource(nil), Env:[]core.EnvVar(nil), Resources:core.ResourceRequirements{Limits:core.ResourceList(nil), Requests:core.ResourceList(nil), Claims:[]core.ResourceClaim(nil)}, ResizePolicy:[]core.ContainerResizePolicy(nil), RestartPolicy:(*core.ContainerRestartPolicy)(nil), VolumeMounts:[]core.VolumeMount(nil), VolumeDevices:[]core.VolumeDevice(nil), LivenessProbe:(*core.Probe)(nil), ReadinessProbe:(*core.Probe)(nil), StartupProbe:(*core.Probe)(nil), Lifecycle:(*core.Lifecycle)(nil), TerminationMessagePath:"/dev/termination-log", TerminationMessagePolicy:"File", ImagePullPolicy:"Always", SecurityContext:(*core.SecurityContext)(nil), Stdin:false, StdinOnce:false, TTY:false}}, EphemeralContainers:[]core.EphemeralContainer(nil), RestartPolicy:"Never", TerminationGracePeriodSeconds:(*int64)(0x400ccecc50), ActiveDeadlineSeconds:(*int64)(nil), DNSPolicy:"ClusterFirst", NodeSelector:map[string]string(nil), ServiceAccountName:"", AutomountServiceAccountToken:(*bool)(nil), NodeName:"", SecurityContext:(*core.PodSecurityContext)(0x4014d12cf0), ImagePullSecrets:[]core.LocalObjectReference(nil), Hostname:"", Subdomain:"", SetHostnameAsFQDN:(*bool)(nil), Affinity:(*core.Affinity)(nil), SchedulerName:"default-scheduler", Tolerations:[]core.Toleration(nil), HostAliases:[]core.HostAlias(nil), PriorityClassName:"", Priority:(*int32)(nil), PreemptionPolicy:(*core.PreemptionPolicy)(nil), DNSConfig:(*core.PodDNSConfig)(nil), ReadinessGates:[]core.PodReadinessGate(nil), RuntimeClassName:(*string)(nil), Overhead:core.ResourceList(nil), EnableServiceLinks:(*bool)(nil), TopologySpreadConstraints:[]core.TopologySpreadConstraint(nil), OS:(*core.PodOS)(nil), SchedulingGates:[]core.PodSchedulingGate(nil), ResourceClaims:[]core.PodResourceClaim(nil)}}: field is immutable
                          Error: plugin "diff" exited with error
                          helm.go:86: 2025-05-19 11:25:53.904139 +0200 CEST m=+0.367769876 [debug] plugin "diff" exited with error
                          

                          Workaround

                          Currently we implemented a workaround by using --no-hooks flag. Using this flag has its own implications, but it works for our use case

                          Repro steps

                          To reproduce the issue I used a simple helm chart containing only one yaml file:

                          apiVersion: apps/v1kind: Deploymentmetadata:
                          name: helm-diff-reprospec:
                          selector:
                          matchLabels:
                          app: helm-diff-reprotemplate:
                          metadata:
                          labels:
                          app: helm-diff-reprospec:
                          containers:
                          - name: helm-diff-reproimage: nginx
                          ---
                          kind: JobapiVersion: batch/v1metadata:
                          name: helm-diff-repro-hookannotations:
                          "helm.sh/hook": pre-install,pre-upgradespec:
                          template:
                          spec:
                          containers:
                          - name: helm-diff-repro-hookimage: nginxcommand: ["/bin/sh", "-c", "echo 'Hello, World!'"]restartPolicy: Never

                          Then simply install this chart to a kubernetes cluster.
                          After that the Problem 1. will be reproducible.
                          To reproduce Problem 2. make any changes in the helm hook manifest - I modified the helm hook job command

                          Tested versions

                          helm version
                          version.BuildInfo{Version:"v3.17.3", GitCommit:"e4da49785aa6e6ee2b86efd5dd9e43400318262b", GitTreeState:"clean", GoVersion:"go1.24.2"}
                          
                          helm diff version
                          3.11.0
                          

                          Activity

                          Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                          Metadata

                          Metadata

                          Assignees

                          No one assigned

                            Labels

                            Projects

                            No projects

                              Milestone

                              No milestone

                              Relationships

                              None yet

                              Development

                              No branches or pull requests

                              Issue actions

                              , 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
                              Skip to content

                              Diff failing when diffing helm hook jobs with --take-ownership flag #782

                              Description

                              @blaskoa

                              Problem description

                              When a helm chart contains helm hooks defined as Jobs, then helm diff is not behaving as expected when used with --take-ownership flag:

                              1. When no changes are made on the chart, the chart is marked as having changes anyway

                              Correct behavior without --take-ownership flag (empty)

                              helm diff upgrade helm-diff-repro . --install --debug
                              Executing helm version
                              Executing helm get manifest helm-diff-repro --namespace default
                              Executing helm get values helm-diff-repro --output yaml --all
                              Executing helm version
                              Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values2739512397 --validate --is-upgrade --dry-run=client
                              Executing helm get hooks helm-diff-repro --namespace default
                              

                              Incorrect behavior with --take-ownership flag

                              helm diff upgrade helm-diff-repro . --install --take-ownership --debug
                              Executing helm version
                              Executing helm get manifest helm-diff-repro --namespace default
                              Executing helm get values helm-diff-repro --output yaml --all
                              Executing helm version
                              Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values3415737044 --take-ownership --validate --is-upgrade --dry-run=client
                              default, helm-diff-repro-hook, Job (batch) changed ownership:
                              - + default/helm-diff-repro
                              

                              We are using helmfile, and the problem causes creation of new release for all helm charts which contain any helm hooks, even when they did not change

                              2. When the helm hook contains any changes, then helm diff command fails

                              Correct behavior without --take-ownership flag

                              helm diff upgrade helm-diff-repro . --install --debug
                              Executing helm version
                              Executing helm get manifest helm-diff-repro --namespace default
                              Executing helm get values helm-diff-repro --output yaml --all
                              Executing helm version
                              Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values3415473100 --validate --is-upgrade --dry-run=client
                              Executing helm get hooks helm-diff-repro --namespace default
                              default, helm-diff-repro-hook, Job (batch) has changed:
                              # Source: test-chart/templates/deployment.yaml
                              kind: Job
                              apiVersion: batch/v1
                              metadata:
                              name: helm-diff-repro-hook
                              annotations:
                              "helm.sh/hook": pre-install,pre-upgrade
                              spec:
                              template:
                              spec:
                              containers:
                              - name: helm-diff-repro-hook
                              image: nginx
                              - command: ["/bin/sh", "-c", "echo 'Hello, World!'"]
                              + command: ["/bin/sh", "-c", "echo 'Hello, World! 1'"]
                              restartPolicy: Never
                              

                              Incorrect behavior (crash) with --take-ownership flag

                              helm diff upgrade helm-diff-repro . --install --take-ownership --debug
                              Executing helm version
                              Executing helm get manifest helm-diff-repro --namespace default
                              Executing helm get values helm-diff-repro --output yaml --all
                              Executing helm version
                              Executing helm template helm-diff-repro . --namespace default --values /var/folders/w2/1243kx491313m323bfpyn50w0000gn/T/existing-values854516463 --take-ownership --validate --is-upgrade --dry-run=client
                              Error: unable to generate manifests: cannot patch "helm-diff-repro-hook" with kind Job: Job.batch "helm-diff-repro-hook" is invalid: spec.template: Invalid value: core.PodTemplateSpec{ObjectMeta:v1.ObjectMeta{Name:"", GenerateName:"", Namespace:"", SelfLink:"", UID:"", ResourceVersion:"", Generation:0, CreationTimestamp:time.Date(1, time.January, 1, 0, 0, 0, 0, time.UTC), DeletionTimestamp:<nil>, DeletionGracePeriodSeconds:(*int64)(nil), Labels:map[string]string{"batch.kubernetes.io/controller-uid":"eb0265ed-b7eb-4e12-b09f-7896f25fbec4", "batch.kubernetes.io/job-name":"helm-diff-repro-hook", "controller-uid":"eb0265ed-b7eb-4e12-b09f-7896f25fbec4", "job-name":"helm-diff-repro-hook"}, Annotations:map[string]string(nil), OwnerReferences:[]v1.OwnerReference(nil), Finalizers:[]string(nil), ManagedFields:[]v1.ManagedFieldsEntry(nil)}, Spec:core.PodSpec{Volumes:[]core.Volume(nil), InitContainers:[]core.Container(nil), Containers:[]core.Container{core.Container{Name:"helm-diff-repro-hook", Image:"nginx", Command:[]string{"/bin/sh", "-c", "echo 'Hello, World! 1'"}, Args:[]string(nil), WorkingDir:"", Ports:[]core.ContainerPort(nil), EnvFrom:[]core.EnvFromSource(nil), Env:[]core.EnvVar(nil), Resources:core.ResourceRequirements{Limits:core.ResourceList(nil), Requests:core.ResourceList(nil), Claims:[]core.ResourceClaim(nil)}, ResizePolicy:[]core.ContainerResizePolicy(nil), RestartPolicy:(*core.ContainerRestartPolicy)(nil), VolumeMounts:[]core.VolumeMount(nil), VolumeDevices:[]core.VolumeDevice(nil), LivenessProbe:(*core.Probe)(nil), ReadinessProbe:(*core.Probe)(nil), StartupProbe:(*core.Probe)(nil), Lifecycle:(*core.Lifecycle)(nil), TerminationMessagePath:"/dev/termination-log", TerminationMessagePolicy:"File", ImagePullPolicy:"Always", SecurityContext:(*core.SecurityContext)(nil), Stdin:false, StdinOnce:false, TTY:false}}, EphemeralContainers:[]core.EphemeralContainer(nil), RestartPolicy:"Never", TerminationGracePeriodSeconds:(*int64)(0x400ccecc50), ActiveDeadlineSeconds:(*int64)(nil), DNSPolicy:"ClusterFirst", NodeSelector:map[string]string(nil), ServiceAccountName:"", AutomountServiceAccountToken:(*bool)(nil), NodeName:"", SecurityContext:(*core.PodSecurityContext)(0x4014d12cf0), ImagePullSecrets:[]core.LocalObjectReference(nil), Hostname:"", Subdomain:"", SetHostnameAsFQDN:(*bool)(nil), Affinity:(*core.Affinity)(nil), SchedulerName:"default-scheduler", Tolerations:[]core.Toleration(nil), HostAliases:[]core.HostAlias(nil), PriorityClassName:"", Priority:(*int32)(nil), PreemptionPolicy:(*core.PreemptionPolicy)(nil), DNSConfig:(*core.PodDNSConfig)(nil), ReadinessGates:[]core.PodReadinessGate(nil), RuntimeClassName:(*string)(nil), Overhead:core.ResourceList(nil), EnableServiceLinks:(*bool)(nil), TopologySpreadConstraints:[]core.TopologySpreadConstraint(nil), OS:(*core.PodOS)(nil), SchedulingGates:[]core.PodSchedulingGate(nil), ResourceClaims:[]core.PodResourceClaim(nil)}}: field is immutable
                              Error: plugin "diff" exited with error
                              helm.go:86: 2025-05-19 11:25:53.904139 +0200 CEST m=+0.367769876 [debug] plugin "diff" exited with error
                              

                              Workaround

                              Currently we implemented a workaround by using --no-hooks flag. Using this flag has its own implications, but it works for our use case

                              Repro steps

                              To reproduce the issue I used a simple helm chart containing only one yaml file:

                              apiVersion: apps/v1kind: Deploymentmetadata:
                              name: helm-diff-reprospec:
                              selector:
                              matchLabels:
                              app: helm-diff-reprotemplate:
                              metadata:
                              labels:
                              app: helm-diff-reprospec:
                              containers:
                              - name: helm-diff-reproimage: nginx
                              ---
                              kind: JobapiVersion: batch/v1metadata:
                              name: helm-diff-repro-hookannotations:
                              "helm.sh/hook": pre-install,pre-upgradespec:
                              template:
                              spec:
                              containers:
                              - name: helm-diff-repro-hookimage: nginxcommand: ["/bin/sh", "-c", "echo 'Hello, World!'"]restartPolicy: Never

                              Then simply install this chart to a kubernetes cluster.
                              After that the Problem 1. will be reproducible.
                              To reproduce Problem 2. make any changes in the helm hook manifest - I modified the helm hook job command

                              Tested versions

                              helm version
                              version.BuildInfo{Version:"v3.17.3", GitCommit:"e4da49785aa6e6ee2b86efd5dd9e43400318262b", GitTreeState:"clean", GoVersion:"go1.24.2"}
                              
                              helm diff version
                              3.11.0
                              

                              Activity

                              Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

                              Metadata

                              Metadata

                              Assignees

                              No one assigned

                                Labels

                                Projects

                                No projects

                                  Milestone

                                  No milestone

                                  Relationships

                                  None yet

                                  Development

                                  No branches or pull requests

                                  Issue actions