Search code examples
kuberneteskubernetes-helmgo-templates

Helm templating.. values match


Wondering whether it's possible to use a port if it exists in the values, else use http.. something like this;

svc:
  app:
    ports:
      - port: 8080
        name: http
      - port: 8090
        name: metrics
  app2:
    ports:
      - port: 8080
        name: http

Some service expose their metrics over http and some have metrics ports.. So I'd like to template it something like;

{{ define "app.service.ports" }}
{{ range (index .Values.svc (include "app.refName" .) "ports") }}
- name: {{ .name }}
{{ end }}
{{ end }}

This will pull each port name right, but I want to pull metrics if it exists, else pull http.. can someone point me in the right direction?


Solution

  • Neither the Go text/template language nor the Helm extensions have any way to do a generic lookup in a complex structure like this. Instead you need to manually iterate through the list and use a variable to remember what you've seen.

    {{- $ports := index .Values "svc" (include "app.refName" .) "ports" -}}
    
    {{-/* Set up variables to remember what we've seen.  Initialize them
          to an empty dictionary, which is false in a conditional. -*/}}
    {{- $httpPort := dict -}}
    {{- $metricsPort := dict -}}
    
    {{-/* Scan the list of ports. */-}}
    {{- range $port := range $ports -}}
      {{- if eq $port.name "metrics" -}}
        {{- $metricsPort = $port -}}
      {{- else if eq $port.name "http" -}}
        {{- $httpPort = $port -}}
      {{- end -}}
    {{- end -}}
    
    {{-/* Emit the metrics port if it exists, else the HTTP port. */-}}
    {{- coalesce $metricsPort $httpPort | toYaml -}}
    

    In a higher-level language you could imagine searching a list, and for each dictionary element, accepting it if it has a name key with value metrics. This usually involves passing a lambda or anonymous function to a find() function, and the text/template language has no support for that.