Kubernetes Helm: Helpers includes accessible in one file, but not another - kubernetes

In one directory are numerous files, there is a _helpers.tpl file as well as .yaml templates that we use to deploy different services. To simplify my issue and what I'm working with:
_helpers.tpl
backup.yaml
writer.yaml
There is a variable defined in the _helpers.tpl file that is accessible in backup.yaml, but the exact same line of code referring to the exact same variable in writer.yaml gives the following error:
Error: template: temp-chart/templates/template.yaml:8:23: executing "temp-chart/templates/template.yaml" at <include "account" .>: error calling include: template: no template "account" associated with template "gotpl"
I did not create this project, so I'm wondering if there was an error in the initial setup of the writer.yaml file or something was overlooked? Are there any steps required in configuring the helpers file or the templates that ensures they can access one another?
_helpers.tpl
{{- define "account" -}}
{{- if .Values.backup.account -}}
{{- .Values.backup.account-}}
{{- else -}}
{{- .Values.buildVals.accountId -}}
{{- end -}}
{{- end -}}
backup.yaml and writer.yaml are identical:
account: {{ include "account" . }}

Related

Values context in an included helm named template

We have a helm chart that contains named templates to be used by other templates.
Originally, the helm chart containing the named templates has no "values.yaml" file, as all it has are "_function.tpl" files. But now, we would like to use a "values.yaml" file to define some values there instead of having to be passed by the caller like so after defining the dependency in the Chart.yaml.
{{ include "libchart.velero" (list . .Values.velero )}}
The named template then would have a definition, which converts those contexts passed as list to $root and "velero", so we can work comfortably with the caller passed context, like so:
{{- define "libchart.velero" -}}
{{- $root := index . 0 -}}
{{- $velero := index . 1 -}}
But question is, how could I define and consume the variables define in the "values.yaml" file present in the chart that contains the definition of the named template itself.
I've tried using things like {{ $.Values.local }} and {{ .Values.local }} to access "locally to the named template" defined variable, but no luck with those.
With that construction, the top-level Helm object (which contains Values, Release, Namespace, etc. fields) is in the $root variable inside the template.
{{-/*
Call with a list of two items, the top-level Helm object and the
.Values.velero chart configuration.
Outputs something only if the `local` top-level value is set.
*/-}}
{{- define "libchart.velero" -}}
{{- $root := index . 0 -}}
{{- $velero := index . 1 -}}
{{- if $root.Values.local -}} {{-/* <-- like this */-}}
... {{ $velero }} ...
{{- end -}}
{{- end -}}

Helm's v3 Example Doesn't Show Multi-line Properties. Get YAML to JSON parse error

In Helm's v3 documentation: Accessing Files Inside Templates, the author gives an example of 3 properties (toml) files; where each file has only one key/value pair.
The configmap.yaml looks like this. I'm only adding one config.toml for simplicity.
apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-config
data:
{{- $files := .Files }}
{{- range tuple "config.toml" }}
{{ . }}: |-
{{ $files.Get . }}
{{- end }}
This works fine, until I add a second line to the config.toml file.
config.toml
replicaCount=1
foo=bar
Then I get an Error: INSTALLATION FAILED: YAML parse error on deploy/templates/configmap.yaml: error converting YAML to JSON: yaml: line 9: could not find expected ':'
Any thoughts will be appreciated.
Thanks
Helm will read in that file, but it is (for good or bad) a text templating engine. It does not understand that you are trying to compose a YAML file and thus it will not help you. That's actually why you will see so many, many templates in the wild with {{ .thing | indent 8 }} or {{ .otherThing | toYaml }} -- because you need to help Helm know in what context it is emitting the text
Thus, in your specific case, you'll want the indent filter with a value of 4 because your current template has two spaces for the key indent level, and two more spaces for the value block scalar
data:
{{- $files := .Files }}
{{- range tuple "config.toml" }}
{{ . }}: |-
{{ $files.Get . | indent 4 }}
{{/* notice this ^^^ template expression is flush left,
because the 'indent' is handling whitespace, not the golang template itself */}}
{{- end }}
Also, while this is the specific answer to your question, don't overlook the .AsConfig section on that page which seems much more likely to be what you really want to happen, and requires less indent math

Is it possile to create yaml object from helm from file

I want to iterate over the Yaml list which is defined in the file, and use it in Job. For example i have
test.yaml
list:
- first element
- second element
In _helpers.tpl i can define
something like
{{- define "mychart.list" -}}
{{ .Files.Get "test.yaml"| toYaml }}
{{- end }}
And then in Job i want do something like
{{- $lists := include "mychart.list" . }}
{{- range $element := $lists.list}}
apiVersion: batch/v1
kind: Job
metadata:
and then use the $element.
But when i am trying to do the dry-run with --debug it complains about
at <$lists.list>: can't evaluate field list in type string.
Looks like whole value is coming as string rather than Yaml, does it needs explicit call to Yaml parser ? If yes, is there a way to do that ?
BTW i have also tried various combinations of
{{- $lists := include "mychart.list" . | toYaml }}
or loading the file inline, but none of them helps.
I can put the list in Values.yaml, but don't want to do that purposely.
Any help is really appreciated.
Just putting for future reference, if someone comes and have look.
There's fromYaml function, which is undocumented. I found it hard way, but that solved the isssue.
{{- $lists := include "mychart.list" . | fromYaml}}

Helm include templates

Please! Is it possible to squash multiple helm templates into one and then refer to it as a one-liner in a deployment file?
EG:
{{- define "foo.deploy" -}}
value:
{{- include "foo.1" . | nindent 6 }}
{{- include "foo.2" . | nindent 6 }}
{{- include "foo.3" . | nindent 6 }}
And then do an {{- include "foo.deploy" . }} in a separate deployment file.
Which should then contain foo.1, foo.2 and foo.3, and their respective definitions.
As opposed to literally writing out all three different 'includes' especially if you've got loads.
Much appreciated,
Thanks,
A named template (sometimes called a partial or a subtemplate) is simply a template defined inside of a file, and given a name. We’ll see two ways to create them, and a few different ways to use them.
Template names are global. As a result of this, if two templates are declared with the same name the last occurrence will be the one that is used. Since templates in subcharts are compiled together with top-level templates, it is best to name your templates with chart specific names. A popular naming convention is to prefix each defined template with the name of the chart: {{ define "mychart.labels" }}.
More information about named templates you can find here: named-template.
Proper configuration file should look like:
{{/* Generate basic labels */}}
{{- define "mychart.labels" }}
labels:
generator: helm
date: {{ now | htmlDate }}
{{- end }}
In your case part of file should looks like:
{{- define "foo.deploy" -}}
{{- include "foo.1" . | nindent 6 }}
{{- include "foo.2" . | nindent 6 }}
{{- include "foo.3" . | nindent 6 }}
{{ end }}

Helm control input values

I'm looking for a solution to control the input values(defined in values.yaml). I would like to check if the input value is authorized.
Example:
values.yaml
provider: aws
services:
- nginx
- varnish
- php
And in another file(maybe _helpers.tpl?)
authorized_providers:
- aws
- azure
authorized_services:
- nginx
- php
And raise an error(custom message if it's possible) to indicate that the input values are not supported/authorized.
My goal is to avoid to generate a Kubernetes configmap with unsupported values(helm install works but this configuration will generate container errors).
EDIT:
I finally found a solution using "required" with some tricks.
Following my example with my values.yaml config file.
I define in _helpers.tpl:
{{/*
Define the authorized Value for the parameter: .Values.provider
*/}}
{{- define "authorized.provider" }}
{{- printf "aws,azure" }}
{{- end }}
{{/*
Define the error message if the .Values.provider doesn't respect the authorized.provider condition.
*/}}
{{- define "error.provider" }}
{{- $provider := include "authorized.provider" . }}
{{- printf "Invalid value set for .Values.provider - Must be one of %s" $provider }}
{{- end }}
{{/*
Define the authorized Value for the parameter: .Values.services
*/}}
{{- define "authorized.services" }}
{{- printf "nginx,php" }}
{{- end }}
{{/*
Define the error message if the .Values.services doesn't respect the authorized.services condition.
*/}}
{{- define "error.services" }}
{{- $services := include "authorized.services" . }}
{{- printf "Invalid value set for .Values.services - Authorized values are %s" $services }}
{{- end }}
And next, I've created another file: input-values-validation.yaml
{{- $provider := include "authorized.provider" . }}
{{- $errorProvider := include "error.provider" . }}
{{- if not (has .Values.provider (splitList "," $provider)) }}
{{ required $errorProvider .Values.foo }}
{{- end }}
{{- $services := include "authorized.services" . }}
{{- $errorServices := include "error.Services" . }}
{{- $root := . -}}
{{- range .Values.services }}
{{- if not (has . (splitList "," $services)) }}
{{ required $errorServices $root.Values.foo }}
{{- end }}
{{- end }}
Output if bad input value:
==> Linting
[ERROR] templates/: render error in "templates/input-values-validation.yaml": template: templates/input-values-validation.yaml:12:3: executing "templates/input-values-validation.yaml" at<required $errorServ...>: error calling required: Invalid value set for .Values.services - Authorized values are nginx,php
Infos:
".Values.foo" must never be set in the values.yaml file. I used it to fail the "required" check and raise the error.
I've tried to put the content of "input-values-validation.yaml" in the _helpers.tpl file but this generate an error "[ERROR] templates/: rendering template failed: runtime error: invalid memory address or nil pointer dereference". It seems that the "required" function must be used only in yaml files.
So with this solution, I'm able to define the authorized values in the _helpers.tpl file and generate a "custom" error message. And if in the futur I support more providers/services(my example), I'll only need to modify the value in "authorized.provider" and "authorized.services".
I've not seen it done with helm2, at least not in a scan of the official charts, the attempt to define common functions in an incubator chart.
The trickiest bit is being able to give a good error - the closest I've seen is the sprig fail function
But helm3 should provide for this kind of validation, either with schemas or lua
Otherwise perhaps you could do it like:
aws:
nginx: false
varnish: false
php: true
So that the chart user chooses which services they want with a true/false