- 官方文档: 流控制
流控制
控制结构(在模板语言中称为 “actions”)提供给你和模板控制模板迭代的流程能力。Helm 的模板语言提供了以下控制结构:
if/else
: 用来创建条件语句with
: 用来制定范围range
: 用来提供for each
类型的循环
除了这些,还提供了一些声明和使用命名模板的关键字:
- define: 在模板中声明一个新的命名模板
- template: 导入一个命名模板
- block: 声明一种特殊的可填充的模板块
这里我们会讨论关于 if, with 和 range。其他部分会在后续章节说明。
If/Else
第一个控制结构是在按照条件在一个模板中包含一个块文本。即 if/else
块。
基本的条件结构看起来类似这样
1
2
3
4
5
6
7{{ if PIPELINE }}
# Do something
{{ else if OTHER PIPELINE }}
# Do something else
{{ else }}
# Default case
{{ end }}注意上面的条件是 PIPELINE 而不是值。这是因为控制结构可以执行整个 PIPELINE,而不仅仅是计算一个值。
如果是以下值时,PIPELINE 会被设置为 false:
- 布尔 false
- 数字 0
- 空字符串
- nil(空或者 null)
- 空集合(map,slice,tuple,dict,array)
在所有其他条件下,条件都为true。
让我们先在配置映射中添加一个简单的条件。如果饮品是 coffee 则会添加另一个配置:
1
2
3
4
5
6
7
8
9apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }}
{{ if eq .Values.favorite.frink "coffee" }}mug: "true"{{ end }}我们在 values.yaml 文件中将
drink: coffee
注释,这样输出中就不会包含mug: "true"
标识,但如果将这行添加到 values.yaml 文件中,输入就会是这样:1
2
3
4
5
6
7
8
9
10# Source: helm-test/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: helm-test-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: "true"
控制空格
查看条件时,我们需要快速了解一下模板中控制空白的方式,格式化之前的例子,使其更易于阅读:
1
2
3
4
5
6
7
8
9
10
11apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }}
{{ if eq .Values.favorite.drink "coffee" }}
mug: "true"
{{ end }}初始情况下,看起来没问题。但是如果通过模板引擎运行时,我们将得到一个不幸的结果:
1
Error: YAML parse error on helm-test/templates/configmap.yaml: error converting YAML to JSON: yaml: line 9: did not find expected key
这是因为空格导致生成了错误的YAML。
1
2
3
4
5
6
7
8
9
10# Source: helm-test/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: helm-test-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: "true"mug的缩进是不对的。取消缩进重新执行一下:
1
2
3
4
5
6
7
8
9
10
11apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }}
{{ if eq .Values.favorite.drink "coffee" }}
mug: "true"
{{ end }}这样就得到了合法的 YAML,但是看起来还是有点问题
1
2
3
4
5
6
7
8
9
10
11# Source: helm-test/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: helm-test-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: "true"注意,在 mug 与 food 之间多了一个空行,这是为什么?因为当模板引擎运行时,它移除了 {{ 和 }} 里面的内容,但是留下的空白完全保持原样。
YAML 认为空白是有意义的,因此管理空白变得很重要。幸运的是,Helm 模板有些工具可以处理此类问题。
首先,模板声明的大括号语法可以通过特殊的字符修改,并通知模板引擎取消空白。{{- (包括添加的横杠和空格)表示向左删除空白, 而 -}} 表示右边的空格应该被去掉。一定注意空格就是换行。要确保
-
和其他命令之间有一个空格。 {{- 3 }} 表示 “删除左边空格并打印 3”,而 {{-3 }} 表示 “打印 -3”。使用这个语法,我们就可修改我们的模板,去掉新加的空白行:
1
2
3
4
5
6
7
8
9
10
11apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
drink: {{ .Values.favorite.drink | default "tea" | quote }}
food: {{ .Values.favorite.food | upper | quote }}
{{- if eq .Values.favorite.drink "coffee" }}
mug: "true"
{{- end }}可以通过 Helm 运行模板并查看结果:
1
2
3
4
5
6
7
8
9
10# Source: helm-test/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
name: helm-test-configmap
data:
myvalue: "Hello World"
drink: "coffee"
food: "PIZZA"
mug: "true"最终,有时这更容易告诉模板系统如何缩进,而不是试图控制模板指令间的间距。因此,您有时会发现使用
indent
方法 ({{ indent 2 "mug:true" }}) 会很有用。
使用 with 修改范围
下一个控制结构是 with
操作。这个用来控制变量范围。回想一下,.
是对当前作用域的引用。因此 .Values
就是告诉模板在当前作用域查找 Values 对象。
with 的语法与 if 语句类似:
1 | {{ with PIPELINE }} |
作用域可以被改变。with 允许你为特定对象设定当前作用域(.)。
比如,我们已经在使用
.Values.favorite
。 修改配置映射中的.
的作用域指向.Values.favorite
:1
2
3
4
5
6
7
8
9
10apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
{{- end }}注意现在我们可以引用
.drink
和.food
了,而不必限定他们。因为 with 语句设置了.
指向.Values.favorite
。.
被重置为 {{ end }} 之后的上一个作用域。但是这里有个注意事项,在限定的作用域内,无法使用.访问父作用域的对象。错误示例如下:
1
2
3
4
5{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
release: {{ .Release.Name }}
{{- end }}这样会报错因为
Release.Name
不在.
限定的作用域内。但是如果对调最后两行就是正常的, 因为在 {{ end }} 之后作用域被重置了。1
2
3
4
5{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
{{- end }}
release: {{ .Release.Name }}或者,我们可以使用
$
从父作用域中访问Release.Name
对象。当模板开始执行后$
会被映射到根作用域,且执行过程中不会更改。下面这种方式也可以正常工作:1
2
3
4
5{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
release: {{ $.Release.Name }}
{{- end }}
使用 range 操作循环
很多编程语言支持使用 for 循环,foreach 循环,或者类似的方法机制。在 Helm 的模板语言中,在一个集合中迭代的方式是使用 range 操作符。
开始之前,我们先在
values.yaml
文件添加一个披萨的配料列表:1
2
3
4
5
6
7
8favorite:
drink: coffee
food: pizza
pizzaToppings:
- mushrooms
- cheese
- peppers
- onions现在我们有了一个 pizzaToppings 列表(模板中称为切片)。修改模板把这个列表打印到配置映射中:
1
2
3
4
5
6
7
8
9
10
11
12
13
14apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
{{- end }}
toppings: |-
{{- range .Values.pizzaToppings }}
- {{ . | title | quote }}
{{- end }}我们也可以使用
$
从父作用域访问Values.pizzaToppings
列表。当模板开始执行后$
会被映射到根作用域,且执行过程中不会更改。下面这种方式也可以正常工作:1
2
3
4
5
6
7
8
9
10
11
12
13
14apiVersion: v1
kind: ConfigMap
metadata:
name: {{ .Release.Name }}-configmap
data:
myvalue: "Hello World"
{{- with .Values.favorite }}
drink: {{ .drink | default "tea" | quote }}
food: {{ .food | upper | quote }}
toppings: |-
{{- range $.Values.pizzaToppings }}
- {{ . | title | quote }}
{{- end }}
{{- end }}
让我们仔细看看 toppings: 列表。range 方法“涵盖”(迭代)pizzaToppings 列表。但现在发生了有意思的事情。 就像 with 设置了 .
的作用域,range 操作符也做了同样的事。每一次循环,.
都会设置为当前的披萨配料。也就是说,第一次 .
设置成了mushrooms,第二次迭代设置成了 cheese,等等。
我们可以直接发送.的值给管道,因此当我们执行 {{ . | title | quote }} 时,它会发送 . 到 title 然后发送到 quote。 如果执行这个模板,输出是这样的:
1 | # Source: helm-test/templates/configmap.yaml |
toppings: |-
行是声明的多行字符串。所以这个配料列表实际上不是 YAML 列表,是个大字符串。为什么要这样做?因为在配置映射 data 中的数据是由键值对组成,key和value都是简单的字符串。
有时能在模板中快速创建列表然后迭代很有用,Helm 模板的 tuple 可以很容易实现该功能。在计算机科学中,元组表示一个有固定大小的类似列表的集合,但可以是任意数据类型。这大致表达了tuple的用法。
1 | sizes: |- |
上述模板会生成以下内容
1 | sizes: |- |
除了列表和元组,range 可被用于迭代有键值对的集合(像 map 或 dict)。我们会在下一部分介绍模板变量是看到它是如何应用的。