---
title: ytt(YAML Templating Tool)入門 - Templating編
tags: ["YAML", "ytt", "k14s", "Carvle"]
categories: ["Dev", "Carvel", "ytt"]
date: 2020-08-19T06:44:46Z
updated: 2020-08-27T14:25:46Z
---

[ytt](https://get-ytt.io)はスタンドアローンで使えるYAMLのテンプレートツールです。同じ領域のツールとしては

* [Jsonnet](https://jsonnet.org)
* [Dhall](https://dhall-lang.org)
* [Cue](https://cuelang.org)

がありますが、テンプレートツールとしてのyttの特徴としては

* YAMLそのものを記述する(YAMLとしてvalid)
* YAMLのコメントとしてPython-likeな[Starlark](https://github.com/google/starlark-go/blob/master/doc/spec.md)言語を記述することで動的な表現ができる

各ツールとの比較の詳細は[こちら](https://github.com/k14s/ytt/blob/develop/docs/ytt-vs-x.md)。

他のツールが専用の言語を使うのに比べ、yttはあくまでもYAML+コメントなので心理的な導入のハードルは低いかもしれません。
もちろんytt用に文法は覚える必要はありますが、言語部分もPythonを使ったことがあれば覚えやすいかもしれません。

ただし、yttはただのテンプレートツールとは異なり、overlay機能を持っています。これはYAMLの一部を書き換えたり、追加したり、削除したりできる強力な機能で、
Kubernetesの[Kustomize](https://kubernetes-sigs.github.io/kustomize/api-reference/glossary/#overlay)やBOSHの[Operation files](https://bosh.io/docs/cli-ops-files)と同じような機能です。<br>

つまり、Templating + Overlayが大きなyttの機能です。
本記事ではTemplating機能のみフォーカスします。Overlay機能については[次の記事](/entries/545)で説明します。

yttの仕様は[こちら](https://github.com/k14s/ytt/blob/develop/docs/lang.md)です。

**目次**
<!-- toc -->

### インストール

[こちら](https://github.com/k14s/ytt/releases)からバイナリをダウンロードするか、

```
curl -sL https://k14s.io/install.sh | bash
```

あるいは

```
brew tap k14s/tap
brew install ytt
```

でインストールできます。

### 基本的な書き方

yttのYAMLは次の形式のコメントを使用します。それ以外のコメント(普通のYAMLのコメント)を書くとエラーになります。
エラーを許容する場合は`--ignore-unknown-comments`オプションをつける必要があります。

```yaml
#! YAMLのコメント

#@ Starlark言語の値またはコード
```

YAMLの値の部分にStarlarkの値を使って次の`config.yml`を作成します。

```yaml
null_value: #@ None
boolean_true: #@ True
boolean_false: #@ False

string_value: #@ "Hello World!"
string_format: #@ "Hello {}!".format("ytt")

list_value: #@ ["a", "b", "c"]
list_range: #@ list(range(0, 5))

map_value: #@ {"a": 100, "b": 200, "c": "Hello"}
```

このファイルを`ytt`コマンドに入力すると次のような出力を得られます。

```
$ ytt -f config.yml
null_value: null
boolean_true: true
boolean_false: false
string_value: Hello World!
string_format: Hello ytt!
list_value:
- a
- b
- c
list_range:
- 0
- 1
- 2
- 3
- 4
map_value:
  a: 100
  b: 200
  c: Hello
```

Python同様にリスト内包表記も利用できます。

```yaml
servers: #@ ["server-{}".format(x) for x in range(5)]
```

次のような出力結果になります。

```
$ ytt -f config.yml
servers:
- server-0
- server-1
- server-2
- server-3
- server-4
```

### 変数

変数の定義と参照ができます。

```yaml
#@ message = "Hello!"
#@ coins = {
#@   "penny": 1,
#@   "nickel": 5,
#@   "dime": 10,
#@   "quarter": 25,
#@ }

message: #@ message
conis: #@ coins
dime: #@ coins["dime"]

conin_keys: #@ coins.keys()
conin_values: #@ coins.values()
```

次のような出力結果になります。

```
$ ytt -f config.yml
message: Hello!
conis:
  penny: 1
  nickel: 5
  dime: 10
  quarter: 25
dime: 10

conin_keys:
- penny
- nickel
- dime
- quarter
conin_values:
- 1
- 5
- 10
- 25
```
### 関数

関数の定義、呼び出しができます。

```yaml
#@ def twice(x):
#@    return x * 2
#@ end

foo: #@ twice(7)
list: #@ [twice(x) for x in [1, 2, 3]]
```

Python/Starlarkとは異なり、関数の定義は`end`で閉じる必要があります。

次のような出力結果になります。

```
$ ytt -f config.yml
foo: 14
list:
- 2
- 4
- 6
```

YAML Fragmentを関数として定義することもできます。

```yaml
#@ def labels():
organization: demo
space: develop
#@ end

labels: #@ labels()
```

次のような出力結果になります。

```
$ ytt -f config.yml
labels:
  organization: demo
  space: develop
```

### If文
If文が利用できます。これも`end`が必要です。

```yaml
#@ enabled = True
#@ if enabled:
foo: enabled
#@ else:
foo: disabled
#@ end
```

次のような出力結果になります。

```
$ ytt -f config.yml
foo: enabled
```

if/elseを一行で表現することもできます。

```yaml
foo: #@ "enabled" if enabled else "disabled" 
```

elseが不要でかつ、1ブロックだけを制御したい場合は`if/end`で省略できます。

```yaml
#@ enabled = True
#@ if/end enabled:
foo: enabled
```

### For文
For文が利用できます。これも`end`が必要です。

```yaml
#@ for i in range(5):
- #@ i
#@ end
```

次のような出力結果になります。

```
$ ytt -f config.yml
- 0
- 1
- 2
- 3
- 4
```

次のような書き方もできます。

```
#@ foo = ["a", 1], ["b", 2], ["c", 3]
#@ for a, i in foo:
- key: #@ a
  value: #@ i
#@ end
```

次のような出力結果になります。

```
$ ytt -f config.yml
- key: a
  value: 1
- key: b
  value: 2
- key: c
  value: 3
```
### モジュール作成
関数の定義を別ファイルに外部化して、モジュールとして読み込むことができます。

次の`demo.lib.yml`を用意します。

```yaml
#@ def labels():
organization: demo
space: develop
#@ end
```

`load("モジュール名", "関数名", "関数名", ...)`で関数を読み込めます。


```yaml
#@ load("demo.lib.yml", "labels")

labels: #@ labels()
```

次のような出力結果になります。

```
$ ytt -f config.yml -f demo.lib.yml
labels:
  organization: demo
  space: develop
```

モジュールファイルは拡張子の前に`.lib`が必要です。これがないとマルチドキュメントなYAMLとして、出力結果に含まれてしまいます。

モジュールはStarlarkでも記述できます。

```python
# demo.star
def square(x):
  return x * x
end
```

```yaml
#@ load("demo.star", "square")

value: #@ square(7)
```

次のような出力結果になります。

```
$ ytt -f config.yml -f demo.star   
value: 49
```

### 組み込みライブラリの利用

組み込み`ytt`ライブラリが用意されています。

https://github.com/k14s/ytt/blob/develop/docs/lang-ref-ytt.md

`ytt`ライブラリの`base64`モジュールを使う例です。

```yaml
#@ load("@ytt:base64", "base64")
#@ raw_value = "Hello World!"
value: #@ base64.encode(raw_value)
```

次のような出力結果になります。

```
$ ytt -f config.yml
value: SGVsbG8gV29ybGQh
```

> Note: yttでは次のように用語を使っています。
>
> * モジュール ... YAMLや関数などを含む単一ファイル
> * パッケージ ... モジュールを含む単一ディレクトリ
> * ライブラリ ... パッケージの集合
>
> https://github.com/k14s/ytt/blob/develop/docs/lang-ref-load.md#terminology

### カスタムライブラリの作成

`_ytt_lib`ディレクトリにカスタムライブラリを作成できます。
外部プロジェクトを`git submodule`で取得したり、[`vendir`](https://github.com/k14s/vendir)で取得した場合に配置する良いです。

簡単なサンプルとして[https://github.com/making/demo-lib](https://github.com/making/demo-lib)を取り込みましょう。

```
mkdir -p _ytt_lib/github.com/making/demo-lib
curl -sL https://github.com/making/demo-lib/archive/master.tar.gz | tar -xzvf - -C _ytt_lib/github.com/making/demo-lib --strip-components=1
```

次のディレクトリ構造になります。

```
$ tree .
.
|-- _ytt_lib
|   `-- github.com
|       `-- making
|           `-- demo-lib
|               `-- demo.star
`-- config.yml
```

`config.yml`でこの`github.com/making/demo-lib`ライブラリの`demo.star`モジュールを使用します。


```yaml
#@ load("@github.com/making/demo-lib:demo.star", "square")

value: #@ square(7)
```

次のような出力結果が得られます。

```
$ ytt -f .
value: 49
```

詳細は
* https://github.com/k14s/ytt/blob/develop/docs/lang-ref-load.md
* https://github.com/k14s/ytt/blob/develop/docs/lang-ref-ytt-library.md

### Assert

`ytt`ライブラリの`assert`モジュールで入力チェックができます。

```yaml
#@ load("@ytt:assert", "assert")

#@ foo = 200

foo: #@ foo if foo > 150 else assert.fail("'foo' must be greater than 150.")
```

nullチェックは次のように記述できます。

```yaml
#@ load("@ytt:assert", "assert")

#@ iaas = "vsphere"
iaas: #@ iaas or assert.fail("'iaas' is required!")
```

これは次の例の短縮版です。

```yaml
#@ load("@ytt:assert", "assert")

#@ iaas = "vsphere"
iaas: #@ iaas if iaas else assert.fail("'iaas' is required!")
```
### Dataの外部化

変更可能な設定項目を外部YAMLに切り出すことができます。

次の`values.yml`に設定項目を記述します。`#@data/values`アノテーションをつけてください。

```yaml
#@data/values
---
vsphere:
  hostname: vcsa-01.example.com
  username: administrator@vsphere.local	
  password: VMware1!
```

`ytt`ライブラリの`data`モジュールを使って読み込みます。
`#@data/values`アノテーションをつけたファイルを`ytt`コマンドの入力に含めると、`data.values.xxxxx`で参照できるようにんります。

```yaml
#@ load("@ytt:data", "data")
iaas-configurations:
- vcenter_host: #@ data.values.vsphere.hostname
  vcenter_username: #@ data.values.vsphere.username
  vcenter_password: #@ data.values.vsphere.password
```

次のように結合されて出力されます。

```
$ ytt -f config.yml -f values.yml
iaas-configurations:
- vcenter_host: vcsa-01.example.com
  vcenter_username: administrator@vsphere.local
  vcenter_password: VMware1!
```

設定項目は`ytt`コマンドの`-v`オプションで上書きできます。

```
$ ytt -f config.yml -f values.yml -v vsphere.password=password
iaas-configurations:
- vcenter_host: vcsa-01.example.com
  vcenter_username: administrator@vsphere.local
  vcenter_password: password
```

JSONを読み込む例も紹介します。Terraformなど別のツールの結果がJSONとして出力され、それをそのまま読み込みたいケースを想定します。

次の`values.json`があるとします。
```json
{
  "vsphere": {
    "hostname": "vcsa-01.example.com",
    "password": "VMware1!",
    "username": "administrator@vsphere.local"
  }
}
```

JSONファイルを文字列として読み込み、`json`モジュールでdecodeします。

```yaml
#@ load("@ytt:data", "data")
#@ load("@ytt:json", "json")

#@ values = json.decode(data.read("values.json"))
iaas-configurations:
- vcenter_host: #@ values["vsphere"]["hostname"]
  vcenter_username: #@ values["vsphere"]["username"]
  vcenter_password: #@ values["vsphere"]["password"]
```

次のような出力結果を得られます。

```
$ ytt -f config.yml -f values.json
iaas-configurations:
- vcenter_host: vcsa-01.example.com
  vcenter_username: administrator@vsphere.local
  vcenter_password: VMware1!
```

同様に`yaml`モジュールでデコードする例をみてみます。

次の[Concourse](https://concourse-ci.org)の`pipeline.yml`で、Taskの`config`以下のYAMLを別ファイルに外出ししたい場合。

```yaml
jobs:
- name: hello-world
  plan:
  - task: say-hello
    config:
      platform: linux
      image_resource:
        type: docker-image
        source:
          repository: ubuntu
          tag: bionic
      run:
        path: bash
        args: 
        - -c
        - |
          echo "Hello, world!"
```

切り出したTaskファイルを`hello.lib.yml`というファイル名で保存します。ファイル名に`.lib`を含めるのは、読み込まれた際にマルチドキュメントなYAMLとして出力されないようにするためです。

```yaml
platform: linux
image_resource:
  type: docker-image
  source:
    repository: ubuntu
    tag: bionic
run:
  path: bash
  args:
  - -c
  - |
    echo "Hello, world!"
```

`pipeline.yml`で、この`hello.lib.yml`を`data`モジュールで文字列としてとして読み込み、`yaml`モジュールでdecodeし、`config`に設定します。

```yaml
#@ load("@ytt:data", "data")
#@ load("@ytt:yaml", "yaml")

jobs:
- name: hello-world
  plan:
  - task: say-hello
    config: #@ yaml.decode(data.read("hello.lib.yml"))
```

次のようなディレクトリ構造にします。

```
$ tree .
.
├── pipeline.yml
└── tasks
    └── hello.lib.yml
```

次のコマンドで一つのpipelineに結合されます。

```
$ ytt -f pipeline.yml -f tasks                        
jobs:
- name: hello-world
  plan:
  - task: say-hello
    config:
      platform: linux
      image_resource:
        type: docker-image
        source:
          repository: ubuntu
          tag: bionic
      run:
        path: bash
        args:
        - -c
        - echo "Hello, world!"
```

### Textテンプレート
最初に`"文字列".format(...)`で文字列内に変数を埋める例を紹介しましたが、複数行の文字列に変数を埋め込みたい時は
`@yaml/text-templated-strings`アノテーションをつけると便利です。

次のようなファイルが、

```yaml
#@ customer_name = "John Doe"

#@yaml/text-templated-strings
message: |
  Dear (@= customer_name @),

  Thanks for reading this blog post.

  Best regards,
  @making
```

次のように出力されます。

```
$ ytt -f config.yml                        
message: |-
  Dear John Doe,

  Thanks for reading this blog post.

  Best regards,
  @making
```

テンプレートの部分は別ファイルに切り出せます。`message_body.lib.txt`に次の内容を記述し、

```
(@ def message_body(customer_name): -@)
Dear (@= customer_name @),

Thanks for reading this blog post.

Best regards,
@making
(@- end @)
```

次のように読み込めば、

```yaml
#@ load("message_body.lib.txt", "message_body")
#@ customer_name = "John Doe"
message: #@ message_body(customer_name)
```

次のように出力されます。

```yaml
$ ytt -f config.yml -f message_body.lib.txt
message: |-
  Dear John Doe,

  Thanks for reading this blog post.

  Best regards,
  @making
```

---
yttのTemplating機能を見てきました。[次の記事](/entries/545)はYAMLの柔軟な加工に便利なOverlay機能を見ます。
