# 45. Helm 模板之模板函数与管道
上节课我们学习了如何将信息渲染到模板之中，但是这些信息都是直接传入模板引擎中进行渲染的，有的时候我们想要转换一下这些数据才进行渲染，这就需要使用到 Go 模板语言中的一些其他用法。

## 模板函数
比如我们需要从`.Values`中读取的值变成字符串的时候就可以通过调用`quote`模板函数来实现：(templates/configmap.yaml)
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  k8s: {{ quote .Values.course.k8s }}
  python: {{ .Values.course.python }}
```

模板函数遵循调用的语法为：`functionName arg1 arg2...`。在上面的模板文件中，`quote .Values.course.k8s`调用`quote`函数并将后面的值作为一个参数传递给它。最终被渲染为：
```shell
$ helm install --dry-run --debug .
[debug] Created tunnel using local port: '39405'
......
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: masked-saola-configmap
data:
  myvalue: "Hello World"
  k8s: "devops"
  python: django
```

我们可以看到`.Values.course.k8s`被渲染成了字符串`devops`。上节课我们也提到过 Helm 是一种 Go 模板语言，拥有超过60多种可用的内置函数，一部分是由[Go 模板语言](https://godoc.org/text/template)本身定义的，其他大部分都是[`Sprig`模板库](https://godoc.org/github.com/Masterminds/sprig)提供的一部分，我们可以前往这两个文档中查看这些函数的用法。

比如我们这里使用的`quote`函数就是`Sprig 模板库`提供的一种字符串函数，用途就是用双引号将字符串括起来，如果需要双引号`"`，则需要添加`\`来进行转义，而`squote`函数的用途则是用双引号将字符串括起来，而不会对内容进行转义。

所以在我们遇到一些需求的时候，首先要想到的是去查看下上面的两个模板文档中是否提供了对应的模板函数，这些模板函数可以很好的解决我们的需求。


## 管道
模板语言除了提供了丰富的内置函数之外，其另一个强大的功能就是**管道**的概念。和`UNIX`中一样，管道我们通常称为`Pipeline`，是一个链在一起的一系列模板命令的工具，以紧凑地表达一系列转换。简单来说，管道是可以按顺序完成一系列事情的一种方法。比如我们用管道来重写上面的 ConfigMap 模板：（templates/configmap.yaml）
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  k8s: {{ .Values.course.k8s | quote }}
  python: {{ .Values.course.python }}
```

这里我们直接调用`quote`函数，而是调换了一个顺序，使用一个管道符`|`将前面的**参数**发送给后面的**模板函数**：`{{ .Values.course.k8s | quote }}`，使用管道我们可以将几个功能顺序的连接在一起，比如我们希望上面的 ConfigMap 模板中的 k8s 的 value 值被渲染后是大写的字符串，则我们就可以使用管道来修改：（templates/configmap.yaml）
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  k8s: {{ .Values.course.k8s | upper | quote }}
  python: {{ .Values.course.python }}
```

这里我们在管道中增加了一个`upper`函数，该函数同样是[`Sprig 模板库`](https://godoc.org/github.com/Masterminds/sprig)提供的，表示将字符串每一个字母都变成大写。然后我们用`debug`模式来查看下上面的模板最终会被渲染成什么样子：
```shell
$ helm install --dry-run --debug .
[debug] Created tunnel using local port: '46651'
......
---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: maudlin-labradoodle-configmap
data:
  myvalue: "Hello World"
  k8s: "DEVOPS"
  python: django
```

我们可以看到之前我们的`devops`已经被渲染成了`"DEVOPS"`了，要注意的是使用管道操作的时候，前面的操作结果会作为参数传递给后面的模板函数，比如我们这里希望将上面模板中的 python 的值渲染为重复出现3次的字符串，则我们就可以使用到[`Sprig 模板库`](https://godoc.org/github.com/Masterminds/sprig)提供的`repeat`函数，不过该函数需要传入一个参数`repeat COUNT STRING`表示重复的次数：（templates/configmap.yaml）
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  k8s: {{ .Values.course.k8s | upper | quote }}
  python: {{ .Values.course.python | quote | repeat 3 }}
```

该`repeat`函数会将给定的字符串重复3次返回，所以我们将得到这个输出：
```shell
helm install --dry-run --debug .
[debug] Created tunnel using local port: '39712'

......

Error: YAML parse error on mychart/templates/configmap.yaml: error converting YAML to JSON: yaml: line 7: did not find expected key

---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: piquant-butterfly-configmap
data:
  myvalue: "Hello World"
  k8s: "DEVOPS"
  python: "django""django""django"
```

我们可以看到上面的输出中 python 对应的值变成了3个相同的字符串，这显然是不符合我们预期的，我们的预期是形成一个字符串，而现在是3个字符串了，而且上面还有错误信息，根据管道处理的顺序，我们将`quote`函数放到`repeat`函数后面去是不是就可以解决这个问题了：（templates/configmap.yaml）
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: "Hello World"
  k8s: {{ .Values.course.k8s | upper | quote }}
  python: {{ .Values.course.python | repeat 3 | quote }}
```

现在是不是就是先重复3次`.Values.course.python`的值，然后调用`quote`函数：
```shell
helm install --dry-run --debug .
[debug] Created tunnel using local port: '33837'

......

---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: braided-manatee-configmap
data:
  myvalue: "Hello World"
  k8s: "DEVOPS"
  python: "djangodjangodjango"
```

现在是不是就正常了，也得到了我们的预期结果，所以我们在使用管道操作的时候一定要注意是按照从前到后一步一步顺序处理的。

## default 函数
另外一个我们会经常使用的一个函数是`default 函数`：`default DEFAULT_VALUE GIVEN_VALUE`。该函数允许我们在模板内部指定默认值，以防止该值被忽略掉了。比如我们来修改上面的 ConfigMap 的模板：（templates/configmap.yaml）
```yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: {{ .Release.Name }}-configmap
data:
  myvalue: {{ .Values.hello | default  "Hello World" | quote }}
  k8s: {{ .Values.course.k8s | upper | quote }}
  python: {{ .Values.course.python | repeat 5 | quote }}
```

由于我们的`values.yaml`文件中只定义了 course 结构的信息，并没有定义 hello 的值，所以如果没有设置默认值的话是得不到`{{ .Values.hello }}`的值的，这里我们为该值定义了一个默认值：`Hello World`，所以现在如果在`values.yaml`文件中没有定义这个值，则我们也可以得到默认值：
```shell
$ helm install --dry-run --debug .
[debug] Created tunnel using local port: '42670'

......

---
# Source: mychart/templates/configmap.yaml
apiVersion: v1
kind: ConfigMap
metadata:
  name: orbiting-hog-configmap
data:
  myvalue: "Hello World"
  k8s: "DEVOPS"
  python: "djangodjangodjangodjangodjango"
```

我们可以看到`myvalue`值被渲染成了**Hello World**，证明我们的默认值生效了。



---
[点击查看本文视频](https://youdianzhishi.com/course/6n8xd6/)

扫描下面的二维码(或微信搜索`k8s技术圈`)关注我们的微信公众帐号，在微信公众帐号中回复 **加群** 即可加入到我们的 kubernetes 讨论群里面共同学习。

![k8s技术圈二维码](https://www.qikqiak.com/img/posts/qrcode_for_gh_d6dd87b6ceb4_430.jpg)
