yttはスタンドアローンで使えるYAMLのテンプレートツールです。同じ領域のツールとしては
がありますが、テンプレートツールとしてのyttの特徴としては
- YAMLそのものを記述する(YAMLとしてvalid)
- YAMLのコメントとしてPython-likeなStarlark言語を記述することで動的な表現ができる
各ツールとの比較の詳細はこちら。
他のツールが専用の言語を使うのに比べ、yttはあくまでもYAML+コメントなので心理的な導入のハードルは低いかもしれません。
もちろんytt用に文法は覚える必要はありますが、言語部分もPythonを使ったことがあれば覚えやすいかもしれません。
ただし、yttはただのテンプレートツールとは異なり、overlay機能を持っています。これはYAMLの一部を書き換えたり、追加したり、削除したりできる強力な機能で、
KubernetesのKustomizeやBOSHのOperation filesと同じような機能です。
つまり、Templating + Overlayが大きなyttの機能です。
本記事ではTemplating機能のみフォーカスします。Overlay機能については次の記事で説明します。
yttの仕様はこちらです。
目次
インストール
こちらからバイナリをダウンロードするか、
curl -sL https://k14s.io/install.sh | bash
あるいは
brew tap k14s/tap
brew install ytt
でインストールできます。
基本的な書き方
yttのYAMLは次の形式のコメントを使用します。それ以外のコメント(普通のYAMLのコメント)を書くとエラーになります。
エラーを許容する場合は--ignore-unknown-comments
オプションをつける必要があります。
#! YAMLのコメント
#@ Starlark言語の値またはコード
YAMLの値の部分にStarlarkの値を使って次のconfig.yml
を作成します。
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同様にリスト内包表記も利用できます。
servers: #@ ["server-{}".format(x) for x in range(5)]
次のような出力結果になります。
$ ytt -f config.yml
servers:
- server-0
- server-1
- server-2
- server-3
- server-4
変数
変数の定義と参照ができます。
#@ 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
関数
関数の定義、呼び出しができます。
#@ 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を関数として定義することもできます。
#@ def labels():
organization: demo
space: develop
#@ end
labels: #@ labels()
次のような出力結果になります。
$ ytt -f config.yml
labels:
organization: demo
space: develop
If文
If文が利用できます。これもend
が必要です。
#@ enabled = True
#@ if enabled:
foo: enabled
#@ else:
foo: disabled
#@ end
次のような出力結果になります。
$ ytt -f config.yml
foo: enabled
if/elseを一行で表現することもできます。
foo: #@ "enabled" if enabled else "disabled"
elseが不要でかつ、1ブロックだけを制御したい場合はif/end
で省略できます。
#@ enabled = True
#@ if/end enabled:
foo: enabled
For文
For文が利用できます。これもend
が必要です。
#@ 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
を用意します。
#@ def labels():
organization: demo
space: develop
#@ end
load("モジュール名", "関数名", "関数名", ...)
で関数を読み込めます。
#@ load("demo.lib.yml", "labels")
labels: #@ labels()
次のような出力結果になります。
$ ytt -f config.yml -f demo.lib.yml
labels:
organization: demo
space: develop
モジュールファイルは拡張子の前に.lib
が必要です。これがないとマルチドキュメントなYAMLとして、出力結果に含まれてしまいます。
モジュールはStarlarkでも記述できます。
# demo.star
def square(x):
return x * x
end
#@ 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
モジュールを使う例です。
#@ 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/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
モジュールを使用します。
#@ 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
モジュールで入力チェックができます。
#@ load("@ytt:assert", "assert")
#@ foo = 200
foo: #@ foo if foo > 150 else assert.fail("'foo' must be greater than 150.")
nullチェックは次のように記述できます。
#@ load("@ytt:assert", "assert")
#@ iaas = "vsphere"
iaas: #@ iaas or assert.fail("'iaas' is required!")
これは次の例の短縮版です。
#@ load("@ytt:assert", "assert")
#@ iaas = "vsphere"
iaas: #@ iaas if iaas else assert.fail("'iaas' is required!")
Dataの外部化
変更可能な設定項目を外部YAMLに切り出すことができます。
次のvalues.yml
に設定項目を記述します。#@data/values
アノテーションをつけてください。
#@data/values
---
vsphere:
hostname: vcsa-01.example.com
username: administrator@vsphere.local
password: VMware1!
ytt
ライブラリのdata
モジュールを使って読み込みます。#@data/values
アノテーションをつけたファイルをytt
コマンドの入力に含めると、data.values.xxxxx
で参照できるようにんります。
#@ 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
があるとします。
{
"vsphere": {
"hostname": "vcsa-01.example.com",
"password": "VMware1!",
"username": "administrator@vsphere.local"
}
}
JSONファイルを文字列として読み込み、json
モジュールでdecodeします。
#@ 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のpipeline.yml
で、Taskのconfig
以下の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として出力されないようにするためです。
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
に設定します。
#@ 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
アノテーションをつけると便利です。
次のようなファイルが、
#@ 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 @)
次のように読み込めば、
#@ load("message_body.lib.txt", "message_body")
#@ customer_name = "John Doe"
message: #@ message_body(customer_name)
次のように出力されます。
$ ytt -f config.yml -f message_body.lib.txt
message: |-
Dear John Doe,
Thanks for reading this blog post.
Best regards,
@making
yttのTemplating機能を見てきました。次の記事はYAMLの柔軟な加工に便利なOverlay機能を見ます。