consul-template解析以Go Template格式编写的文件,如果您不熟悉语法,请阅读Go的文档和示例。除Go提供的模板功能外,Consul Template还提供以下功能。

1. API函数

API函数与远程API调用交互,与Consul和Vault等外部服务进行通信。

datacenters

查询Consul其中所有数据中心。

1
{{ datacenter }}

例如:

1
2
{{ range datacenters }}
{{ . }}{{ end }}

结果:

dc1
dc2

可以指定一个可选的布尔值,指示Consul Template忽略无法访问或没有当前leader的数据中心。启用此选项需要执行O(N + 1)操作,因此不建议在性能较高的环境中使用。

1
2
// Ignores datacenters which are inaccessible
{{ datacenters true }}

file

读取并输出磁盘上本地文件的内容。如果无法读取文件,则会发生错误。当文件更改时,Consul Template将获取更改并重新渲染模板。

1
{{ file "" }}

例如:

1
{{ file "/path/to/my/file" }}

结果:

file contents

这不会处理嵌套模板。有关呈现嵌套模板的方法,请参阅executeTemplate。

key

查询Consul获取给定键路径的值。如果密钥不存在,Consul Template将停止渲染,直到键值存在。要避免阻塞,请使用keyOrDefaultkeyExists

1
{{ key "@" }}

例如:

1
{{ key "service/redis/maxconns" }}

结果:

15

keyExists

查询Consul获取给定键路径的值。如果密钥存在,则返回true,否则返回false。与key不同,如果key不存在,则此功能不会阻塞。这对于控制流程很有用。

1
{{ keyExists "@" }}

属性是可选的;如果省略,则使用本地数据中心(后面其他参数若无特殊说明,则同理)。

例如:

1
2
3
4
5
{{ if keyExists "app/beta_active" }}
 # ...
{{ else }}
 # ...
{{ end }}

keyOrDefault

查询Consul获取给定键路径的值。如果key不存在,则将使用默认值。与key不同,如果键值不存在,则此功能不会阻塞流程。

1
{{ keyOrDefault "@" "" }}

例如:

1
{{ keyOrDefault "service/redis/maxconns" "5" }}

结果:

5

请注意,Consul Template使用多阶段执行。在评估的第一阶段,Consul Template将没有来自Consul的数据,因此将始终回退到默认值。从Consul的后续读取将在下一个模板传递中从Consul(如果key存在)中提取实际值。这很重要,因为这意味着由于keyOrDefault中缺少键值,Consul Template永远不会“阻塞”模板的渲染。即使key存在,如果Consul尚未返回key的数据,也将使用默认值。

ls

查询给定键路径上所有顶级键值对

1
{{ ls "@" }}

例如:

1
2
{{ range ls "service/redis" }}
{{ .Key }}:{{ .Value }}{{ end }}
maxconns:15
minconns:5

node

查询consul节点

1
{{node "@"}}

属性是可选的;如果省略,则使用本地agent节点。

例如:

1
2
{{ with node }}
{{ .Node.Address }}{{ end }}

结果:

10.5.2.6

查询一个同的节点

1
2
{{ with node "node1@dc2" }}
{{ .Node.Address }}{{ end }}

结果:

10.4.2.6

要访问TaggedAddressesMeta等地图数据,请使用Go’s text/template 地图索引。

nodes

查询所有consul节点

1
{{ nodes "@~" }}

属性是可选的;如果省略,则以词法顺序指定结果。如果提供了节点名称,则结果按照提供的节点的最短往返时间排序。如果提供_agent,则结果按照到本地代理的最短往返时间排序。

例如:

1
2
{{ range nodes }}
{{ .Address }}{{ end }}

结果:

10.4.2.13
10.46.2.5

要按最短的响应时间查询获取不同的数据中心:

1
2
{{ range nodes "@dc2~_agent" }}
{{ .Address }}{{ end }}

secret

查询给定路径上的密码。

1
{{ secret "" "" }}

属性是可选的;如果省略,请求将是一个密码vault read(HTTP GET)请求。如果提供,请求将是vault write(HTTP PUT / POST)请求。

例如:

1
2
{{ with secret "secret/passwords" }}
{{ .Data.wifi }}{{ end }}

结果:

FORWARDSoneword

要访问版本化的密码值(对于K / V版本2后端):

1
2
{{ with secret "secret/passwords?version=1" }}
{{ .Data.data.wifi }}{{ end }}

省略?version参数时,将获取最新版本的密码。引用密码值时请注意嵌套的.Data.data语法。有关使用K/V v2后端的详细信息,请参阅Vault文档。

使用Vault版本0.10.0 / 0.10.1时,密码路径必须以“data”为前缀,即上述示例的secret/data/passwords。对于0.10.1之后的Vault版本,这不是必需的,因为consul-template将检测正在使用的KV后端版本。版本2 KV后端在0.10.0之前不存在,因此这些是唯一受影响的版本。

使用write生成PKI证书的示例:

1
2
{{ with secret "pki/issue/my-domain-dot-com" "common_name=foo.example.com" }}
{{ .Data.certificate }}{{ end }}

参数必须是key = value对,并且每对必须是函数的自己的参数:

请始终考虑在磁盘上以纯文本形式保密的内容的安全隐患。如果***者能够访问该文件,他们将可以访问纯文本机密。

请注意,Vault不支持阻塞查询。因此,如果密码被更改,Consul Template将不会立即重新加载,就像Consul的键值存储一样。 Consul Template将在原始密码的租约期限的一半时间内获取新密码。例如,Vault的通用密码后端中的大多数项目都有默认的30天租约。这意味着Consul Template将每15天更新一次密码。因此,建议在生成初始密钥时使用较小的租约期限,以迫使Consul Template更频繁地续订。

在使用将与Vault交互的模板时,还要考虑启用error_on_missing_key。默认情况下,Consul Template使用Go的模板语言。访问不存在的结构字段或映射键时,默认为打印""。这可能不是理想的行为,尤其是在处理密码或其他数据时。因此,建议您设置:

template {
 error_on_missing_key = true
}

您还可以使用ifwith block来防止空值。

1
2
3
4
5
{{ with secret "secret/foo"}}
{{ if .Data.password }}
password = "{{ .Data.password }}"
{{ end }}
{{ end }}

secrets

查询Vault以获取给定路径上的密码列表。并非所有节点都支持列表。

1
{{ secrets "" }}

例如:

1
2
{{ range secrets "secret/" }}
{{ . }}{{ end }}

结果:

bar
foo
zip

要迭代并列出Vault中通用密码后端中的每个密码:

1
2
3
4
{{ range secrets "secret/" }}
{{ with secret (printf "secret/%s" .) }}{{ range $k, $v := .Data }}
{{ $k }}: {{ $v }}
{{ end }}{{ end }}{{ end }}

你可能永远不会这样做。

另请注意,Vault不支持阻塞查询。要了解其含义,请阅读secret末尾的注释。

service

根据健康状况查询服务

1
{{ service ".@~|" }}

属性是可选的;如果省略,将查询所有节点。

属性是可选的;如果省略,则仅返回健康服务。提供过滤器允许客户端过滤服务。

上面的示例是在“east-aws”数据中心查询Consul的健康“web”服务。tag和datacenter属性是可选的。要查询当前数据中心的“Web”服务的所有节点(无论标签如何):

1
2
{{ range service "web" }}
server {{ .Name }}{{ .Address }}:{{ .Port }}{{ end }}

结果,使用名为“web”的逻辑服务呈现所有健康节点的IP地址::

server web01 10.5.2.45:2492
server web02 10.2.6.61:2904

默认情况下,仅返回健康服务。要列出所有服务,请传递“any”过滤器,这将返回注册到代理的所有服务,无论其状态如何。:

1
{{ service "web|any" }}

要按特定的运行状况集过滤服务,请指定以逗号分隔的运行状况列表:

1
{{ service "web|passing,warning" }}

这将根据Consul中定义的节点和服务级别检查返回被视为“通过”或“警告”的服务。请注意,逗号表示“或”,而不是“和”。

注意:以下内容存在架构差异:

1
2
{{ service "web" }}
{{ service "web|passing" }}

前者将返回Consul认为“健康”的所有服务并通过。后者将返回在Consul代理注册的所有服务并执行客户端的过滤。作为一般规则,如果您只想要健康的服务,请不要单独使用passing参数,只需要省略第二个参数即可。

services

查询所有的服务

1
{{ services "@" }}

例如:

1
2
{{ range services }}
{{ .Name }}: {{ .Tags | join "," }}{{ end }}

结果:

node01 tag1,tag2,tag3

tree

查询指定路径键值对

1
{{ tree "@" }}

例如:

1
2
{{ range tree "service/redis" }}
{{ .Key }}:{{ .Value }}{{ end }}

结果:

minconns 2
maxconns 12
nested/config/value "value"

不像lstree返回带前缀的所有键值,就像Unix的tree命令。

2. Scratch暂存器

暂存器在模板的上下文中可用于存储临时数据或计算。 Scratch数据不在模板之间共享,也不在调用之间缓存。

scratch.Key

如果数据存在于命名键的暂存器中,则返回布尔值。即使该键的数据为“nil”,仍然会返回true。

1
{{ scratch.Key "foo" }}

scratch.Get

返回指定键的暂存器中的值。如果数据不存在,则返回“nil”。

1
{{ scratch.Get "foo" }}

scratch.Set

将给定值保存在给定键上。如果该值已存在,则会被覆盖。

1
{{ scratch.Set "foo" "bar" }}

scratch.SetX

此行为与Set完全相同,但如果值已存在则不会覆盖。

1
{{ scratch.SetX "foo" "bar" }}

scratch.MapSet

将值保存在映射的命名键中。如果该值已存在,则会被覆盖。

1
{{ scratch.MapSet "vars" "foo" "bar" }}

scratch.MapSetX

此行为与MapSet完全相同,但如果值已存在则不会覆盖。

1
{{ scratch.MapSetX "vars" "foo" "bar" }}

scratch.MapValues

返回指定映射中所有值的排序列表(按键)。

1
{{ scratch.MapValues "vars" }}

3. Helper Functions辅助函数

与API函数不同,辅助函数不查询远程服务。这些函数对于解析数据,格式化数据,执行数学等非常有用。

base64Decode

接受base64编码的字符串并返回解码的结果,如果给定的字符串不是有效的base64字符串,则返回错误。

1
{{ base64Decode "aGVsbG8=" }}

结果:

hello

base64Encode

接受一个字符串并返回一个base64编码的字符串。

1
{{ base64Encode "hello" }}

结果:

aGVsbG8=

base64URLDecode

接受base64编码的URL安全字符串并返回解码结果,如果给定字符串不是有效的base64 URL安全字符串,则返回错误。

1
{{ base64URLDecode "aGVsbG8=" }}

结果:

hello

base64URLEncode

接受一个字符串并返回base-64编码的URL安全字符串。

1
{{ base64Encode "hello" }}

结果:

aGVsbG8=

byKey

接受tree调用返回的对的列表,并创建一个按顶级目录对进行分组的映射。

例如此目录列表:

groups/elasticsearch/es1
groups/elasticsearch/es2
groups/elasticsearch/es3
services/elasticsearch/check_elasticsearch
services/elasticsearch/check_indexes

使用以下模板

1
2
3
{{ range $key, $pairs := tree "groups" | byKey }}{{ $key }}:
{{ range $pair := $pairs }}  {{ .Key }}={{ .Value }}
{{ end }}{{ end }}

结果:

elasticsearch:
 es1=1
 es2=1
 es3=1

请注意,最顶层的键是从Key值中删除的。剥离后没有前缀的键将从列表中删除。

结果键值对被键控为一个映射,因此可以按键查找单个值:

1
2
3
4
{{ $weights := tree "weights" }}
{{ range service "release.web" }}
 {{ $weight := or (index $weights .Node) 100 }}
 server {{ .Node }} {{ .Address }}:{{ .Port }} weight {{ $weight }}{{ end }}

byTag

获取serviceservices函数返回的服务列表,并创建一个按标签对服务进行分组的映射

1
2
3
{{ range $tag, $services := service "web" | byTag }}{{ $tag }}
{{ range $services }} server {{ .Name }} {{ .Address }}:{{ .Port }}
{{ end }}{{ end }}

contains

确定是否在可迭代元素内。

1
2
3
{{ if .Tags | contains "production" }}
# ...
{{ end }}

containsAll

如果所有值都在可迭代元素内,则返回true,否则返回false。如果值列表为空,则返回true。

1
2
3
{{ if containsAll $requiredTags .Tags }}
# ...
{{ end }}

containsAny

如果任意一个值在可迭代元素内,则返回true,否则返回false。如果值列表为空,则返回false。

1
2
3
{{ if containsAny $acceptableTags .Tags }}
# ...
{{ end }}

containsNone

如果没有值在可迭代元素内,则返回true,否则返回false。如果值列表为空,则返回true。

1
2
3
{{ if containsNone $forbiddenTags .Tags }}
# ...
{{ end }}

containsNotAll

如果某些值不在可迭代元素内,则返回true,否则返回false。如果值列表为空,则返回false。

1
2
3
{{ if containsNotAll $excludingTags .Tags }}
# ...
{{ end }}

env

读取当前进程可访问的给定环境变量。

1
{{ env "CLUSTER_ID" }}

可以链接此函数来操作输出:

1
{{ env "CLUSTER_ID" | toLower }}

例如,读取给定的环境变量,如果它不存在或为空,则使用默认值12345:

1
{{ or (env "CLUSTER_ID") "12345" }}

executeTemplate

执行并返回已定义的模板。

1
2
3
4
5
6
7
8
9
10
11
12
13
{{ define "custom" }}my custom template{{ end }}

This is my other template:
{{ executeTemplate "custom" }}

And I can call it multiple times:
{{ executeTemplate "custom" }}

Even with a new context:
{{ executeTemplate "custom" 42 }}

Or save it to a variable:
{{ $var := executeTemplate "custom" }}

explode

treels调用获取结果并将其转换为深度嵌套的映射以进行解析/遍历。

1
{{ tree "config" | explode }}

注意:在失败后,您将丢失有关键值对的所有元数据。但还可以访问深层嵌套值:

1
2
{{ with tree "config" | explode }}
{{ .a.b.c }}{{ end }}

indent

通过在每行前面添加N个空格来缩进文本块。

1
{{ tree "foo" | explode | toYAML | indent 4 }}

in

确定针是否在可迭代元素内。

1
2
3
{{ if in .Tags "production" }}
# ...
{{ end }}

loop

接受不同的参数,并根据这些参数区分其行为。

如果给出一个整数的循环,它将返回一个从0开始并循环到但不包括给定整数的goroutine:

1
2
{{ range loop 5 }}
# Comment{{end}}

如果给定两个整数,则此函数将返回从第一个整数开始并循环到但不包括第二个整数的goroutine:

例如:

1
2
{{ range $i := loop 5 8 }}
stanza-{{ $i }}{{ end }}

结果:

stanza-5
stanza-6
stanza-7

注意:由于函数返回goroutine而不是切片,因此无法获取索引和元素。换句话说,以下内容无效

1
2
3
# Will NOT work!
{{ range $i, $e := loop 5 8 }}
# ...{{ end }}

join

字符串连接:

1
{{ $items | join "," }}

trimSpace

获取给定的字符串并将其解析为布尔值:

{{ "true" | parseBool }}

这可以与键和条件检查结合使用,例如:

1
{{ if key "feature/enabled" | parseBool }}{{ end }}

parseFloat

获取给定的字符串并将其解析为基数为10的float64:

1
{{ "1.2" | parseFloat }}

parseInt

获取给定的字符串并将其解析为base-10 int64:

{{ "1" | parseInt }}

这可以与其他助手结合使用,例如:

1
2
{{ range $i := loop key "config/pool_size" | parseInt }}
# ...{{ end }}

parseJSON

获取给定的输入(通常是键中的值)并将结果解析为JSON:

1
{{ with $d := key "user/info" | parseJSON }}{{ $d.name }}{{ end }}

注意:Consul Template会多次评估模板,并且在第一次评估时,键的值将为空(因为尚未加载任何数据)。这意味着模板必须防止空响应。

parseUint

获取给定的字符串并将其解析为base-10 int64:

1
{{ "1" | parseUint }}

plugin

获取插件和可选有效加载的名称并执行Consul Template插件。

1
{{ plugin "my-plugin" }}

该插件可以采用任意数量的字符串参数,也可以是生成字符串的管道的目标。这通常与用于自定义的JSON过滤器结合使用:

有关插件的更多信息,请参阅插件部分

1
{{ tree "foo" | explode | toJSON | plugin "my-plugin" }}

regexMatch

将参数作为正则表达式,如果在给定字符串上匹配则返回true,否则返回false

1
2
3
4
5
{{ if "foo.bar" | regexMatch "foo([.a-z]+)" }}
# ...
{{ else }}
# ...
{{ end }}

regexReplaceAll

将参数作为正则表达式,并将所有出现的正则表达式替换为给定的字符串。与go一样,您可以使用$1之类的变量来引用替换字符串中的子表达式。

1
{{ "foo.bar" | regexReplaceAll "foo([.a-z]+)" "$1" }}

replaceAll

将参数作为字符串取代,并用给定的字符串替换给定字符串的所有匹配项。

1
{{ "foo.bar" | replaceAll "." "_" }}

此功能也可以与其他功能链接:

1
{{ service "web" }}{{ .Name | replaceAll ":" "_" }}{{ end }}

split

在提供的分隔符上拆分给定的字符串:

1
{{ "foo\nbar\n" | split "\n" }}

这可以与链接和管道与其他功能结合使用:

1
{{ key "foo" | toUpper | split "\n" | join "," }}

timestamp

以字符串(UTC)形式返回当前时间戳。如果没有给出参数,则结果是当前的RFC3339时间戳:

1
{{ timestamp }} // e.g. 1970-01-01T00:00:00Z

如果给出了可选参数,则它用于格式化时间戳。参考日期Mon Jan 2 15:04:05 -0700 MST 2006可用于根据需要格式化日期:

1
{{ timestamp "2006-01-02" }} // e.g. 1970-01-01

有关更多信息,请参阅Go’s time.Format

作为一种特殊情况,如果可选参数是“unix”,则以秒为单位的unix时间戳将作为字符串返回。

1
{{ timestamp "unix" }} // e.g. 0

toJSON

treels调用获取结果并将其转换为JSON对象。

1
{{ tree "config" | explode | toJSON }}

结果:

1
{"admin":{"port":"1234"},"maxconns":"5","minconns":"2"}

注意:Consul将所有KV数据存储为字符串。因此,true为“true”,1为“1”,等等。

toJSONPretty

treels调用获取结果并将其转换为漂亮打印的JSON对象,缩进两个空格。

1
{{ tree "config" | explode | toJSONPretty }}

结果:

1
2
3
4
5
6
7
{
 "admin": {
   "port": "1234"
 },
 "maxconns": "5",
 "minconns": "2",
}

toLower

将参数作为字符串并将其转换为小写。

1
{{ key "user/name" | toLower }}

参见Go’s strings.ToLower

toTitle

将参数作为字符串并将其转换为标题。

1
{{ key "user/name" | toTitle }}

参见Go’s strings.Title

toTOML

treels调用获取结果并将其转换为TOML对象。

1
{{ tree "config" | explode | toTOML }}

结果:

1
2
3
4
5
maxconns = "5"
minconns = "2"

[admin]
 port = "1134"

toUpper

将参数作为字符串并将其转换为大写。

1
{{ key "user/name" | toUpper }}

参见Go’s strings.ToUpper

toYAML

treels调用获取结果并将其转换为漂亮打印的YAML对象,缩进两个空格。

1
{{ tree "config" | explode | toYAML }}

结果:

1
2
3
4
admin:
 port: "1234"
maxconns: "5"
minconns: "2"

4. Math Functions数学函数

方便浮点数和整数值数学计算的函数

add

返回两个值的总和。

1
{{ add 1 2 }} // 3

这也可以与管道功能一起使用(下同)。

1
{{ 1 | add 2 }} // 3

subtract

返回第一个值与第二个值的差值。

1
{{ subtract 2 5 }} // 3
1
{{ 5 | subtract 2 }} // 3

请仔细注意参数的顺序(下同)。

multiply

返回两个值的乘积。

1
{{ multiply 2 2 }} // 4
1
{{ 2 | multiply 2 }} // 4

divide

返回第一个值的第二个值的除法。

1
{{ divide 2 10 }} // 5
1
{{ 10 | divide 2 }} // 5

modulo

返回第一个值的第二个模的模数。

1
{{ modulo 2 5 }} // 1
1
{{ 5 | modulo 2 }} // 1