How to include nested value in helm template

8/13/2018

I have this kind of template file in helm:

apiVersion: networking.istio.io/v1alpha3

kind: VirtualService
metadata:
  name: {{.Values.app.name}}-global-route
  namespace: {{.Release.Namespace}}
spec:
  hosts:
  - "{{.Values.app.name}}-global.{{.Release.Namespace}}.svc.cluster.local"
  gateways:
  - {{.Values.app.name}}-gateway
  - mesh
  http:
  # 1st priority, to route specific end-user to canary service
  - route:
    - destination:
        host: "{{.Values.app.name}}-global.{{.Release.Namespace}}.svc.cluster.local"
        subset: canary
    match: {{.Values.infra.trafficRoute.canaryCondition}}

And I want to expose the values.yaml like below:

# default values supplied for templates/* files
app:
  name: java-maven-app

infra:
  trafficRoute:
    canaryCondition:
    - headers:
        end-user:
          exact: apratama
        key:
          exact: agung

So, basically what I want to achieve is to let end-user (the one who use my helm chart) customize the canary condition. The condition itself is depends on istio's match data structure (which can be nested and complex values). I tried above with helm upgrade --install command, but somehow I got this error:

Error: UPGRADE FAILED: YAML parse error on java-maven-app-infra/templates/global-service.yaml: error converting YAML to JSON: yaml: line 17: found unexpected ':'
make: *** [deploy-infra] Error 1

However, when I comment out this line:

match: {{.Values.infra.trafficRoute.canaryCondition}}

it works without error.

Any advice?

-- Agung Pratama
kubernetes-helm

2 Answers

8/13/2018

Solved. I chatted with some people in #helm-users slack channel (kubernetes.slack.com), and so the value being provided to the template is a string value. So one needs to convert it to yaml object and indent it approriately. I solved this by changing it slightly to this

  # 1st priority, to route specific end-user to canary service
  - route:
    - destination:
        host: "{{.Values.app.name}}-global.{{.Release.Namespace}}.svc.cluster.local"
        subset: canary
    match: 
{{ toYaml .Values.infra.trafficRoute.canaryCondition | indent 4 }}
-- Agung Pratama
Source: StackOverflow

8/13/2018

we discussed this solution earlier in k8s Slack. I noticed you posted your own answer, but I thought I might as well expand on it a bit in case someone else encounters the same problem.

The issue is that Helm chart templating does text templating instead of YAML templating. Therefore, the inserted YAML sub-tree (canaryCondition) is not automatically converted to YAML and elegantly placed under the match key, but instead it's converted to string and inserted directly where the template directive is. With simple values such as strings and integers, this works fine for most cases, but more complex values such as arrays and maps need to handled differently.

In order to insert YAML sub-trees in the template, you need to first convert the sub-tree to YAML using toYaml function, and then ensure that correct indentation level is used with the indent function.

{{ toYaml .Values.infra.trafficRoute.canaryCondition | indent 4 }}

See the NGINX template example for another example on how to insert YAML sub-trees in the template.

To get started in debugging Helm chart templating, you can use the helm template command to see the YAML that the Helm chart generates.

-- Jaakko Pallari
Source: StackOverflow