<p>Given this <code>deployment.yaml</code></p>
<pre><code>apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment
spec:
  revisionHistoryLimit: 5
  template:
    spec:
      containers:
      {{- include &quot;app.container&quot; (merge .Values.app $) | nindent 8 }}
      {{- include &quot;ports&quot; (merge .Values.app ) | nindent 8 }}
      {{- range $k, $v := .Values.extraContainers }}
      {{- $nameDict := dict &quot;name&quot; $k -}}
        {{- include &quot;app.container&quot; (mustMergeOverwrite $.Values.app $nameDict $v)  | nindent 8 }}
        {{- include &quot;ports&quot; (merge $nameDict $v ) | nindent 8 }}
      {{- end }}

</code></pre>
<p>This helpers file...</p>
<pre><code>{{/* vim: set filetype=helm: */}}

{{/*
app container base
*/}}
{{- define &quot;app.containerBase&quot; -}}
- name: {{ .name | default &quot;app&quot; }}
  image: {{ .image.name }}:{{ .image.tag }}
  {{- if .command }}
  command:
    {{- range .command }}
    - {{ . | quote }}
    {{- end }}
  {{- end }}
  {{- if .args }}
  args:
    {{- range .args }}
    - {{ . | quote }}
      {{- end }}
  {{- end }}
    {{- range .envVars }}
    - name: {{ .name }}
      {{- with .value }}
      value: {{ . | quote }}
      {{- end }}
      {{- with .valueFrom }}
      valueFrom:
        {{- toYaml . | nindent 8 }}
      {{- end }}
    {{- end }}
  {{- if or .Values.configMaps .Values.secrets .Values.configMapRef }}
  envFrom:
    {{- if .Values.configMaps }}
    - configMapRef:
        name: &quot;{{- include &quot;app.fullname&quot; $ }}&quot;
    {{- end }}
    {{- range .Values.configMapRef }}
    - configMapRef:
        name: {{ . }}
    {{- end }}
    {{- range $name, $idk := $.Values.secrets }}
    - secretRef:
        name: &quot;{{- include &quot;app.fullname&quot; $ }}-{{ $name }}&quot;
    {{- end }}
  {{- end }}
{{- end -}}

{{/*
app container
*/}}
{{- define &quot;app.container&quot; -}}
{{- include &quot;app.containerBase&quot; . }}
  resources:
    limits:
      cpu: {{ .resources.limits.cpu | default &quot;100m&quot; | quote }}
      memory: {{ .resources.limits.memory | default &quot;128Mi&quot; | quote }}
      {{- if .resources.limits.ephemeralStorage }}
      ephemeral-storage: {{ .resources.limits.ephemeralStorage }}
      {{- end }}
    requests:
      cpu: {{ .resources.requests.cpu | default &quot;100m&quot; | quote }}
      memory: {{ .resources.requests.memory | default &quot;128Mi&quot; | quote }}
      {{- if .resources.requests.ephemeralStorage }}
      ephemeral-storage: {{ .resources.requests.ephemeralStorage }}
      {{- end }}
  securityContext:
    runAsNonRoot: true
    runAsUser: {{ .securityContext.runAsUser }}
    runAsGroup: {{ .securityContext.runAsGroup }}
    allowPrivilegeEscalation: false
{{- end -}}

{{/*
ports
*/}}
{{- define &quot;ports&quot; -}}
{{- if .port }}
  ports:
  - containerPort: {{ .port }}
  protocol: {{ .protocol | default &quot;tcp&quot; | upper }}
  {{- range .extraPorts}}
    - containerPort: {{ required &quot;.port is required.&quot; .port }}
    {{- if .name }}
      name: {{ .name }}
    {{- end }}
    {{- if .protocol }}
      protocol: {{ .protocol | upper }}
    {{- end }}
  {{- end }}
{{- end }}
{{- end -}}



</code></pre>
<p>This values.yaml</p>
<pre><code>extraContainers:
  extra-container2:
    image:
      name: xxx.dkr.ecr.eu-west-1.amazonaws.com/foobar-two
      tag: test
    command:
      - sleep
      - 10
    envVars:
      - name: FOO
        value: BAR
    probes:
      readinessProbe:
        path: /ContainerTwoReadinessPath
      livenessProbe:
        path: /ContainerTwoLivenessPath
    resources:
      limits:
        cpu: 200Mi
        memory: 2Gi
      requests:
        cpu: 200Mi
        memory: 2Gi
  extra-container3:
    image:
      name: xx.dkr.ecr.eu-west-1.amazonaws.com/foobar-three
      tag: latest
    command:
      - sleep
      - 10
    envVars:
      - name: FOO
        value: BAZ
    probes:
      readinessProbe:
        enabled: false
      livenessProbe:
        enabled: false

app:
  image:
    name: xx.dkr.ecr.eu-west-1.amazonaws.com/foobar
    tag: latest
  probes:
    readinessProbe:
      path: /_readiness
      enabled: true
    livenessProbe:
      path: /_liveness
      enabled: true
  port: 100
  resources:
    limits:
      cpu: 100Mi
      memory: 1Gi
    requests:
      cpu: 100Mi
      memory: 1Gi
</code></pre>
<p>Why does <code>helm template</code>  result in the below:</p>
<pre><code>---
# Source: corp-service/templates/deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: deployment
spec:
  revisionHistoryLimit: 5
  template:
    spec:
      containers:
        - name: app
          image: xx.dkr.ecr.eu-west-1.amazonaws.com/foobar:latest
          resources:
            limits:
              cpu: &quot;100Mi&quot;
              memory: &quot;1Gi&quot;
            requests:
              cpu: &quot;100Mi&quot;
              memory: &quot;1Gi&quot;
          securityContext:
            runAsNonRoot: true
            runAsUser: 2000
            runAsGroup: 2000
            allowPrivilegeEscalation: false
        
          ports:
          - containerPort: 100
          protocol: TCP
        - name: extra-container2
          image: xx.dkr.ecr.eu-west-1.amazonaws.com/foobar-two:test
          command:
            - &quot;sleep&quot;
            - &quot;10&quot;
            - name: FOO
              value: &quot;BAR&quot;
          resources:
            limits:
              cpu: &quot;200Mi&quot;
              memory: &quot;2Gi&quot;
            requests:
              cpu: &quot;200Mi&quot;
              memory: &quot;2Gi&quot;
          securityContext:
            runAsNonRoot: true
            runAsUser: 2000
            runAsGroup: 2000
            allowPrivilegeEscalation: false
        
        - name: extra-container3
          image: xx.dkr.ecr.eu-west-1.amazonaws.com/foobar-three:latest
          command:
            - &quot;sleep&quot;
            - &quot;10&quot;
            - name: FOO
              value: &quot;BAZ&quot;
          resources:
            limits:
              cpu: &quot;200Mi&quot;
              memory: &quot;2Gi&quot;
            requests:
              cpu: &quot;200Mi&quot;
              memory: &quot;2Gi&quot;
          securityContext:
            runAsNonRoot: true
            runAsUser: 2000
            runAsGroup: 2000
            allowPrivilegeEscalation: false
</code></pre>
<p>i.e why does <code>extra-container3</code> have <code>resources:</code> set with values from <code>extra-container2</code> instead of taking them from the default set of resources found under <code>.values.app</code>.</p>
<p>essentially, i want <code>extracontainers</code> to use default values from <code>.values.app</code> unless i explicitly set them inside the <code>extracontainers</code> section. however it seems that if a previous iteration of the loop over <code>extracontainers</code> defines some values, they are used in the next iteration if they are not overriden.</p>
<p>the resources for extra-container3 i am expecting are:</p>
<pre><code>resources:
    limits:
      cpu: 100Mi
      memory: 1Gi
    requests:
      cpu: 100Mi
      memory: 1Gi
</code></pre>
<p>what am i doing wrong?</p>

gurpsone
<p>So there are a couple of things going on here, but mostly the answer is that <code>(mergeMustOverwrite)</code> <strong>mutates</strong> the <code>$dest</code> map, which causes your range to &quot;remember&quot; the last value it saw, which according to your question isn't the behavior you want. The simplest answer is to use <code>(deepCopy $.Values.app)</code> as the <code>$dest</code>, but there's an asterisk to that due to another bug we'll cover in a second</p>
<pre><code>       {{- $nameDict := dict &quot;name&quot; $k -}}
-        {{- include &quot;app.container&quot; (mustMergeOverwrite $.Values.app $nameDict $v)  | nindent 8 }}
+        {{- include &quot;app.container&quot; (mustMergeOverwrite (deepCopy $.Values.app) $nameDict $v)  | nindent 8 }}
         {{- include &quot;ports&quot; (merge $nameDict $v ) | nindent 8 }}
       {{- end }}
</code></pre>
<p>You see, <code>(deepCopy $.Values.app)</code> stack-overflows <code>helm</code> because of your inexplicable use of <code>(merge .Values.app $)</code> drags in <code>Chart</code>, <code>Files</code>, <code>Capabilities</code> and the whole world. I'm guessing your <code>_helpers.tpl</code> must have been copy-pasted from somewhere else, which explains the erroneous <em>relative</em> <code>.Values</code> reference inside that <code>define</code>. One way to fix that is to remove the <code>.Values.configMaps</code>, so it will track the <code>.app</code> context like I expect you meant, or you can change the first <code>(merge)</code> to artificially create a <code>&quot;Values&quot;: {}</code> item just to keep the template from blowing up when in tries to reference <code>.app.Values.configMaps</code>. The correct one will depend on what you were intending, but <code>(merge .Values.app $)</code> is almost certainly not it</p>
<p>so, either:</p>
<pre><code>--- a/templates/_helpers.tpl
+++ b/templates/_helpers.tpl
@@ -28,13 +28,13 @@ app container base
         {{- toYaml . | nindent 8 }}
       {{- end }}
     {{- end }}
-  {{- if or .Values.configMaps .Values.secrets .Values.configMapRef }}
+  {{- if or .configMaps .secrets .configMapRef }}
   envFrom:
-    {{- if .Values.configMaps }}
+    {{- if .configMaps }}
     - configMapRef:
         name: &quot;{{- include &quot;app.fullname&quot; $ }}&quot;
     {{- end }}
-    {{- range .Values.configMapRef }}
+    {{- range .configMapRef }}
     - configMapRef:
         name: {{ . }}
     {{- end }}
</code></pre>
<p><em>or</em></p>
<pre><code>       containers:
-      {{- include &quot;app.container&quot; (merge .Values.app $) | nindent 8 }}
+      {{- include &quot;app.container&quot; (merge .Values.app (dict &quot;Values&quot; (dict))) | nindent 8 }}
       {{- include &quot;ports&quot; (merge .Values.app ) | nindent 8 }}
</code></pre>
