confd 模板语法

本文是 confd 在 github 上的文档的翻译和此文的摘抄,该文档详细介绍了confd模板的语法和结构。另外,最后我列出一些我个人常用的表达式,方便未来复用。

一、模板源

模板源以TOML编写并已 .toml 作为后缀的来定义的。默认情况下,模板源存储在/etc/confd/conf.d 目录下。模板源使用Go的库: text/template.。

必要参数

  • dest (字符串) - 目标文件。
  • keys (字符串数组) - 键数组。
  • src (字符串) - 配置模板的相对路径 。

可选参数

  • gid (int) - 应该拥有该文件的gid。默认为有效的gid。
  • mode (字符串) - 文件的权限模式。
  • uid (int) - 应该拥有该文件的uid。默认为有效的uid。
  • reload_cmd (字符串) - 重新加载配置的命令。
  • check_cmd (字符串)- 检查配置的命令。
  • prefix (字符串) - 键前缀的字符串。

[template]
src = "nginx.conf.tmpl"
dest = "/etc/nginx/nginx.conf"
uid = 0
gid = 0
mode = "0644"
keys = [
  "/nginx",
]
check_cmd = "/usr/sbin/nginx -t -c {{.src}}"
reload_cmd = "/usr/sbin/service nginx restart"

二、模板函数

map

创建接口和字符串的键值映射

{{$endpoint := map "name" "elasticsearch" "private_port" 9200 "public_port" 443}}

name: {{index $endpoint "name"}}
private-port: {{index $endpoint "private_port"}}
public-port: {{index $endpoint "public_port"}}

如果您是子模板并且想要向其传递多个值,则特别有用。

base

path.Base函数的别名 。

{{with get "/key"}}
    key: {{base .Key}}
    value: {{.Value}}
{{end}}

exists

判断键是否存在。如果找不到键,则返回false。

{{if exists "/key"}}
    value: {{getv "/key"}}
{{end}}

get

返回键与其键匹配的键值对。如果未找到键,则返回错误。

{{with get "/key"}}
    key: {{.Key}}
    value: {{.Value}}
{{end}}

gets

返回与其key匹配所有键值对,如果未找到键,则返回错误。

{{range gets "/*"}}
    key: {{.Key}}
    value: {{.Value}}
{{end}}

getv

返回与其键或可选的默认值匹配的字符串,如果未找到键且未给出默认值,则返回错误。

value: {{getv "/key"}}

getv默认值

value: {{getv "/key" "default_value"}}

getvs

返回与其键匹配所有值的字符串,如果未找到密钥,则返回错误。

{{range getvs "/*"}}
    value: {{.}}
{{end}}

getenv

返回在os.Getenv 中检索由键命名的环境变量的值。如果变量不存在,该值将为空。(可选)您可以提供一个默认值,如果该键不存在,将返回该值。

export HOSTNAME=`hostname`
hostname: {{getenv "HOSTNAME"}}

getenv默认值

ipaddr: {{getenv "HOST_IP" "127.0.0.1"}}

datetime

time.Now的别名

Generated by confd {{datetime}}
输出:
Generated by confd 2015-01-23 13:34:56.093250283 -0800 PST
Generated by confd {{datetime.Format "Jan 2, 2006 at 3:04pm (MST)"}}
输出:
Generated by confd Jan 23, 2015 at 1:34pm (EST)

更多用法,请参阅官方时间用法

split

包装器 strings.Split。分隔输入的字符串并返回一个子字符串切片。

{{ $url := split (getv "/deis/service") ":" }}
    host: {{index $url 0}}
    port: {{index $url 1}}

toUpper

strings.ToUpper的 别名 返回大写字符串。

key: {{toUpper "value"}}

toLower

strings.ToLower的 别名 。返回小写字符串。

key: {{toLower "Value"}}

json

返回map[string]interface{}形式的json值。

lookupSRV

net.LookupSRV 包装器 。通过组合net.SRV结构的所有字段按字母顺序对SRV记录进行排序,以减少不必要的配置重新加载。

{{range lookupSRV "mail" "tcp" "example.com"}}
  target: {{.Target}}
  port: {{.Port}}
  priority: {{.Priority}}
  weight: {{.Weight}}
{{end}}

etcd添加键值

etcdctl set /services/zookeeper/host1 '{"Id":"host1", "IP":"192.168.10.11"}'
etcdctl set /services/zookeeper/host2 '{"Id":"host2", "IP":"192.168.10.12"}'

创建模板源

[template]
src = "services.conf.tmpl"
dest = "/tmp/services.conf"
keys = [
  "/services/zookeeper/"
]

创建模板

{{range gets "/services/zookeeper/*"}}
{{$data := json .Value}}
  id: {{$data.Id}}
  ip: {{$data.IP}}
{{end}}

map遍历

一旦解析了JSON,就可以使用普通的Go模板函数遍历它 index

更高级的结构,如下所示:

{
  "animals": [
    {"type": "dog", "name": "Fido"},
    {"type": "cat", "name": "Misse"}
  ]
}

它可以像这样遍历:

{{$data := json (getv "/test/data/")}}
type: {{ (index $data.animals 1).type }}
name: {{ (index $data.animals 1).name }}
{{range $data.animals}}
{{.name}}
{{end}}

jsonArray

从接口返回json数组,例如: [“a”, “b”, “c”]`。

{{range jsonArray (getv "/services/data/")}}
	val: {{.}}
{{end}}

ls

返回匹配路径的所有子键,字符串等。如果找不到路径,则返回空列表。

{{range ls "/deis/services"}}
   value: {{.}}
{{end}}

lsdir

返回匹配路径的所有子键,字符串等。注意它只返回也有子键的子键。如果找不到路径,则返回空列表。

{{range lsdir "/deis/services"}}
   value: {{.}}
{{end}}

dir

返回制定键的父目录。

{{with dir "/services/data/url"}}
	dir: {{.}}
{{end}}

join

strings.Join 函数的别名 。

{{$services := getvs "/services/elasticsearch/*"}}
services: {{join $services ","}}

replace

strings.place 函数的别名 。

{{$backend := getv "/services/backend/nginx"}}
backend = {{replace $backend "-" "_" -1}}

lookupIP

net.LookupIP 函数的包装器 。包装器还按字母顺序排序IP地址。这一点至关重要,因为在动态环境中,DNS服务器通常会混淆链接到域名的地址。这将导致不必要的配置重新加载。

{{range lookupIP "some.host.local"}}
    server {{.}};
{{end}}

atoi

strconv.Atoi 函数的别名 。

{{seq 1 (atoi (getv "/count"))}}

三、我的例子

  1. 需要end包裹的关键字

    • range
    • with
  2. 获取环境变量

    {{$hostname := getenv "KELU_HOSTNAME"}}
    hostname="{{$hostname}}"
    
  3. 取值with

    {{with get (printf "/confd/%s/config" $hostname)}}
    {{ with get $node }}
    
  4. 循环range

    {{range $key, $dir := lsdir "/confd"}}
    	{{ $node := printf "/confd/%s/config" $dir }} # 字符串拼接+赋值
       
    	{{ with get $node }}
    		{{ $data := json .Value }}
    	{{end}}
    {{end}}
    
  5. 对比 eq/ne

    {{if $data.isTrue }}
    {{if ne $data.name "kelu"}}
    
    eq
    	Returns the boolean truth of arg1 == arg2
    ne
    	Returns the boolean truth of arg1 != arg2
    lt
    	Returns the boolean truth of arg1 < arg2
    le
    	Returns the boolean truth of arg1 <= arg2
    gt
    	Returns the boolean truth of arg1 > arg2
    ge
    	Returns the boolean truth of arg1 >= arg2
    
  6. 字符串拼接printf

    {{ $config := (printf "/haproxy/%s/config" $haproxy)}}
    
  7. 解析 json

    {{ with get $node }}{{ $data := json .Value }}
    
  8. 数组长度len

    {{$dir := lsdir "/confd"}}
    {{$count := len $dir}}
    
    and
    	Returns the boolean AND of its arguments by returning the
    	first empty argument or the last argument, that is,
    	"and x y" behaves as "if x then y else x". All the
    	arguments are evaluated.
    call
    	Returns the result of calling the first argument, which
    	must be a function, with the remaining arguments as parameters.
    	Thus "call .X.Y 1 2" is, in Go notation, dot.X.Y(1, 2) where
    	Y is a func-valued field, map entry, or the like.
    	The first argument must be the result of an evaluation
    	that yields a value of function type (as distinct from
    	a predefined function such as print). The function must
    	return either one or two result values, the second of which
    	is of type error. If the arguments don't match the function
    	or the returned error value is non-nil, execution stops.
    html
    	Returns the escaped HTML equivalent of the textual
    	representation of its arguments. This function is unavailable
    	in html/template, with a few exceptions.
    index
    	Returns the result of indexing its first argument by the
    	following arguments. Thus "index x 1 2 3" is, in Go syntax,
    	x[1][2][3]. Each indexed item must be a map, slice, or array.
    slice
    	slice returns the result of slicing its first argument by the
    	remaining arguments. Thus "slice x 1 2" is, in Go syntax, x[1:2],
    	while "slice x" is x[:], "slice x 1" is x[1:], and "slice x 1 2 3"
    	is x[1:2:3]. The first argument must be a string, slice, or array.
    js
    	Returns the escaped JavaScript equivalent of the textual
    	representation of its arguments.
    len
    	Returns the integer length of its argument.
    not
    	Returns the boolean negation of its single argument.
    or
    	Returns the boolean OR of its arguments by returning the
    	first non-empty argument or the last argument, that is,
    	"or x y" behaves as "if x then x else y". All the
    	arguments are evaluated.
    print
    	An alias for fmt.Sprint
    printf
    	An alias for fmt.Sprintf
    println
    	An alias for fmt.Sprintln
    urlquery
    	Returns the escaped value of the textual representation of
    	its arguments in a form suitable for embedding in a URL query.
    	This function is unavailable in html/template, with a few
    	exceptions.
    
  9. 获取字符串最后一个单词base

    {{base $dir}}
    
  10. 反释义

原文:

image-20211020164157259

在模板中要在外部包裹{{}}再加双引号:

image-20211020164303072

  1. 数组解析jsonArray

    etcd中的数据为:/confd/local/ip/static ["1.2.3.4","5.6.7.8"]

    {{range jsonArray (getv "/confd/local/ip/static")}}allow {{.}};{{end}}
    
  2. 字符串拆分为数组 spilt

    etcd中的数据为:/a/b/c/d {"target":"a,b,c,d"}

    {{ $backend := split $data.target "," }}
    {{ range $backend }}
    ...
    {{ end }}
    

参考资料


在 macOS 上配置 SSH 隧道并创建 SOCKS 代理 升级 ubuntu 18.04 的内核