Search code examples
kuberneteskubernetes-helmistio

Helm with nested lists using range


I have the following content in values.yaml and virtualservice.yaml: I want to create template file using above value but getting error while helm rendenring template.

values.yaml:

istio:
  enabled: true
  virtualService:
    enabled: true
    virtualServices:
      "0": 
        name: hello-app
        gateways: 
           - gateway-new
        hosts:
          - prod.abc.com
        apps:
          name: primary
          path: "/api"
          routes:
            "0":
              weight: 100
              port: 8080
              name: hello-app
            "1":
              weight: 0
              port: 8080
              name: hello-app-canary 
        "1": 
            name: hello-app-internal
            gateways: 
              - mesh
            hosts:
              - hello-app.test.prod.svc.cluster.local
            apps:
              name: internal
              path: "/api"
              routes:
                "0":
                  weight: 100
                  port: 9081
                  name: hello-app
                "1":
                  weight: 0
                  port: 9081
                  name: hello-app-canary  

virtualservice.yaml:

{{- if ((.Values.istio).enabled) }}
{{- if ((.Values.istio.virtualService).enabled) }}
{{- range $key, $value := .Values.istio.virtualService.virtualServices }}
apiVersion: networking.istio.io/v1beta1
kind: VirtualService
metadata:
  name: {{ .name }}
  namespace: {{ $.Release.Namespace }}
  labels:
{{ include "common.labels" $ | indent 4 }}
spec:
  gateways:
  {{- range .gateways }}
  - {{.}}
  {{- end }}
  hosts:
  {{- range .hosts }}
  - {{.}}
  {{- end }}
  http:
  {{- range $app := $value.apps }}
  - match:
      - uri:
          prefix: "/{{ $app.path }}"
    name: {{ $app.name }}
    route:
    {{- range $route := $app.routes }}
    - destination:
          host: {{ $route.name }}
          port:
            number: {{ $route.port  }}
      weight: {{ $route.weight  }}
    {{- end }}
    {{- end }}
{{- end }}
{{- end }}
{{- end }}

How do I iterate if I have more than one list using range? With the above template, I am getting the error:

 at <$app.path>: can't evaluate field path in type interface {}

Any help is highly appreciated.


Solution

  • First of all, it looks like the virtual service "1" has one extra indent (right now it is under virtual service "0" section, but it should be under "virtualServices").

    Moreover, If you want to iterate over the apps of each virtual service, the apps field should be a list, but right now it is a dictionary. Therefore, when you iterate with range $app := $value.apps you are actually iterating over the fields of apps, which leads to the error you are getting.

    Here's the fixed version of values.yam:

    istio:
      enabled: true
      virtualService:
        enabled: true
        virtualServices:
          "0": 
            name: hello-app
            gateways: 
               - gateway-new
            hosts:
              - prod.abc.com
            apps:
            - name: primary
              path: "/api"
              routes:
                "0":
                  weight: 100
                  port: 8080
                  name: hello-app
                "1":
                  weight: 0
                  port: 8080
                  name: hello-app-canary 
          "1": 
              name: hello-app-internal
              gateways: 
                - mesh
              hosts:
                - hello-app.test.prod.svc.cluster.local
              apps:
              - name: internal
                path: "/api"
                routes:
                  "0":
                    weight: 100
                    port: 9081
                    name: hello-app
                  "1":
                    weight: 0
                    port: 9081
                    name: hello-app-canary