'helm - how to loop with complex values

I need to loop over objects with range in a Helm chart. I have error in network.yaml at line 4. How can I fix it?

My values.yaml contains:

network:
  service_shob_server:
  - name: shob-server-{{ .Release.Namespace }}-se
    app: shob-server-{{ .Release.Namespace }}
    port: 7725
    targetPort: 7725

  service_shob_pyland:
  - name: shob-pyland-{{ .Release.Namespace }}-se
    app: shob-server-{{ .Release.Namespace }}
    port: 4599 #hard coded
    targetPort: 4599 #hard coded

  service_shob_ui:
  - name: shob-ui-{{ .Release.Namespace }}-se
    app: shob-ui-{{ .Release.Namespace }}
    port: 7726
    targetPort: 7726

  service_ddsim_server:
  - name: ddsim-server-{{ .Release.Namespace }}-se
    app: shob-server-{{ .Release.Namespace }}
    port: 4849 #hard coded
    targetPort: 4849 #hard coded
   
  service_ddsim-client:
  - name: ddsim-client-{{ .Release.Namespace }}-se
    app: shob-ui-{{ .Release.Namespace }}
    port: 4850 #hard coded
    targetPort: 4850 

  service_web_streamer:
  - name: web-streamer-{{ .Release.Namespace }}-se
    app: shob-ui-{{ .Release.Namespace }}
    port: 2000
    targetPort: 2000

templates/service.yaml contains:

{{- $namespace := .Release.Namespace -}}
apiVersion: v1
kind: Service
{{- range $key, $value := .Values.network }}
metadata:
  name:  {{ $key }}
  value: {{ $value}}
  namespace: {{ $namespace }}
 spec:
   selector:
     app: {{ $key }} #shob-server-{{ $namespace }}
     value: {{ $value }}
   ports:
   - port: {{ $key }}
     value: {{ $value }}
     targetPort: {{ $key }}
     value: {{ $value }}
{{- end }}
---


Solution 1:[1]

In general, if you want to use Helm to produce multiple Kubernetes objects from one template file, you need to put the range loop around the entire YAML object. I'd start with the --- YAML start-of-document marker.

{{- range $key, $value := .Values.network }}
---
apiVersion: v1
kind: Service
metadata:
  name: {{ $key }}
  value: {{ $value }}
  {{-/* Helm will use the `helm install --namespace` by default
        and you don't need to explicitly declare namespace: here */}}
spec: { ... }
{{- end }}

Your value structure is a little more complicated than it needs to be, and this is probably causing problems with the looping. In your existing structure, .Values.network is a mapping or dictionary; each of the values is a list containing a single item. I'm guessing you don't need the keys and just flattening this into a flat list would simplify things.

# modified values.yaml
network:
  - name: shob-server-{{ .Release.Namespace }}-se
    app: shob-server-{{ .Release.Namespace }}
    port: 7725
    targetPort: 7725

  - name: shob-pyland-{{ .Release.Namespace }}-se
    app: shob-server-{{ .Release.Namespace }}
    port: 4599
    targetPort: 4599

Now when you iterate over this, the special variable . will get set to each item in the list, and you can refer to fields in this .. It syntactically is the same as .Values but now . is not the top-level Helm object.

{{- range .Values.network }}
---
apiVersion: v1
kind: Service
metadata:
  name: {{ .name }}
{{- end }}

There's one more piece to this puzzle. If you run helm template over this, you'll see literal {{ .Release.Namespace }} appearing in the output. You need to use the Helm tpl function to cause Helm to evaluate the template strings from the values object. This needs the top-level Helm object, but range redefines the . special variable to be something else. I tend to save the original value of . in a variable; using $ should work as well.

{{- $top := . -}}
{{- range .Values.network }}
name: {{ tpl .name $top }}
{{- end }}

Putting this all together, you should get something like:

{{- $top := . }}
{{- range .Values.network }}
apiVersion: v1
kind: Service
metadata:
  name: {{ tpl .name $top }}
spec:
  selector:
    app: {{ tpl .app $top }}
  ports:
  - port: {{ .port }}
    targetPort: {{ .targetPort }}
{{- end }}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1