Helm: How to Reference Variables From values?

 

Background

Helm provides a straightforward template language that makes it simple to refer to configuration settings that are defined in a "values file."

 

 

e.g. The "name" config value from the "values file" is referred to in the Helm chart template above. The template will eventually be produced like follows, assuming the string "world" is the value of the configuration field "name":

 

 

Referencing a "value" in a Helm chart template appears tidy and easy. So how might we use the Helm chart's "values file" to refer to other values?

 

Why?

An example "values file" that provides a collection of API endpoint URLs is shown below:

 

apiOneUrl: http://example.com/apiOne/v0

apiTwoUrl: http://example.com/apiTwo/v3

apiThreeUrl: http://example.com/apiThree/v7

 

You could see that the string "http://example.com/" appears in every configuration field. If we could provide the "values file" like follows, that would be nice:

 

baseUrl: http://example.com

apiOneUrl: "{{ .Values.baseUrl }}/apiOne/v0"

apiTwoUrl: "{{ .Values.baseUrl }}/apiTwo/v3"

apiThreeUrl: "{{ .Values.baseUrl }}/apiThree/v7"

 

That will not only eliminate duplication and save a ton of typing, but it will also make updating configuration variables much simpler in the future.

However, since Helm's template engine won't parse the "values file," referencing ".Values.apiOneUrl" in your template will simply return the original string "{{ .Values.baseUrl }}/apiOne/v0"

 

tpl Function to Rescue

Here is where the Helm chart tpl function comes in in. It enables developers to evaluate texts as templates inside of templates. The function anticipates two inputs:

  • A template string that has to be processed is the first argument.

  • The context data to be used when processing the template string is the final argument. We frequently succeed. This is up-to-date context information to ensure that all configuration settings are accessible throughout template processing.

 

In your template, you may attempt the following:

MyApiOneUrl: {{ tpl .Values.apiOneUrl . }}

you will find .Values.apiOneUrl ‘s value "{{ .Values.baseUrl }}/apiOne/v0" will be converted into "http://example.com//apiOne/v0” and the above template will be rendered as:

MyApiOneUrl: http://example.com/apiOne/v0

 

Problem with Non-string Types

When using basic string type configuration settings, the technique works effectively. What happens, though, if the configuration value is of a non-string type, such as a "map" or "list"?

 

Have a look at the values file below:

 

baseUrl: http://example.com

apiUrls:

 apiOneUrl: "{{ .Values.baseUrl }}/apiOne/v0"

 apiTwoUrl: "{{ .Values.baseUrl }}/apiTwo/v3"

 apiThreeUrl: "{{ .Values.baseUrl }}/apiThree/v7"

 

In your template, you may attempt the following:

MyApiUrls: {{ tpl .Values.apiUrls . }}

 

The following template error will appear:

wrong type for value; expected string; got map[string]interface {}

 

The error message makes sense because the tpl function anticipates a template string as the first parameter. So how might we address the problem and make our solution applicable to this typical use case?

 

One option is to feed the non-string type value (in this example, map) to the toYaml function first and then convert it to a "YAML" string:

MyApiUrls:

{{ tpl (.Values.apiUrls | toYaml) . | indent 2 }}

 

If you use the aforementioned template, it will be appropriately displayed as:

MyApiUrls:

 

apiOneUrl: http://example.com/apiOne/v0

 apiTwoUrl: http://example.com/apiTwo/v3

 apiThreeUrl: http://example.com/apiThree/v7

 

The Complete Solution

We may construct a "named template" to recognize the configuration value type and use the appropriate logic, making the solution portable:

 

{{ define "render-value" }}

 {{- if kindIs "string" .value }}

   {{- tpl .value .context }}

 {{- else }}

   {{- tpl (.value | toYaml) .context }}    

 {{- end }}

{{- end }}

 

You may use the include function to call the "named template" in a template:

MyApiOneUrl: {{ include "render-value" ( dict "value" .Values.apiOneUrl "context" .) }}

MyApiUrls:

{{ include "render-value" ( dict "value" .Values.apiUrls "context" .) | indent 2}}

The following will be created from the aforementioned template:

 

MyApiOneUrl: http://example.com/apiOne/v0

MyApiUrls:

 apiOneUrl: http://example.com/apiOne/v0

 apiTwoUrl: http://example.com/apiTwo/v3

 apiThreeUrl: http://example.com/apiThree/v7

 

You may also utilize Bitnami’s “common” library chart if you want to avoid having to define your own "named template”. Just add the upcoming dependency to your Chart.yaml to start using it :

dependencies:

 - name: common

   version: 1.11.1

   repository: https://charts.bitnami.com/bitnami

 

Then, using the same reasoning as the following, you may call the built-in "named template"

MyApiOneUrl: {{ include "common.tplvalues.render" ( dict "value" .Values.apiOneUrl "context" .) }}

 

Summary

We can combine configuration data into templates thanks to Helm's straightforward yet effective template engine. But, occasionally we could also wish to use the Helm's "values file" as a configuration file. The "named template" implementation of a tpl function-based solution is introduced in this article.

If you are unfamiliar with Helm's "named template" function, you may want to read Help official to learn more about what it can accomplish.


 

In Apprecode we are always ready to consult you about implementing DevOps methodology. Please contact us for more information.

 

Read also

What you need to know about ePBF in Kubernetes

eBPF, the Extended Berkeley Packet Filter, is a new technology gaining traction in Kubernetes. It allows users to attach custom programs to specific events within the network and other kernel subsystems. This article explores the potential applications of eBPF in Kubernetes, highlighting its benefits for networking, security, and system monitoring.

Understanding Kubernetes Horizontal Pod Autoscaling

By using auto-scaling, your application's computing resources can be automatically increased or decreased depending on the amount of resources it needs at any given time. It came about thanks to advances in cloud computing, which fundamentally altered how computer resources are assigned and made it possible to build a fully scalable server on the cloud.