github.com/felipejfc/helm@v2.1.2+incompatible/docs/chart_template_guide/named_templates.md (about) 1 # Named Templates 2 3 It is time to move beyond one template, and begin to create others. In this section, we will see how to define _named templates_ in one file, and then use them elsewhere. 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. 4 5 In the "Conditionals and Loops" section we introduced three actions for declaring and managing templates: `define`, `template`, and `block`. In this section, we'll cover those three actions, and also introduce a special-purpose `include` function that works similarly to the `template` action. 6 7 ## Partials and `_` files 8 9 So far, we've used one file, and that one file has contained a single template. But Helm's template language allows you to create named embedded templates, that can be accessed by name elsewhere. 10 11 Before we get to the nuts-and-bolts of writing those templates, there is file naming convention that deserves mention: 12 13 - Most files in `templates/` are treated as if they contain Kubernetes manifests 14 - The `NOTES.txt` is one exception 15 - But files whose name begins with an underscore (`_`) are assumed to _not_ have a manifest inside. The rendered version of these files is not sent to Kubernetes. 16 17 These files are used to store partials and helpers. In fact, when we first created `mychart`, we saw a file called `_helpers.tpl`. That file is the default location for template partials. 18 19 ## Declaring and using templates with `define` and `template` 20 21 The `define` action allows us to create a named template inside of a template file. Its syntax goes like this: 22 23 ```yaml 24 {{ define "MY_NAME" }} 25 # body of template here 26 {{ end }} 27 ``` 28 29 For example, we can define a template to encapsulate a Kubernetes block of labels: 30 31 ```yaml 32 {{- define "my_labels" }} 33 labels: 34 generator: helm 35 date: {{ now | htmlDate }} 36 {{- end }} 37 ``` 38 39 Now we can embed this template inside of our existing ConfigMap, and then include it with the `template` action: 40 41 ``` 42 {{- define "my_labels" }} 43 labels: 44 generator: helm 45 date: {{ now | htmlDate }} 46 {{- end }} 47 apiVersion: v1 48 kind: ConfigMap 49 metadata: 50 name: {{ .Release.Name }}-configmap 51 {{- template "my_labels" }} 52 data: 53 myvalue: "Hello World" 54 {{- range $key, $val := .Values.favorite }} 55 {{ $key }}: {{ $val | quote }} 56 {{- end }} 57 ``` 58 59 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: 60 61 ```yaml 62 # Source: mychart/templates/configmap.yaml 63 apiVersion: v1 64 kind: ConfigMap 65 metadata: 66 name: running-panda-configmap 67 labels: 68 generator: helm 69 date: 2016-11-02 70 data: 71 myvalue: "Hello World" 72 drink: "coffee" 73 food: "pizza" 74 ``` 75 76 Conventionally, Helm charts put these templates inside of a partials file, usually `_helpers.tpl`. Let's move this function there: 77 78 ```yaml 79 {{/* Generate basic labels */}} 80 {{- define "my_labels" }} 81 labels: 82 generator: helm 83 date: {{ now | htmlDate }} 84 {{- end }} 85 ``` 86 87 By convention, `define` functions should have a simple documentation block (`{{/* ... */}}`) describing what they do. 88 89 Even though this definition is in `_helpers.tpl`, it can still be accessed in `configmap.yaml`: 90 91 ```yaml 92 apiVersion: v1 93 kind: ConfigMap 94 metadata: 95 name: {{ .Release.Name }}-configmap 96 {{- template "my_labels" }} 97 data: 98 myvalue: "Hello World" 99 {{- range $key, $val := .Values.favorite }} 100 {{ $key }}: {{ $val | quote }} 101 {{- end }} 102 ``` 103 104 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. 105 106 One popular naming convention is to prefix each defined template with the name of the chart: `{{ define "mychart.labels" }}` or `{{ define "mychart_labels" }}`. 107 108 ## Setting the scope of a template 109 110 In the template we defined above, we did not use any objects. We just used functions. Let's modify our defined template to include the chart name and chart version: 111 112 ```yaml 113 {{/* Generate basic labels */}} 114 {{- define "my_labels" }} 115 labels: 116 generator: helm 117 date: {{ now | htmlDate }} 118 chart: {{ .Chart.Name }} 119 version: {{ .Chart.Version }} 120 {{- end }} 121 ``` 122 123 If we render this, the result will not be what we expect: 124 125 ```yaml 126 # Source: mychart/templates/configmap.yaml 127 apiVersion: v1 128 kind: ConfigMap 129 metadata: 130 name: moldy-jaguar-configmap 131 labels: 132 generator: helm 133 date: 2016-11-02 134 chart: 135 version: 136 ``` 137 138 What happened to the name and version? They weren't in the scope for our defined template. When a named template (created with `define`) is rendered, it will receive the scope passed in by the `template` call. In our example, we included the template like this: 139 140 ``` 141 {{- template "my_labels" }} 142 ``` 143 144 No scope was passed in, so within the template we cannot access anything in `.`. This is easy enough to fix, though. We simply pass a scope to the template: 145 146 ```yaml 147 apiVersion: v1 148 kind: ConfigMap 149 metadata: 150 name: {{ .Release.Name }}-configmap 151 {{- template "my_labels" . }} 152 ``` 153 154 Note that we pass `.` at the end of the `template` call. We could just as easily pass `.Values` or `.Values.favorite` or whatever scope we want. But what we want is the top-level scope. 155 156 Now when we execute this template with `helm install --dry-run --debug ./mychart`, we get this: 157 158 ```yaml 159 # Source: mychart/templates/configmap.yaml 160 apiVersion: v1 161 kind: ConfigMap 162 metadata: 163 name: plinking-anaco-configmap 164 labels: 165 generator: helm 166 date: 2016-11-02 167 chart: mychart 168 version: 0.1.0 169 ``` 170 171 Now `{{ .Chart.Name }}` resolves to `mychart`, and `{{ .Chart.Version }}` resolves to `0.1.0`. 172 173 ## Creating override-able sections with `block` 174 175 Say we want to create a template in our `_helpers.tpl` file, but then override part of its behavior in our template. This is what blocks are for. Sometimes we don't want to just insert a template with `template`, but we want to sketch out a default and let another template override our default. This makes it possible for one chart to define a base template, but allow another chart to strategically override some of its behavior. 176 177 Blocks are declared like this: 178 179 ```yaml 180 {{ block "NAME" PIPELINE }} 181 {{ end }} 182 ``` 183 184 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. 185 186 Let's start with `_helpers.tpl`: 187 188 ``` 189 {{- define "my_labels" }} 190 labels: 191 chart: {{ .Chart.Name }} 192 version: {{ .Chart.Version }} 193 {{ block "my_extra_labels" . }}extras: false{{ end }} 194 {{- end `u}} 195 ``` 196 197 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: 198 199 ```yaml 200 # Source: mychart/templates/configmap.yaml 201 apiVersion: v1 202 kind: ConfigMap 203 metadata: 204 name: tinseled-womba-configmap 205 labels: 206 chart: mychart 207 version: 0.1.0 208 extras: false 209 data: 210 myvalue: "Hello World" 211 drink: "coffee" 212 food: "pizza" 213 ``` 214 215 But inside of our `configmap.yaml` template, we can override `my_extra_labels`: 216 217 ```yaml 218 {{- define "my_extra_labels" }}chart: {{ .Chart.Name }}{{ end -}} 219 apiVersion: v1 220 kind: ConfigMap 221 metadata: 222 name: {{ .Release.Name }}-configmap 223 {{- template "my_labels" . }} 224 data: 225 myvalue: "Hello World" 226 {{- range $key, $val := .Values.favorite }} 227 {{ $key }}: {{ $val | quote }} 228 {{- end }} 229 ``` 230 231 On the first line, we redefine `my_extra_labels` to include `chart: {{ .Chart.Name }}`. If we 232 run this, we will get: 233 234 ```yaml 235 # Source: mychart/templates/configmap.yaml 236 apiVersion: v1 237 kind: ConfigMap 238 metadata: 239 name: ignorant-scorp-configmap 240 labels: 241 chart: mychart 242 version: 0.1.0 243 chart: mychart 244 data: 245 myvalue: "Hello World" 246 drink: "coffee" 247 food: "pizza" 248 ``` 249 250 Gone is the `extras: false` section, since that part of the template is now overridden by our new template, which placed `chart: mychart` into the output. 251 252 Blocks are not frequently used in Helm charts. But they do provide one mechanism for creating "abstract" charts, and then selectively overriding parts of the abstract template with concrete implementations. 253 254 ## The `include` function 255 256 Say we've defined a simple template that looks like this: 257 258 ``` 259 {{- define "mychart_app" -}} 260 app_name: {{ .Chart.Name }} 261 app_version: "{{ .Chart.Version} }+{{ .Release.Time.Seconds }}" 262 {{- end -}} 263 ``` 264 265 Now say I want to insert this both into the `labels:` section of my template, and also the `data:` section: 266 267 ```yaml 268 apiVersion: v1 269 kind: ConfigMap 270 metadata: 271 name: {{ .Release.Name }}-configmap 272 labels: 273 {{ template "mychart_app" .}} 274 data: 275 myvalue: "Hello World" 276 {{- range $key, $val := .Values.favorite }} 277 {{ $key }}: {{ $val | quote }} 278 {{- end }} 279 {{ template "mychart_app" . }} 280 281 ``` 282 283 The output will not be what we expect: 284 285 ```yaml 286 # Source: mychart/templates/configmap.yaml 287 apiVersion: v1 288 kind: ConfigMap 289 metadata: 290 name: measly-whippet-configmap 291 labels: 292 app_name: mychart 293 app_version: "0.1.0+1478129847" 294 data: 295 myvalue: "Hello World" 296 drink: "coffee" 297 food: "pizza" 298 app_name: mychart 299 app_version: "0.1.0+1478129847" 300 ``` 301 302 Note that the indentation on `app_version` is wrong in both places. Why? Because the template that is substituted in has the text aligned to the right. Because `template` is an action, and not a function, there is no way to pass the output of a `template` call to other functions; the data is simply inserted inline. 303 304 To work around this case, Helm provides an alternative to `template` that will import the contents of a template into the present pipeline where it can be passed along to other functions in the pipeline. 305 306 Here's the example above, corrected to use `indent` to indent the `mychart_app` template correctly: 307 308 ```yaml 309 apiVersion: v1 310 kind: ConfigMap 311 metadata: 312 name: {{ .Release.Name }}-configmap 313 labels: 314 {{ include "mychart_app" . | indent 4 }} 315 data: 316 myvalue: "Hello World" 317 {{- range $key, $val := .Values.favorite }} 318 {{ $key }}: {{ $val | quote }} 319 {{- end }} 320 {{ include "mychart_app" . | indent 2 }} 321 ``` 322 323 Now the produced YAML is correctly indented for each section: 324 325 ```yaml 326 # Source: mychart/templates/configmap.yaml 327 apiVersion: v1 328 kind: ConfigMap 329 metadata: 330 name: edgy-mole-configmap 331 labels: 332 app_name: mychart 333 app_version: "0.1.0+1478129987" 334 data: 335 myvalue: "Hello World" 336 drink: "coffee" 337 food: "pizza" 338 app_name: mychart 339 app_version: "0.1.0+1478129987" 340 ``` 341 342 > 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. 343 344 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.