Consul Template チートシート

Consul Template のドキュメントと Go Template のドキュメントの両方を往復するのに疲れたのでまとめました。

対応バージョン: 0.21.0

## 用語の整理

| 用語 | 内容 |
| ---- | ---- |
| **ドット** (dot) | テンプレート実行中に特定のデータを指し示すカーソル。記法 `.` で参照できる。 |
| **アクション** (action) | 制御構造のこと |
| **引数** (argument) | 単純な値のこと |
| **コマンド** (command) | 引数 or 関数 or メソッド |
| **パイプライン** (pipeline) | 複数のコマンドを <code>&#124;</code> で連結したもの。パイプで受け取った値は関数やメソッドの引数の末尾に追加される。 |
| **空の値** (empty) | `false`、`0`、`nil`ポインタまたはインタフェース値、長さがゼロの配列、スライス、マップ、文字列 |

## [アクション](https://golang.org/pkg/text/template/#hdr-Actions)

| 内容 | 記述例 | 備考 |
| ---- | ---- | ---- |
| **コメント** | `{{/* 任意のコメント */}}` | 複数行可 |
| **値の出力** | `{{パイプライン}}` | |
| **if** | `{{if パイプライン}}` T1 `{{end}}` | |
| **else** | `{{if パイプライン}}` T1 `{{else}}` T0 `{{end}}` | |
| **else if** | `{{if パイプライン}}` T1 `{{else if パイプライン}}` T0 `{{end}}` | |
| **ループ** | `{{range パイプライン}}` T1 `{{end}}` |
| **ループ** | `{{range パイプライン}}` T1 `{{else}}` T0 `{{end}}` | パイプラインが空なら T0 |
| **テンプレート定義** | `{{define "名前"}} T1 {{end}}` | [Nested template definitions](https://golang.org/pkg/text/template/hdr-Nested_template_definitions)
| **テンプレート実行** | `{{template "名前"}}` | ドットが `nil` にセットされる |
| **テンプレート実行** | `{{template "名前" パイプライン}}` | ドットがパイプラインの値にセットされる |
| **テンプレート定義+実行** | `{{block "名前" パイプライン}}` T1 `{{end}}` | 以下の記述と同等:<br>`{{define "名前"}}` T1 `{{end}}`<br>`{{template "名前" .}}` |
| **ドット設定** | `{{with パイプライン}}` T1 `{{end}}` | パイプラインが空なら何もしない |
| **ドット設定** | `{{with パイプライン}}` T1 `{{else}}` T0 `{{end}}` | パイプラインが空なら T0 |
| **変数の初期化** | `$variable := パイプライン` | 任意のアクション内のパイプラインで使用可 |
| **range 内の変数の初期化** | `{{range $index, $element := パイプライン}}` | 

## パイプライン

| 内容 | 定義 | 記述例 |
| ---- | ---- | ---- |
| **パイプライン** | :black_small_square: コマンド<br>:black_small_square: コマンド <code>&#124;</code> コマンド | `{{"\"output\""}}`<br><code>{{"output" &#124; printf "%q"}}</code> |
| **コマンド** | :black_small_square: 引数<br>:black_small_square: `.`メソッド名 [引数...]<br>:black_small_square: 関数名 [引数...] | `"foo"`<br>`.Method`<br>`.Method "foo" "bar" "baz"`<br>`functionName`<br>`functionName "foo" "bar" "baz"`

## [引数](https://golang.org/pkg/text/template/#hdr-Arguments)

| 内容 | 記述例 | 備考 |
| ---- | ---- | ---- |
| **値** | `true`, `false`, `"abc"`, `'a'`, `'\t'`, `42`, `0600`, `0xBadFace`, `72.40`, `6.67428e-11`, `6.67428e-11i` | Goの文法における真偽値、文字列、文字、数値、浮動小数点数、虚数、複素数の定数
| **nil** | `nil`
| **ドット** | `.`
| **変数** | `$piOver2`, `$` | ドルマークに続く英数字の文字列(なくてもよい) |
| **フィールド** | `.Field`, `.Field1.Field2`, `.$x.Field1.Field2` |
| **マップのキー** | `.Key`, `.Field1.Key1.Field2.Key2`, `$x.key1.key2` | キーは英数字(小文字始まりでも可)
| **引数なしのメソッド** | `.Method`, `.Field1.Key1.Method1.Field2.Key2.Method2`, `$x.Method1.Field` |
| **引数なしの関数** | `fun` |
| **グループ** | `(コマンド)`<br> `print (.F1 arg1) (.F2 arg2)`, `(.StructValuedMethod "arg").Field`

## 関数

### [標準関数](https://golang.org/pkg/text/template/#hdr-Functions)

| 関数名 |  内容 | 記述例 | 備考 |
| ---- | ---- | ---- | ---- |
| `and` | 論理積 | `and x y` | xが真のときy、そうでなければx |
| `call` | 最初の引数の関数に残りの引数をパラメータとして与えて実行した結果 | `.X.Y 1 2` | Goで書くと `dot.X.Y(1, 2)` |
| `html` | HTML のエスケープ | `html "foo & bar"` |
| `index` | 最初の引数を続く引数でインデックスした結果 | `index x 1 2 3` | Goの文法では `x[1][2][3]` |
| `js` | JavaScript のエスケープ |
| `len` | 引数の長さ |
| `not` | 論理否定値 | 
| `or` | 論理和 | `or x y` | xが真のときx、そうでなければy |
| `print` | [fmt.Sprint](https://golang.org/pkg/fmt/#Sprint) |
| `printf` | [fmt.Sprintf](https://golang.org/pkg/fmt/#Sprintf) |
| `println` | [fmt.Sprintln](https://golang.org/pkg/fmt/#Sprintln) |
| `urlquery` | URLエンコード | 
| `eq` | == | `eq 1 2` |
| `ne` | != | `ne 1 2` |
| `lt` | < | `lt 1 2` |
| `le` | <= | `le 1 2` |
| `gt` | > | `gt 1 2` |
| `ge` | >= | `ge 1 2` |

### [Consul Template 関数](https://github.com/hashicorp/consul-template#api-functions)

#### API Functions

| 関数名 |  内容 | 記述例 | 備考 |
| ---- | ---- | ---- | ---- |
| [`datacenters`](https://github.com/hashicorp/consul-template#datacenters) | データセンターリストを返す | `{{datacenters}}` |
| [`file`](https://github.com/hashicorp/consul-template#file) | ローカルファイルを読み込んで返す | `{{file "/path/to/local/file"}}` |
| [`key`](https://github.com/hashicorp/consul-template#key) | KV の値を返す | `{{key "service/redis/maxconns@east-aws"}}`<br>`{{key "service/redis/maxconns"}}` |
| [`keyExists`](https://github.com/hashicorp/consul-template#keyexists) | KV のキーがあるかどうか返す | `{{ if keyExists "app/beta_active" }}`<br>`  # ...`<br>`{{ else }}`<br>`  # ...<br>{{ end }}`
| [`keyOrDefault`](https://github.com/hashicorp/consul-template#keyordefault) | Consul の KV の値を返す。キーがなければデフォルト値を返す | `{{keyOrDefault "service/redis/maxconns@east-aws" "5"}}` |
| [`ls`](https://github.com/hashicorp/consul-template#ls) | key-value ペアを返す | `{{range ls "service/redis@east-aws"}}{{.Key}} {{.Value}}{{end}}` |
| [`node`](https://github.com/hashicorp/consul-template#node) | 単一ノードの値を返す | `{{node "node1"}}`<br>`{{node}}`<br>`{{node "node1" "@east-aws"}}` | ノード名を省略すると自分自身のノード |
| [`nodes`](https://github.com/hashicorp/consul-template#nodes) | 全ノードのリストを返す | `{{nodes}}`<br>`{{nodes "@east-aws"}}` |
| [`secret`](https://github.com/hashicorp/consul-template#secret) | [Vault](https://www.vaultproject.io/)の値(単一)を返す | <code>{{with secret "secret/passwords"}}<br>{{.Data.password}}{{end}}</code> |
| [`secrets`](https://github.com/hashicorp/consul-template#secrets) | [Vault](https://www.vaultproject.io/)の値(複数)を返す | <code>{{range secrets "secret/"}}<br>{{.}}{{end}}</code> |
| [`service`](https://github.com/hashicorp/consul-template#service) | サービスのリストを返す | `{{service "release.web@east-aws"}}`<br>`{{service "web|any"}}`<br>`{{service "web|passing,warning"}}` | フィルタ (`|` の後ろ) でステータスを指定できる |
| [`services`](https://github.com/hashicorp/consul-template#services) | 全サービスのリストを返す | `{{services}}`<br>`{{services "@east-aws"}}` |
| [`tree`](https://github.com/hashicorp/consul-template#tree) | KV のツリーを返す | <code>{{range tree "service/redis@east-aws"}}<br>{{.Key}} {{.Value}}{{end}}</code> | ネストしたキーは `/` で区切られる

#### Scratch

テンプレート内で使えるテンポラリな Key-Value ストア。

| 関数名 |  内容 | 記述例 | 備考 |
| ---- | ---- | ---- | ---- |
| [`scratch.Key`](https://github.com/hashicorp/consul-template#scratchkey) | キーの有無を返す | `{{ scratch.Key "foo" }}`
| [`scratch.Get`](https://github.com/hashicorp/consul-template#scratchget) | 値を返す | `{{ scratch.Get "foo" }}`
| [`scratch.Set`](https://github.com/hashicorp/consul-template#scratchset) | 値を設定 (上書きあり) | `{{ scratch.Set "foo" "bar" }}`
| [`scratch.SetX`](https://github.com/hashicorp/consul-template#scratchsetx) | 値を設定 (上書きなし) | `{{ scratch.SetX "foo" "bar" }}`
| [`scratch.MapSet`](https://github.com/hashicorp/consul-template#scratchmapset) | マップに値を設定 (上書きあり) | `{{ scratch.MapSet "vars" "foo" "bar" }}`
| [`scratch.MapSetX`](https://github.com/hashicorp/consul-template#scratchmapsetx) | マップに値を設定 (上書きなし) | `{{ scratch.MapSetX "vars" "foo" "bar" }}`
| [`scratch.MapValues`](https://github.com/hashicorp/consul-template#scratchmapvalues) | マップの値をリストで返す | `{{ scratch.MapValues "vars" }}`

#### Helper Functions

| 関数名 |  内容 | 記述例 | 備考 |
| ---- | ---- | ---- | ---- |
| [`base64Decode`](https://github.com/hashicorp/consul-template#base64decode) | BASE64 デコード | `{{ base64Decode "aGVsbG8=" }}`
| [`base64Encode`](https://github.com/hashicorp/consul-template#base64encode) | BASE64 エンコード | `{{ base64Encode "hello" }}`
| [`base64URLDecode`](https://github.com/hashicorp/consul-template#base64urldecode) | BASE64 デコード (URL セーフ) | `{{ base64URLDecode "aGVsbG8=" }}`
| [`base64URLEncode`](https://github.com/hashicorp/consul-template#base64urlencode) | BASE64 エンコード (URL セーフ) | `{{ base64Encode "hello" }}`
| [`byKey`](https://github.com/hashicorp/consul-template#bykey) | `tree` 関数の返した値を第1階層のキーでグループ化 | <code>{{range $key, $pairs := tree "groups" &#124; byKey}}<br>{{$key}}:<br>{{range $pair := $pairs}}  {{.Key}}={{.Value}}<br>{{end}}{{end}}</code> |
| [`byTag`](https://github.com/hashicorp/consul-template#bytag) | `service`, `services` 関数の返した値をタグでグループ化 | <code>{{range $tag, $services := service "web" &#124; byTag}}{{$tag}}<br>{{range $services}} server {{.Name}} {{.Address}}:{{.Port}}<br>{{end}}{{end}}</code> |
| [`contains`](https://github.com/hashicorp/consul-template#contains) | リストが引数に指定した値を含むかどうか | <code>{{ if .Tags &#124; contains "production" }}<br># ...<br>{{ end }}</code> |
| [`containsAll`](https://github.com/hashicorp/consul-template#containsall) | リストが引数に指定したリストをすべて含むかどうか | <code>{{ if containsAll $requiredTags .Tags }}<br># ...<br>{{ end }}</code>
| [`containsAny`](https://github.com/hashicorp/consul-template#containsany) | リストが引数に指定したリストを一つでも含むかどうか | <code>{{ if containsAny $acceptableTags .Tags }}<br># ...<br>{{ end }}</code>
| [`containsNone`](https://github.com/hashicorp/consul-template#containsnone) | リストが引数に指定したリストを全く含まないかどうか | <code>{{ if containsNone $forbiddenTags .Tags }}<br># ...<br>{{ end }}</code>
| [`env`](https://github.com/hashicorp/consul-template#env) | 環境変数を返す | `{{env "CLUSTER_ID"}}` |
| [`executeTemplate`](https://github.com/hashicorp/consul-template#executetemplate) | 定義したテンプレートを実行 | `{{ define "custom" }}my custom template{{ end }}`<br><br>This is my other template:<br>`{{ executeTemplate "custom" }}`<br><br>And I can call it multiple times:<br>`{{ executeTemplate "custom" }}`<br><br>Even with a new context:<br>`{{ executeTemplate "custom" 42 }}`<br><br>Or save it to a variable:<br>`{{ $var := executeTemplate "custom" }}`
| [`explode`](https://github.com/hashicorp/consul-template#explode) | `tree`, `ls` 関数の返した値をネストしたマップに変換する | <code>{{ tree "config" &#124; explode }}</code> |
| [`in`](https://github.com/hashicorp/consul-template#in) | 値が引数に指定したリストに含まれるかどうか | <code>{{ if in .Tags "production" }}<br># ...<br>{{ end }}</code> | `contains` 関数と引数の順番が逆 |
| [`indent`](https://github.com/hashicorp/consul-template#indent) | インデント | <code>{{ tree "foo" | explode | toYAML | indent 4 }}</code>
| [`loop`](https://github.com/hashicorp/consul-template#loop) | 指定回数繰り返す | <code>{{range loop 5}}<br># Comment{{end}}</code><hr><code>{{range $i := loop 5 8}}<br>stanza-{{$i}}{{end}}</code> | `loop a b` だと a から b - 1 まで繰り返す
| [`join`](https://github.com/hashicorp/consul-template#join) | 文字列リストを連結 | <code>{{$items &#124; join ","}}</code> |
| [`trimSpace`](https://github.com/hashicorp/consul-template#trimspace) | 空白を削除 | <code>{{ file "/etc/ec2_version"&#124; trimSpace }}</code> |
| [`parseBool`](https://github.com/hashicorp/consul-template#parsebool) | 文字列を boolean に変換 | <code>{{if key "feature/enabled" &#124; parseBool}}{{end}}</code> |
| [`parseFloat`](https://github.com/hashicorp/consul-template#parsefloat) | 文字列を float64 に変換 | <code>{{"1.2" &#124; parseFloat}}</code> |
| [`parseInt`](https://github.com/hashicorp/consul-template#parseint) | 文字列を int64 に変換 | <code>{{range $i := loop key "config/pool_size" &#124; parseInt}}<br># ...{{end}}</code> |
| [`parseJSON`](https://github.com/hashicorp/consul-template#parsejson) | JSON をパース | <code>{{with $d := key "user/info" &#124; parseJSON}}{{$d.name}}{{end}}</code> |
| [`parseUint`](https://github.com/hashicorp/consul-template#parseuint) | 文字列を uint64 に変換 | <code>{{"1" &#124; parseUint}}</code> |
| [`plugin`](https://github.com/hashicorp/consul-template#plugin) | Consul Template plugin を実行 | <code>{{tree "foo" &#124; explode &#124; toJSON &#124; plugin "my-plugin"}}</code> | [プラグインは外部コマンドとして実装できる](https://github.com/hashicorp/consul-template#authoring-plugins)。 |
| [`regexMatch`](https://github.com/hashicorp/consul-template#regexmatch) | 正規表現マッチ | <code>{{"foo.bar" &#124; regexMatch "foo([.a-z]+)"}}</code> |
| [`regexReplaceAll`](https://github.com/hashicorp/consul-template#regexreplaceall) | 正規表現で置換 | <code>{{"foo.bar" &#124; regexReplaceAll "foo([.a-z]+)" "$1"}}</code> |
| [`replaceAll`](https://github.com/hashicorp/consul-template#replaceall) | 文字列置換 | <code>{{"foo.bar" &#124; replaceAll "." "_"}}</code> |
| [`split`](https://github.com/hashicorp/consul-template#split) | 文字列を分割 | <code>{{"foo\nbar\n" &#124; split "\n"}}</code> |
| [`timestamp`](https://github.com/hashicorp/consul-template#timestamp) | タイムスタンプを RFC3339 形式 (`1970-01-01T00:00:00Z`) の文字列で返す | `{{timestamp}}`<br>`{{timestamp "2006-01-02"}}`<br>`{{timestamp "unix"}}` | 引数を指定すると、[time.Format()](http://golang.org/pkg/time/#Time.Format)のフォーマットで返す。`unix` とすると UNIX Timestamp (ただし文字列) で返す。 |
| [`toJSON`](https://github.com/hashicorp/consul-template#tojson) | tree, ls 関数の返した値を JSON 形式の文字列に変換 | <code>{{ tree "config" &#124; explode &#124; toJSON }}</code> |
| [`toJSONPretty`](https://github.com/hashicorp/consul-template#tojsonpretty) | tree, ls 関数の返した値をインデントされた JSON 形式の文字列に変換 | <code>{{ tree "config" &#124; explode &#124; toJSONPretty }}</code> |
| [`toLower`](https://github.com/hashicorp/consul-template#tolower) | 小文字に変換 | <code>{{key "user/name" &#124; toLower}}</code> |
| [`toTitle`](https://github.com/hashicorp/consul-template#totitle) | Titlecase に変換 | <code>{{key "user/name" &#124; toTitle}}</code> |
| [`toTOML`](https://github.com/hashicorp/consul-template#totoml) | tree, ls 関数の返した値を TOML 形式の文字列に変換 | <code>{{ tree "config" | explode | toTOML }}</code>
| [`toUpper`](https://github.com/hashicorp/consul-template#toupper) | 大文字に変換 | <code>{{key "user/name" &#124; toUpper}}</code> |
| [`toYAML`](https://github.com/hashicorp/consul-template#toyaml) | tree, ls 関数の返した値を YAML 形式の文字列に変換 | <code>{{ tree "config" &#124; explode &#124; toYAML }}</code> |

#### Math Functions

| 関数名 |  内容 | 記述例 | 備考 |
| ---- | ---- | ---- | ---- |
| [`add`](https://github.com/hashicorp/consul-template#add) | 足し算 | `{{ add 1 2 }}` → 3<br><code>{{ 1 &#124; add 2 }}</code> → 3 |
| [`subtract`](https://github.com/hashicorp/consul-template#subtract) | 引き算 | `{{ subtract 2 5 }}` → 3<br><code>{{ 5 &#124; subtract 2 }}</code> → 3 | 引数の順番に注意 |
| [`multiply`](https://github.com/hashicorp/consul-template#multiply) | 掛け算 | `{{ multiply 2 2 }}` → 4<br><code>{{ 2 &#124; multiply 2 }}</code> → 4
| [`divide`](https://github.com/hashicorp/consul-template#divide) | 割り算 | `{{ divide 2 10 }}` → 5<br><code>{{ 10 &#124; divide 2 }}</code> → 5 | 引数の順番に注意 |
| [`modulo`](https://github.com/hashicorp/consul-template#modulo) | 剰余 | `{{ modulo 2 5 }}` → 1<br><code>{{ 5 &#124; modulo 2 }}</code>→ 1 | 引数の順番に注意 |

## [空白の制御](https://golang.org/pkg/text/template/#hdr-Text_and_spaces)

Go 1.6 / Consul Template 0.13.0 以降で利用可能。

  * `{{` の代わりに `{{-` を使うと直前の連続する空白が削除される
  * `}}` の代わりに `-}}` を使うと直後の連続する空白が削除される
  * 削除される空白は space, horizontal tab, carriage return, and newline
  * `{{-`, `-}}` とテンプレート記述本体の間はスペースで区切る必要がある

| 記述例 | 出力 |
| ---- | ---- |
| `{{23 -}} < {{- 45}}` | `23<45` |

## 参照

  * [Go Template](https://golang.org/pkg/text/template/) ([日本語訳](http://golang-jp.org/pkg/text/template/))
  * [Consul Template](https://github.com/hashicorp/consul-template)