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 }}
Related
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 -}}
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
I have the following snippet in a helm library which is supposed to add in all the files in a folder into a ConfigMap except for those ending in .tz.
{{- /*
add the contents of every file in the config folder of this IOC helm chart
into the config map - this must include start.sh the startup script.
The files must be text only. Any files ending in .tz are explicitly ommitted
*/ -}}
{{ (.Files.Glob "config/*[!.tz]").AsConfig | indent 2 }}
version.txt: |
IOC {{ .Release.Name }} version {{ .Chart.AppVersion }}
This does not quite work in that it filters out all files ending in . or t or z.
I cant see how to do this with Go globbing. I also cannot work out the syntax for using 'without' for the list created by .Files.Glob.
Can anyone enlighten me? Thanks!
Thanks #David Maze. This almost worked but the reference to .Files.Get failed and I think that is because the . context becomes the range counter within the range loop.
Adding $ to .Files within the loop got it working.
Also to fully replicate AsConfig I needed to extract the basename from
the path which I did by adding regexReplaceAll.
data:
{{- range $path, $_ := .Files.Glob "config/*" }}
{{- if not (hasSuffix ".tz" $path) }}
{{ regexReplaceAll "(.*)/" $path "" }}: |
{{ $.Files.Get $path | indent 4 }}
{{- end }}
{{- end }}
The Helm documentation references the Go "github.com/gobwas/glob" package for the supported glob syntax; that doesn't support any sort of "except" cases, except for single characters.
What you can do instead is iterate over all of the files, and then use a normal conditional to exclude the ones you don't want. You have to construct the ConfigMap structure yourself rather than relying on the AsConfig helper.
data:
{{- range $path, $_ := .Files.Glob "config/*" }}
{{- if not (hasSuffix ".tz" $path) }}
{{ $path }}: |
{{ .Files.Get $path | indent 4 }}
{{- end }}
{{- end }}
im currently going through the docs of helm, and there have been at least 3 different uses for the dot (.), is there any specific definition for it? and does it have something in common with the bash use (actual folder/files)?
some cases in the documentation
This print the accesed files in the range called before?
{{- $files := .Files }}
{{- range tuple "config1.toml" "config2.toml" "config3.toml" }}
{{ . }}: |-
{{ $files.Get . }}
{{- end }}
This tells "mychart.app" to use the files in the current folder (bash-like behaviour)
{{ include "mychart.app" . | indent 4 }}
and this, i guess it takes the values from the whole folder??? i guess this is not correct since is not working (it has been made by another employee back then and i have to fix it)
{{- define "read.select-annot" -}}
{{- range $key, $value := . }}
{{ $key }}: {{ $value }}
{{- end }}
{{- end }}
thanks for the help
In general, . in Helm templates has nothing to do with files or directories.
The Helm templating language uses Go's text/template system. There are a couple of different ways a period character can appear there.
First of all, . can be a character in a string:
{{- range tuple "config1.toml" "config2.toml" "config3.toml" }}
{{/* ^^^^^^^^^^^^
this is a literal string "config1.toml" */}}
...
{{- end }}
Secondly, . can be a lookup operator. There aren't any solid examples in your question, but a typical use is looking up in values. If your values.yaml file has
root:
key: value
then you can expand
{{ .Values.root.key }}
and the . before root and key navigates one level down in the dictionary structure.
The third use, and possibly the one that's confusing you, is that . on its own is a variable.
{{ . }}
You can do field lookups on it, and you have some examples of that: .Files is the same as index . "Files", and looks up the field "Files" on the object ..
You use . as a variable in several places:
{{- $files := .Files }} {{/* Get "Files" from . */}}
{{ . }} {{/* Write . as a value */}}
{{ include "mychart.app" . }} {{/* Pass . as the template parameter */}}
. is tricky in that it has somewhat contextual meaning:
At the top level, Helm initializes . to an object with keys Files, Release, Values, and Chart.
In a defined template, . is the parameter to the template. (So when you include or template, you need to pass . down as that parameter.)
In a range loop, . is the current item being iterated on.
In a with block, . is the matched item if it exists.
In particular, the interaction with range can be tricky. Let's look at a simplified version of your loop:
# {{ . }}
{{- range tuple "config1.toml" "config2.toml" "config3.toml" }}
- {{ . }}
{{- end }}
Outside the range loop, . is probably the top-level Helm object. But inside the range loop, . is the file name (each value from the tuple in turn). That's where you need to save values from . into local variables:
{{/* We're about to invalidate ., so save .Files into a variable. */}}
{{- $files := .Files }}
{{- range tuple "config1.toml" "config2.toml" "config3.toml" }}
{{/* This . is the filename from the "tuple" call */}}
{{ . }}: |-
{{/* Call .Get, from the saved $files, passing the filename .
as the parameter */}}
{{ $files.Get . }}
{{- end }}
Whats the best way to get the helm subchart service names to reference into my ingress controller that will sit in the parent chart
values.yaml
---
ingress:
paths:
- serviceName: app-1
path: /app-1/*
port: 8080
- serviceName: app-2
path: /app-2/*
port: 8080
ingress.yaml
---
{{- range .Values.ingress.paths }}
- path: {{ .path }}
backend:
{{- $subchart := .serviceName -}}
serviceName: {{- include "$subchart.fullname" .}}
servicePort: {{ .port }}
{{- end }}
template: no template "$subchart.fullname" associated with template "gotpl"
helm 3.7 version has solved the problem
https://github.com/helm/helm/pull/9957.
You can use like this
{{ template "bar.fullname" .Subcharts.bar }}
How about hardcoded subchart name scoped by release ?
{{ .Release.Name }}-<subchart_name>
I have found that the best way to reference a service name is to override the template that they are using. There are some caveats to doing this however.
The subchart and your chart will have different contexts so they will most likely render the template differently
There are some things that are only available to the subchart
Most charts have a template similar to the one below in their _helpers.tpl file.
{{/*
Create a default fully qualified app name.
We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec).
If release name contains chart name it will be used as a full name.
*/}}
{{- define "newchart.fullname" -}}
{{- if .Values.fullnameOverride }}
{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- $name := default .Chart.Name .Values.nameOverride }}
{{- if contains $name .Release.Name }}
{{- .Release.Name | trunc 63 | trimSuffix "-" }}
{{- else }}
{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }}
{{- end }}
{{- end }}
{{- end }}
The subchart has different .Values to your chart. We will fix this when we render this template by creating a context that is similar to the subcharts context.
Instead of calling it with the . context we create a new context by replacing the .Values with the subcharts .Values.
{{ template "newchart.fullname" (set (deepCopy .) "Values" .Values.newchart }}
We use deepCopy so that we don't actually change the . context but rather create a new one to use.
The subchart has access to its own .Chart values that we can't replicate. In this case we will have to hardcode the value of .Chart.Name to the template. In this we can just replace it with the chart name newchart.
Once we have done this both nameOverride and fullnameOverride on the subchart will work without you having to manually change anything in your template files.
If the subchart uses the fullname function from _helpers.tpl (provided by helm by default for new charts) you can use this (replace postgresql with the name of the subchart):
{{- $fullName := include "postgresql.fullname" (mustMerge (dict "Chart" (dict "Name" "postgresql") "Values" .Values.postgresql) (deepCopy .)) -}}
It depends on the sub-chart definition!
As an example, elasticsearch chart, see here https://github.com/elastic/helm-charts/blob/master/elasticsearch/templates/service.yaml, is defining 2 services.
Both services name can be declared as value clusterName.