kuberneteskubernetes-helmgo-templatessprig-template-functions

Creating a filtered list using helm template helpers


I'm trying to use a helm template helper to filter out values from a list in my values.yaml file, based on the value of one of the keys in each list member.

My chart is currently comprised of these files -
values.yaml -

namespaces:
- name: filter
  profiles:
  - nonProduction
- name: dont-filter
  profiles:
  - production  
clusterProfile: production

templates/namespaces.yaml

apiVersion: v1
kind: List
items:
{{ $filteredList := include "filteredNamespaces" . }}
{{ range $filteredList }}
  {{ .name }}
{{- end -}}

templates/_profile-match.tpl

{{/* vim: set filetype=mustache: */}}
{{- define "filteredNamespaces" -}}
  {{ $newList := list }}
  {{- range .Values.namespaces }}
    {{- if has $.Values.clusterProfile .profiles -}}
      {{ $newList := append $newList . }}
    {{- end -}}
  {{ end -}}
  {{ $newList }}
{{- end -}}

The problem is that within my helper file, the $newList variable is only populated within the scope of the range loop, and I end up with an empty list returning to the namespaces.yaml template.
Is there any way around this issue? Am I taking the wrong approach for solving this?


Solution

  • For all that Go templates are almost general-purpose functions, they have a couple of limitations. One of those limitations is that they only ever return a string; you can't write basic functional helpers like map or filter because you can't return the resulting list.

    The more straightforward approach to doing filtering as you've shown is to move it up to the point of the caller (and possibly repeat the condition if it's needed in multiple places):

    items:
    {{- range .Values.namespaces }}
    {{- if has $.Values.clusterProfile .profiles }}
      - {{ .name }}
    {{- end }}
    {{- end }}
    

    A hacky way to make this work as you have it is to marshal the list to some other string-based format, like JSON:

    {{- define "filteredNamespaces" -}}
    ...
    {{ toJson $newList }}
    {{- end -}}
    
    {{- range include "filteredNamespaces" . | fromJson -}}...{{- end -}}
    

    Also remember that you can inject Helm values files using the helm install -f option. So rather than listing every permutation of option and then filtering out the ones you don't want, you could restructure this so that namespaces: only contains the list of namespaces you actually want to use, but then you use a different values file for each profile.