2.10.4 逻辑控制
控制结构允许用户编写更加复杂的逻辑结构,进而让用户更加方便地控制输出结果。Helm提供如下的逻辑控制语言。
·if/else:负责条件控制。
·with:指定调用范围。
·range:负责循环。
1.if/else语句
我们先来学习条件控制器,条件控制语言编写形态如下所示。
{{ if PIPELINE }} # Do something {{ else if OTHER PIPELINE }} # Do something else {{ else }} # Default case {{ end }}
if语句被判断为false的条件如下所示。
·一个布尔false。
·数值0。
·空字符串。
·nil。
·一个空的容器(map、slice、tuple、dict、array)。
其他情况下,if语句会被判定为true,然后执行对应的块逻辑。我们想让.Values.favorite.drink为coffee时需要增加输出内容。下面我们修改一下ConfigMap,来增加条件控制器。
apiVersion: 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 and .Values.favorite.drink (eq .Values.favorite.drink "coffee") }}mug: true{{ end }}
这里的eq就是比较字符串是否相等的操作符。需要注意的是,.Values.favorite.drink必须有值,否则运行时就会报错。我们修改values.yaml将drink:coffee写入,这样configmap中就会增加一个mug:true字段,运行效果如下。
apiVersion: v1 kind: ConfigMap metadata: name: eating-cow-configmap data: myvalue: "Hello World" drink: "coffee" food: "PIZZA" mug: true
2.管理空格
在继续介绍其他控制语言之前,先插入一个关于模板语言中空格处理的知识。我们沿用上面的模板并稍微修改一下格式。
apiVersion: 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}}
看起来格式还挺简单清晰的,运行一下看看。
Error: YAML parse error on myChart/templates/configmap.yaml: error converting YAML to JSON: yaml: line 9: did not find expected key # Source: myChart/templates/configmap.yaml apiVersion: v1 kind: ConfigMap metadata: name: bailing-poodle-configmap data: myvalue: "Hello World" drink: "coffee" food: "PIZZA" mug: true
可以看到程序输出结果首先报了个错误,然后输出的格式也不是我们期望的那样简单清晰。这代表mug的缩进是不正确的,下面把它向左侧进行缩进。
apiVersion: 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}}
修改完成后运行再看一下输出结果。
apiVersion: v1 kind: ConfigMap metadata: name: vociferous-lamb-configmap data: myvalue: "Hello World" drink: "coffee" food: "PIZZA" mug: true
这样看来,mug向左侧缩进是正确的,但是似乎多出来了一个空行。这是为什么呢?这是因为当模板引擎运行时,它会将{{}}内的内容都移除,但是其中的空行还会留下。
yaml是换行敏感的,因此处理好换行符是非常重要的事。幸运的是,Helm模板提供了一些工具去处理这样的问题。
首先在编写大括号的时候,声明需要模板引擎帮助去掉这个换行符。{{--}}这样的写法就是告诉模板引擎处理完里面的控制逻辑后,要去除对应的换行符。下面我们修改一下上面的例子。
apiVersion: 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}}
请注意,这里只针对左侧声明了清理空行,结果的括号并没有声明清除空行。先看看如下所示的输出格式。
apiVersion: v1 kind: ConfigMap metadata: name: jumpy-alpaca-configmap data: myvalue: "Hello World" drink: "coffee" food: "PIZZA" mug: true
可以看到输出格式符合我们期望。注意这里仅对左侧进行了声明,很多时候可能会弄错写成下面的格式。
food: {{ .Values.favorite.food | upper | quote }} {{- if eq .Values.favorite.drink "coffee" -}} mug: true {{- end -}}
这样的输出格式会变成:food:"PIZZA"mug:true,这是因为两边的空行都被消除了。
Helm模板语言也提供了缩进函数,你可以简单告诉模板引擎需要缩进多少,写法类似{{indent 2"mug:true"}}。
3.范围控制器with
下面介绍范围控制器with,它能控制变量的使用范围。我们先回想一下“.”代表当前Chart范围,因此.Values表示寻找当前Chart下的values。with的使用方式和if语句类似。
{{ with PIPELINE }} # restricted scope {{ end }}
with的范围是可以改变的,with函数允许将“.”指定为特殊的对象引用。例如我们使用过.Values.favorites,所以这里重写一下ConfigMap,让“.”直接指向.Values.favorites。
apiVersion: 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,不需要在前面添加.Values关键字,在{{-end}}关键字后,“.”又会恢复到原来的指向范围。
但是有一点需要注意,那就是在受限区域,我们没有办法直接获取当前对象的父节点的信息,举个例子:
{{- with .Values.favorite }} drink: {{ .drink | default "tea" | quote }} food: {{ .food | upper | quote }} release: {{ .Release.Name }} {{- end }}
这个模板在运行时就会报错,因为Release.Name并不在当前对象中。但如果我们把它放到end模板范围外,它就可以正常工作了。
{{- with .Values.favorite }} drink: {{ .drink | default "tea" | quote }} food: {{ .food | upper | quote }} {{- end }} release: {{ .Release.Name }}
4.使用range实现循环功能
许多编程语言提供for、foreach循环功能。在Helm模板语言中,循环功能需要使用range关键字。
为了演示这个功能,我们需要先修改一下values.yaml的内容。
favorite: drink: coffee food: pizza pizzaToppings: - mushrooms - cheese - peppers - onions
现在我们有了一个pizzaToppings列表,它在Helm模板语言中叫作slice。下面我们修改ConfigMap来打印这些信息。
apiVersion: 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 }}
仔细看toppings列表,range函数会循环读取Values.pizzaToppings。这里有一个比较有意思的地方,那就是“.”,它和with关键词很相似,列表中的每个元素都被赋值给了“.”。换句话说,“.”第一次的值是mushrooms,第二次的值是cheese,以此类推。
我们也能给“.”继续使用管道方法,{{.|title|quote}}就是把参数传递给了title函数,这个函数会输出标题格式的字符串,上面这个ConfigMap输出内容如下。
apiVersion: v1 kind: ConfigMap metadata: name: edgy-dragonfly-configmap data: myvalue: "Hello World" drink: "coffee" food: "PIZZA" toppings: |- - "Mushrooms" - "Cheese" - "Peppers" - "Onions"
在上面的yaml中有一点比较奇怪,即toppings:|-,这个是yaml的语法,代表一个多行的字符串。Helm提供了一个函数list,可以很方便地将一个字符串变成一个列表,然后依次将它们遍历出来。
sizes: |- {{- range list "small" "medium" "large" }} - {{ . }} {{- end }}
输出结果如下。
sizes: |- - small - medium - large