kubernetes-helm

How can a Helm function access a value without knowning the name?


I'm working on a Helm chart and trying to create a general-purpose templating function. The goal is to pass in any parameter (e.g., .Values.name) and have the function template it without knowing the specifics of the parameter.

The function should accept a parameter like .Values.name.

It should evaluate the parameter using tpl if a certain condition (e.g., .Values.enableTpl) is met.

The function should handle various types, such as strings and lists.

{{- define "function" -}}
  {{- $value := . -}} <-- this should pick up the parameter
  {{- if .Values.enableTpl }}
    {{ tpl $value $ }}
  {{- else }}
    {{ $value }}
  {{- end }}
{{- end }}

# Usage in template
{{ include "function" .Values.name }} <-- function should not know about this parameter

When trying this, I’m encountering the error: "can't evaluate field Values in type string" when trying to check the enableTpl condition. Removing the enableTpl check also fails, as the . is not the actual parameter ".Values.name"


Note that this is an abstraction of my problem, so please don't question the reasoning for not just doing it without the include :)


Solution

  • The Helm syntax for this is a little tricky. A Go-template function only takes a single parameter, and within that function both the default value of . and $ are set to that parameter.

    This means that, if you need both some value and also the top-level Helm object, you need to pack them together into a single object. I've historically done this via putting both objects into a list

    {{ include "function" (list .Values.name .) }}
    

    and then in the function unpacking them.

    {{- define "function" -}}
    {{- $value := index . 0 -}}
    {{- $top := index . 1 -}}
    {{- if $top.Values.enableTpl -}}
    {{- tpl $value $top -}}
    ...
    

    Note that we've passed the top-level Helm object explicitly as a parameter; unpacked it to $top within the function; and then anywhere we need a field off of that, qualified it as access to $top.

    You can also pass the two "parameters" as a map dict "value" .Values.name "top" .. If you do then you don't need to unpack them in the function, and can directly refer to e.g. .top.Values.enableTpl or tpl .value .top. This pushes some complexity from the function to the caller. Neither pattern is that common but I've seen both.