Search code examples
kubernetes-helmgo-templatessprig-template-functions

How to unpack a list of dictionaries in a Go template


This is a toy example

{{/*Merge a list of objects*/}}
{{- define "mymerge" }}
    {{- toYaml (merge .) }}
{{- end }}

{{- define "mymergewrapper" }}
    {{- $dict1 := (dict "data" (dict "dict1key" "dict1val"))}}
    {{- $dict2 := (dict "data" (dict "dict2key" "dict2val"))}}
    {{- $dict3 := (dict "data" (dict "dict3key" "dict3val"))}}
    {{- $dict4 := (dict "data" (dict "dict3key" "dict4val"))}}
    {{- $arbitrary_list_of_dicts_of_unknown_length_and_content := (list $dict1 $dict2 $dict3 $dict4) }}
    {{- include "mymerge" $arbitrary_list_of_dicts_of_unknown_length_and_content }}
{{- end }}

apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name | printf "%s-%s" .Chart.Name }}
data:
  myvalue: "Hello World"
  whatHappens:
  {{- include "mymergewrapper" . }}
 helm install myappchart2 myappchart2/ --debug --dry-run
Error: INSTALLATION FAILED: template: myappchart2/templates/deployment.yaml:22:6: executing "myappchart2/templates/deployment.yaml" at <include "mymergewrapper" .>: error calling include: template: myappchart2/templates/deployment.yaml:12:8: executing "mymergewrapper" at <include "mymerge" $arbitrary_list_of_dicts_of_unknown_length_and_content>: error calling include: template: myappchart2/templates/deployment.yaml:3:22: executing "mymerge" at <.>: wrong type for value; expected map[string]interface {}; got []interface {}
helm.go:84: [debug] template: myappchart2/templates/deployment.yaml:22:6: executing "myappchart2/templates/deployment.yaml" at <include "mymergewrapper" .>: error calling include: template: myappchart2/templates/deployment.yaml:12:8: executing "mymergewrapper" at <include "mymerge" $arbitrary_list_of_dicts_of_unknown_length_and_content>: error calling include: template: myappchart2/templates/deployment.yaml:3:22: executing "mymerge" at <.>: wrong type for value; expected map[string]interface {}; got []interface {}

I read "Merge two or more dictionaries into one..." a little too fast.

Is there a way to unpack my $objects list and pass the individual dicts to merge ?


Solution

  • The merge function has a variadic argument. Templates do not support passing a slice as a variadic argument.

    Use a loop to merge one by one:

    {{- define "mymerge" }}
        {{- $dest := (dict) }}
        {{- range .}}{{merge $dest .}}{{end}}
        {{- toYaml $dest }}
    {{- end }}