@@ -21,13 +21,13 @@ The first control structure we'll look at is for conditionally including blocks
The basic structure for a conditional looks like this:
```
{{if PIPELINE}}
{{ if PIPELINE }}
# Do something
{{else if OTHER PIPELINE}}
{{ else if OTHER PIPELINE }}
# Do something else
{{ else }}
# Default case
{{end}}
{{ end }}
```
Notice that we're now talking about _pipelines_ instead of values. The reason for this is to make it clear that control structures can execute an entire pipeline, not just evaluate a value.
...
...
@@ -48,12 +48,12 @@ Let's add a simple conditional to our ConfigMap. We'll add another setting if th
{{if eq .Values.favorite.drink "coffee"}}mug:true{{ end }}
```
Since we commented out `drink: coffee` in our last example, the output should not include a `mug: true` flag. But if we add that line back into our `values.yaml` file, the output should look like this:
...
...
@@ -79,11 +79,11 @@ While we're looking at conditionals, we should take a quick look at the way whit
Be careful with the chomping modifiers. It is easy to accidentally do things like this:
```yaml
food:{{.Values.favorite.food | upper | quote}}
food:{{.Values.favorite.food | upper | quote}}
{{- if eq .Values.favorite.drink "coffee" -}}
mug:true
{{- end -}}
...
...
@@ -234,12 +234,12 @@ Scopes can be changed. `with` can allow you to set the current scope (`.`) to a
apiVersion:v1
kind:ConfigMap
metadata:
name:{{.Release.Name}}-configmap
name:{{.Release.Name}}-configmap
data:
myvalue:"HelloWorld"
{{- with .Values.favorite}}
drink:{{.drink | default "tea" | quote}}
food:{{.food | upper | quote}}
drink:{{.drink | default "tea" | quote}}
food:{{.food | upper | quote}}
{{- end}}
```
...
...
@@ -251,9 +251,9 @@ But here's a note of caution! Inside of the restricted scope, you will not be ab
```yaml
{{- with .Values.favorite}}
drink:{{.drink | default "tea" | quote}}
food:{{.food | upper | quote}}
release:{{.Release.Name}}
drink:{{.drink | default "tea" | quote}}
food:{{.food | upper | quote}}
release:{{.Release.Name}}
{{- end}}
```
...
...
@@ -261,10 +261,10 @@ It will produce an error because `Release.Name` is not inside of the restricted
```yaml
{{- with .Values.favorite}}
drink:{{.drink | default "tea" | quote}}
food:{{.food | upper | quote}}
drink:{{.drink | default "tea" | quote}}
food:{{.food | upper | quote}}
{{- end}}
release:{{.Release.Name}}
release:{{.Release.Name}}
```
After looking a `range`, we will take a look at template variables, which offer one solution to the scoping issue above.
...
...
@@ -292,23 +292,23 @@ Now we have a list (called a `slice` in templates) of `pizzaToppings`. We can mo
apiVersion:v1
kind:ConfigMap
metadata:
name:{{.Release.Name}}-configmap
name:{{.Release.Name}}-configmap
data:
myvalue:"HelloWorld"
{{- with .Values.favorite}}
drink:{{.drink | default "tea" | quote}}
food:{{.food | upper | quote}}
drink:{{.drink | default "tea" | quote}}
food:{{.food | upper | quote}}
{{- end}}
toppings:|-
{{- range .Values.pizzaToppings }}
- {{. | title | quote }}
- {{. | title | quote }}
{{- end }}
```
Let's take a closer look at the `toppings:` list. The `range` function will "range over" (iterate through) the `pizzaToppings` list. But now something interesting happens. Just like `with` sets the scope of `.`, so does a `range` operator. Each time through the loop, `.` is set to the current pizza topping. That is, the first time, `.` is set to `mushrooms`. The second iteration it is set to `cheese`, and so on.
We can send the value of `.` directly down a pipeline, so when we do `{{. | title | quote }}`, it sends `.` to `title` (title case function) and then to `quote`. If we run this template, the output will be:
We can send the value of `.` directly down a pipeline, so when we do `{{. | title | quote }}`, it sends `.` to `title` (title case function) and then to `quote`. If we run this template, the output will be:
```yaml
# Source: mychart/templates/configmap.yaml
...
...
@@ -336,7 +336,7 @@ Sometimes it's useful to be able to quickly make a list inside of your template,
```yaml
sizes:|-
{{- range tuple "small" "medium" "large" }}
- {{.}}
- {{ . }}
{{- end }}
```
...
...
@@ -349,4 +349,4 @@ The above will produce this:
- large
```
In addition to lists and tuples, `range` can be used to iterate over collections that have a key and a value (like a `map` or `dict`). We'll see how to do that in the next section when we introduce template variables.
\ No newline at end of file
In addition to lists and tuples, `range` can be used to iterate over collections that have a key and a value (like a `map` or `dict`). We'll see how to do that in the next section when we introduce template variables.
@@ -8,11 +8,11 @@ Let's start with a best practice: When injecting strings from the `.Values` obje
apiVersion: v1
kind: ConfigMap
metadata:
name: {{.Release.Name}}-configmap
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{quote .Values.favorite.drink}}
food: {{quote .Values.favorite.food}}
drink: {{ quote .Values.favorite.drink }}
food: {{ quote .Values.favorite.food }}
```
Template functions follow the syntax `functionName arg1 arg2...`. In the snippet above, `quote .Values.favorite.drink` calls the `quote` function and passes it a single argument.
...
...
@@ -29,11 +29,11 @@ One of the powerful features of the template language is its concept of _pipelin
apiVersion:v1
kind:ConfigMap
metadata:
name:{{.Release.Name}}-configmap
name:{{.Release.Name}}-configmap
data:
myvalue:"HelloWorld"
drink:{{.Values.favorite.drink | quote}}
food:{{.Values.favorite.food | quote}}
drink:{{.Values.favorite.drink | quote}}
food:{{.Values.favorite.food | quote}}
```
In this example, instead of calling `quote ARGUMENT`, we inverted the order. We "sent" the argument to the function using a pipeline (`|`): `.Values.favorite.drink | quote`. Using pipelines, we can chain several functions together:
...
...
@@ -42,11 +42,11 @@ In this example, instead of calling `quote ARGUMENT`, we inverted the order. We
apiVersion:v1
kind:ConfigMap
metadata:
name:{{.Release.Name}}-configmap
name:{{.Release.Name}}-configmap
data:
myvalue:"HelloWorld"
drink:{{.Values.favorite.drink | quote}}
food:{{.Values.favorite.food | upper | quote}}
drink:{{.Values.favorite.drink | quote}}
food:{{.Values.favorite.food | upper | quote}}
```
> Inverting the order is a common practice in templates. You will see `.val | quote` more often than `quote .val`. Either practice is fine.
...
...
@@ -73,11 +73,11 @@ When pipelining arguments like this, the result of the first evaluation (`.Value
The `repeat` function will echo the given string the given number of times, so we will get this for output:
...
...
@@ -99,7 +99,7 @@ data:
One function frequently used in templates is the `default` function: `default DEFAULT_VALUE GIVEN_VALUE`. This function allows you to specify a default value inside of the template, in case the value is omitted. Let's use it to modify the drink example above:
@@ -151,17 +151,17 @@ Let's alter `configmap.yaml` accordingly.
apiVersion:v1
kind:ConfigMap
metadata:
name:{{.Release.Name}}-configmap
name:{{.Release.Name}}-configmap
data:
myvalue:"HelloWorld"
```
The big change comes in the value of the `name:` field, which is now
`{{.Release.Name}}-configmap`.
`{{ .Release.Name }}-configmap`.
> A template directive is enclosed in `{{` and `}}` blocks.
The template directive `{{.Release.Name}}` injects the release name into the template. The values that are passed into a template can be thought of as _namespaced objects_, where a dot (`.`) separates each namespaced element.
The template directive `{{ .Release.Name }}` injects the release name into the template. The values that are passed into a template can be thought of as _namespaced objects_, where a dot (`.`) separates each namespaced element.
The leading dot before `Release` indicates that we start with the top-most namespace for this scope (we'll talk about scope in a bit). So we could read `.Release.Name` as "start at the top namespace, find the `Release` object, then look inside of it for an object called `Name`".
...
...
@@ -210,4 +210,4 @@ data:
Using `--dry-run` will make it easier to test your code, but it won't ensure that Kubernetes itself will accept the templates you generate. It's best not to assume that your chart will install just because `--dry-run` works.
In the next few sections, we'll take the basic chart we defined here and explore the Helm template language in detail. And we'll get started with built-in objects.
\ No newline at end of file
In the next few sections, we'll take the basic chart we defined here and explore the Helm template language in detail. And we'll get started with built-in objects.
@@ -21,39 +21,39 @@ These files are used to store partials and helpers. In fact, when we first creat
The `define` action allows us to create a named template inside of a template file. Its syntax goes like this:
```yaml
{{define "MY_NAME"}}
{{define "MY_NAME"}}
# body of template here
{{end}}
{{end}}
```
For example, we can define a template to encapsulate a Kubernetes block of labels:
```yaml
{{- define "my_labels"}}
{{- define "my_labels"}}
labels:
generator:helm
date:{{now | htmlDate}}
{{- end}}
date:{{now | htmlDate}}
{{- end}}
```
Now we can embed this template inside of our existing ConfigMap, and then include it with the `template` action:
```
{{- define "my_labels"}}
{{- define "my_labels"}}
labels:
generator: helm
date: {{now | htmlDate}}
{{- end}}
date: {{ now | htmlDate }}
{{- end}}
apiVersion: v1
kind: ConfigMap
metadata:
name: {{.Release.Name}}-configmap
{{- template "my_labels"}}
name: {{ .Release.Name }}-configmap
{{- template "my_labels"}}
data:
myvalue: "Hello World"
{{- range $key, $val := .Values.favorite }}
{{$key}}: {{$val | quote}}
{{- end}}
{{ $key }}: {{ $val | quote }}
{{- end}}
```
When the template engine reads this file, it will store away the reference to `my_labels` until `template "my_labels"` is called. Then it will render that template inline. So the result will look like this:
...
...
@@ -77,11 +77,11 @@ Conventionally, Helm charts put these templates inside of a partials file, usual
```yaml
{{/* Generate basic labels */}}
{{- define "my_labels"}}
{{- define "my_labels"}}
labels:
generator:helm
date:{{now | htmlDate}}
{{- end}}
date:{{now | htmlDate}}
{{- end}}
```
By convention, `define` functions should have a simple documentation block (`{{/* ... */}}`) describing what they do.
...
...
@@ -92,18 +92,18 @@ Even though this definition is in `_helpers.tpl`, it can still be accessed in `c
apiVersion:v1
kind:ConfigMap
metadata:
name:{{.Release.Name}}-configmap
{{- template "my_labels"}}
name:{{.Release.Name}}-configmap
{{- template "my_labels"}}
data:
myvalue:"HelloWorld"
{{- range $key,$val:= .Values.favorite}}
{{$key}}:{{$val | quote}}
{{- end}}
{{$key}}:{{$val | quote}}
{{- end}}
```
There is one _really important detail_ to keep in mind when naming templates: **template names are global**. If you declare two templates with the same name, whichever one is loaded last will be the one used. Because templates in subcharts are compiled together with top-level templates, you should be careful to name your templates with chart-specific names.
One popular naming convention is to prefix each defined template with the name of the chart: `{{define "mychart.labels"}}` or `{{define "mychart_labels"}}`.
One popular naming convention is to prefix each defined template with the name of the chart: `{{ define "mychart.labels" }}` or `{{ define "mychart_labels" }}`.
## Setting the scope of a template
...
...
@@ -111,13 +111,13 @@ In the template we defined above, we did not use any objects. We just used funct
```yaml
{{/* Generate basic labels */}}
{{- define "my_labels"}}
{{- define "my_labels"}}
labels:
generator:helm
date:{{now | htmlDate}}
chart:{{.Chart.Name}}
version:{{.Chart.Version}}
{{- end}}
date:{{now | htmlDate}}
chart:{{.Chart.Name}}
version:{{.Chart.Version}}
{{- end}}
```
If we render this, the result will not be what we expect:
...
...
@@ -147,7 +147,7 @@ No scope was passed in, so within the template we cannot access anything in `.`.
apiVersion:v1
kind:ConfigMap
metadata:
name:{{.Release.Name}}-configmap
name:{{.Release.Name}}-configmap
{{- template "my_labels" .}}
```
...
...
@@ -168,7 +168,7 @@ metadata:
version:0.1.0
```
Now `{{.Chart.Name}}` resolves to `mychart`, and `{{.Chart.Version}}` resolves to `0.1.0`.
Now `{{ .Chart.Name }}` resolves to `mychart`, and `{{ .Chart.Version }}` resolves to `0.1.0`.
## Creating override-able sections with `block`
...
...
@@ -177,8 +177,8 @@ Say we want to create a template in our `_helpers.tpl` file, but then override p
Blocks are declared like this:
```yaml
{{block "NAME" PIPELINE}}
{{end}}
{{block "NAME" PIPELINE}}
{{end}}
```
Here, "NAME" is the name that a `define` block can use to override it, and PIPELINE is the pipeline that will set the scope. So let's rewrite our `labels:` section to use this strategy. We'll create a basic labels section in our `_helpers.tpl` file, but add some extra labels in the `configmap.yaml` template.
...
...
@@ -186,12 +186,12 @@ Here, "NAME" is the name that a `define` block can use to override it, and PIPEL
Let's start with `_helpers.tpl`:
```
{{- define "my_labels"}}
{{- define "my_labels"}}
labels:
chart: {{.Chart.Name}}
version: {{.Chart.Version}}
{{block "my_extra_labels" .}}extras: false{{end}}
{{- end}}
chart: {{ .Chart.Name }}
version: {{ .Chart.Version }}
{{ block "my_extra_labels" . }}extras: false{{ end }}
{{- end `u}}
```
Inside of our `my_labels` template, we now declare a block called `my_extra_labels`. By default, this section will have one extra label: `extras: false`. If we were to execute this using the same `configmap.yaml` file from last time, we'd get this:
...
...
@@ -215,20 +215,20 @@ data:
But inside of our `configmap.yaml` template, we can override `my_extra_labels`:
@@ -268,15 +268,15 @@ Now say I want to insert this both into the `labels:` section of my template, an
apiVersion:v1
kind:ConfigMap
metadata:
name:{{.Release.Name}}-configmap
name:{{.Release.Name}}-configmap
labels:
{{template "mychart_app" .}}
{{template "mychart_app" .}}
data:
myvalue:"HelloWorld"
{{- range $key,$val:= .Values.favorite}}
{{$key}}:{{$val | quote}}
{{- end}}
{{template "mychart_app" .}}
{{$key}}:{{$val | quote}}
{{- end}}
{{template "mychart_app" .}}
```
...
...
@@ -309,15 +309,15 @@ Here's the example above, corrected to use `indent` to indent the `mychart_app`
apiVersion:v1
kind:ConfigMap
metadata:
name:{{.Release.Name}}-configmap
name:{{.Release.Name}}-configmap
labels:
{{include "mychart_app" . | indent 4}}
{{include "mychart_app" . | indent 4}}
data:
myvalue:"HelloWorld"
{{- range $key,$val:= .Values.favorite}}
{{$key}}:{{$val | quote}}
{{- end}}
{{include "mychart_app" . | indent 2}}
{{$key}}:{{$val | quote}}
{{- end}}
{{include "mychart_app" . | indent 2}}
```
Now the produced YAML is correctly indented for each section:
...
...
@@ -341,4 +341,4 @@ data:
> It is considered preferable to use `include` over `template` in Helm templates simply so that the output formatting can be handled better for YAML documents.
Sometimes we want to import content, but not as templates. That is, we want to import files verbatim. We can achieve this by accessing files through the `.Files` object described in the next section.
\ No newline at end of file
Sometimes we want to import content, but not as templates. That is, we want to import files verbatim. We can achieve this by accessing files through the `.Files` object described in the next section.
@@ -7,14 +7,14 @@ To add installation notes to your chart, simply create a `templates/NOTES.txt` f
Let's create a simple `NOTES.txt` file:
```
Thank you for installing {{.Chart.Name}}.
Thank you for installing {{ .Chart.Name }}.
Your release is named {{.Release.Name}}.
Your release is named {{ .Release.Name }}.
To learn more about the release, try:
$ helm status {{.Release.Name}}
$ helm get {{.Release.Name}}
$ helm status {{ .Release.Name }}
$ helm get {{ .Release.Name }}
```
...
...
@@ -42,4 +42,4 @@ To learn more about the release, try:
$ helm get rude-cardinal
```
Using `NOTES.txt` this way is a great way to give your users detailed information about how to use their newly installed chart. Creating a `NOTES.txt` file is strongly recommended, though it is not required.
\ No newline at end of file
Using `NOTES.txt` this way is a great way to give your users detailed information about how to use their newly installed chart. Creating a `NOTES.txt` file is strongly recommended, though it is not required.
@@ -38,9 +38,9 @@ Next, we'll create a new ConfigMap template in `mychart/charts/subchart/template
apiVersion: v1
kind: ConfigMap
metadata:
name: {{.Release.Name}}-cfgmap2
name: {{ .Release.Name }}-cfgmap2
data:
dessert: {{.Values.dessert}}
dessert: {{ .Values.dessert }}
```
Because every subchart is a _stand-alone chart_, we can test `mysubchart` on its own:
...
...
@@ -124,7 +124,7 @@ global:
salad:caesar
```
Because of the way globals work, both `mychart/templates/configmap.yaml` and `mysubchart/templates/configmap.yaml` should be able to access that value as `{{.Values.global.salad}}`.
Because of the way globals work, both `mychart/templates/configmap.yaml` and `mysubchart/templates/configmap.yaml` should be able to access that value as `{{.Values.global.salad}}`.
`mychart/templates/configmap.yaml`:
...
...
@@ -132,9 +132,9 @@ Because of the way globals work, both `mychart/templates/configmap.yaml` and `my
apiVersion:v1
kind:ConfigMap
metadata:
name:{{.Release.Name}}-configmap
name:{{.Release.Name}}-configmap
data:
salad:{{.Values.global.salad}}
salad:{{.Values.global.salad}}
```
`mysubchart/tempaltes/configmap.yaml`:
...
...
@@ -143,10 +143,10 @@ data:
apiVersion:v1
kind:ConfigMap
metadata:
name:{{.Release.Name}}-cfgmap2
name:{{.Release.Name}}-cfgmap2
data:
dessert:{{.Values.dessert}}
salad:{{.Values.global.salad}}
dessert:{{.Values.dessert}}
salad:{{.Values.global.salad}}
```
Now if we run a dry run install, we'll see the same value in both outputs:
...
...
@@ -181,11 +181,11 @@ Parent charts and subcharts can share templates. This can become very powerful w
apiVersion:v1
kind:ConfigMap
metadata:
name:{{.Release.Name}}-cfgmap2
{{block "labels" .}}from:mysubchart{{end}}
name:{{.Release.Name}}-cfgmap2
{{block "labels" .}}from:mysubchart{{ end }}
data:
dessert:{{.Values.dessert}}
salad:{{.Values.global.salad}}
dessert:{{.Values.dessert}}
salad:{{.Values.global.salad}}
```
Running this would produce:
...
...
@@ -205,7 +205,7 @@ data:
Note that the `from:` line says `mysubchart`. In a previous section, we created `mychart/templates/_helpers.tpl`. Let's define a new named template there called `labels` to match the declaration on the block above.
```yaml
{{- define "labels"}}from:mychart{{end}}
{{- define "labels"}}from:mychart{{ end }}
```
Recall how the labels on templates are _globally shared_. That means that if we create a block named `labels` in one chart, and then define an override named `labels` in another chart, the override will be applied.
@@ -23,13 +23,13 @@ Now we can use this inside of a template:
apiVersion:v1
kind:ConfigMap
metadata:
name:{{.Release.Name}}-configmap
name:{{.Release.Name}}-configmap
data:
myvalue:"HelloWorld"
drink:{{.Values.favoriteDrink}}
drink:{{.Values.favoriteDrink}}
```
Notice on the last line we access `favoriteDrink` as an attribute of `Values`: `{{.Values.favoriteDrink}}`.
Notice on the last line we access `favoriteDrink` as an attribute of `Values`: `{{.Values.favoriteDrink}}`.
Let's see how this renders.
...
...
@@ -89,11 +89,11 @@ Now we would have to modify the template slightly:
apiVersion: v1
kind: ConfigMap
metadata:
name: {{.Release.Name}}-configmap
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{.Values.favorite.drink}}
food: {{.Values.favorite.food}}
drink: {{ .Values.favorite.drink }}
food: {{ .Values.favorite.food }}
```
While structuring data this way is possible, the recommendation is that you keep your values trees shallow, favoring flatness. When we look at assigning values to subcharts, we'll see how values are named using a tree structure.
@@ -6,9 +6,9 @@ In an earlier example, we saw that this code will fail:
```yaml
{{- with .Values.favorite}}
drink:{{.drink | default "tea" | quote}}
food:{{.food | upper | quote}}
release:{{.Release.Name}}
drink:{{.drink | default "tea" | quote}}
food:{{.food | upper | quote}}
release:{{.Release.Name}}
{{- end}}
```
...
...
@@ -20,14 +20,14 @@ In Helm templates, a variable is a named reference to another object. It follows
apiVersion:v1
kind:ConfigMap
metadata:
name:{{.Release.Name}}-configmap
name:{{.Release.Name}}-configmap
data:
myvalue:"HelloWorld"
{{- $relname:= .Release.Name -}}
{{- with .Values.favorite}}
drink:{{.drink | default "tea" | quote}}
food:{{.food | upper | quote}}
release:{{$relname}}
drink:{{.drink | default "tea" | quote}}
food:{{.food | upper | quote}}
release:{{$relname}}
{{- end}}
```
...
...
@@ -53,7 +53,7 @@ Variables are particularly useful in `range` loops. They can be used on list-lik
```yaml
toppings:|-
{{- range $index, $topping := .Values.pizzaToppings }}
{{$index}}: {{$topping}}
{{ $index }}: {{ $topping }}
{{- end }}
```
...
...
@@ -74,11 +74,11 @@ For data structures that have both a key and a value, we can use `range` to get
apiVersion:v1
kind:ConfigMap
metadata:
name:{{.Release.Name}}-configmap
name:{{.Release.Name}}-configmap
data:
myvalue:"HelloWorld"
{{- range $key,$val:= .Values.favorite}}
{{$key}}:{{$val | quote}}
{{$key}}:{{$val | quote}}
{{- end}}
```
...
...
@@ -98,4 +98,4 @@ data:
Variables are not "global". They are scoped to the block in which they are declared. Earlier, we assigned `$relname` in the top level of the template. That variable will be in scope for the entire template. But in our last example, `$key` and `$val` will only be in scope inside of the `{{range...}}{{end}}` block.
So far we have looked at just one template declared in just one file. But one of the powerful features of the Helm template language is its ability to declare multiple templates and use them together. We'll turn to that in the next section.
\ No newline at end of file
So far we have looked at just one template declared in just one file. But one of the powerful features of the Helm template language is its ability to declare multiple templates and use them together. We'll turn to that in the next section.