@@ -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,
@@ -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`".
@@ -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`:
@@ -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.