云原生应用管理:原理与实践
上QQ阅读APP看书,第一时间看更新

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