Entrykitのrenderで遊ぶ -前編-
Dockerコンテナでsedで頑張っているみんながたどり着くところ.
Entrykitとは
たぶん僕がかいつまんで紹介するよりもこちらの記事の方が全体の紹介がしっかりされているのでこちらを読んで頂ければ良いと思います.
今回扱うのはそのうちのrenderで,シェルスクリプトで頑張っていたところをGo言語のtemplate機能で楽にやろうというものですね.
できること
- 環境変数からの値の埋め込み
ifによる条件分岐rangeによるループ- template中での変数の宣言と使用
- ファイルの読み込み
- インターネットリソースの取得
- 任意のコマンド実行結果の取得
できないこと
もし以下のことも可能であるならコメント等で教えて頂きたいです.
- template中での四則演算
- 改行モードの変更*1
環境
ここでは以下の様なDockerfileのコンテナを用意し試しています.
FROM alpine:latest
ENV WORKDIR /workdir/
ENV ENTRYKIT_REPO progrium/entrykit
ENV ENTRYKIT_VERSION 0.4.0
ENV ENTRYKIT_DL_FILE entrykit_${ENTRYKIT_VERSION}_Linux_x86_64.tgz
RUN mkdir ${WORKDIR}
RUN apk --update --no-cache add wget ca-certificates\
&& wget --no-check-certificate \
https://github.com/${ENTRYKIT_REPO}/releases/download/v${ENTRYKIT_VERSION}/${ENTRYKIT_DL_FILE} \
&& tar xvzf ${ENTRYKIT_DL_FILE} \
&& rm ${ENTRYKIT_DL_FILE} \
&& mv entrykit /bin/entrykit \
&& chmod +x /bin/entrykit \
&& entrykit --symlink \
&& apk --no-cache del wget
WORKDIR ${WORKDIR}
CMD ["render", "test.conf", "--", "cat", "test.conf"]
カレントディレクトリに試したいtest.conf.tmplというテンプレートファイルを用意してコンテナの/workdirにマウントして実行すると最後にrenderした結果を出力してくれます.
$ docker run --rm -v $PWD:/workdir {{ビルドしたコンテナ}}
基本文法
基本的な文法はGoのtext/template*2と同じです.それに加えていくつかの特殊な文法を使用することが出来ます(ここではそれらの区別に関して言及しません).ここではそれらのうちの基本的な部分を示します.
ちなみに,内部的にはSigilというテンプレートエンジンを使用しているため,Goが読める人はこっちを読んだ方が早いかも知れません.
パイプライン
実行結果をチェーンすることができます.前のコマンドの実行結果が,最後の引数として与えられます.つまり,
{{ did hoge | do }}
という式は
{{ do (did hoge) }}
と等価です.
変数宣言
{{ $x := "hoge" }}
これでxという変数を宣言したことになり,以下の様に呼び出すことが出来ます.
{{ $x }} # -> hoge
ifなどの構文中でも使用できます.
var
環境変数から値を取得します.内部ではos.Getenv()しているだけなので,存在しない場合は空文字列が返ります.
{{ var "VARIABLE" }}
if
条件分岐が使えます.
{{ if expr1 }}
expr1 is True
{{ elif expr2 }}
expr2 is True
{{ else }}
False
{{ end }}
また,比較演算子として以下のものが使用できます.
eq: arg1 == arg2 || arg1 == arg3 || …ne: arg1 != arg2lt: arg1 < arg2le: arg1 <= arg2gt: arg1 > arg2ge: arg1 >= arg2
論理演算子の文法は以下です.
{{ if and expr1 expr2 }}
expr1 and expr2 are True
{{ elif or expr1 expr2}}
expr1 or expr2 is True
{{ end }}
{{ if not expr }}
expr is not True
{{ end }}
ただしandを使用した場合,expr1,expr2はどちらも評価されます.
range
ループ表現が使えます.ただしこのexprはiterableなオブジェクトである必要があります.具体的には後編で記述します.
{{ range expr }}
{{ . }}
{{ end }}
{{.}}で現在参照しているオブジェクトを取得できます.
補助的な文法
これらの表現はいずれもパイプラインでチェーンして文字列を受取り,文字列を返します*3.
default
値が存在しない場合,デフォルト値を設定します.
{{ var "NOT_EXISTS_VAR" | default "default"}}
capitalize
最初の1文字が小文字の時,大文字に変換します.
{{ "abcd" | capitalize }} # -> Abcd
lower
全て小文字にします.
{{ "AbCdEf" | lower }} # -> abcdef
upper
全て大文字にします.
{{ "AbCdEf" | upper }} # -> ABCDEF
replace
与えたテキスト中の特定の文字列を全て置き換えます.内部的にはstrings.Replace()しているだけなので,正規表現などを使うことは出来ません.
{{ "Target Text (old)" | replace "old" "new" }} # -> Target Text (new)
trim
文字列の両端から指定文字を取り除きます.具体的には内部でstrings.Trim()の第二引数に\nを渡しているので,改行コードと空白が両端から取り除かれます.
docker-compose.ymlなどでyamlで複数行の文字列を渡したときも全体の両端しか認識しないため,文中の改行は保持されます.
{{ " a b c " | trim }} # -> a b c
indent
二行目以降の先頭に特定の文字列を付加します.使い方がイマイチ分からないですが,第二引数で渡した文字列を\nを認識して分割,2つめ以降の要素の先頭に第一引数の文字列を付加します.
{{ "This is a pen.\nThis is not a pen." | indent "a" }}
結果は以下の様になります.
This is a pen. aThis is not a pen.
len
文字列の長さを返します.
{{ "abcd" | len }} # -> 4
Sigilにはあるが使えないもの
substr
not definedと言われてしまいました.
改行の取り扱い
goのtemplateのように-で改行を制御することが(試した限りでは)できないようです.つまり,制御構文を書いた行は空行になってしまいます.
# 以下のif文をrender {{ if eq 4 (len "abcd") }} This term has 4 characters. {{ else }} This term has not 4 characters. {{ end }}
上の様なコードをrenderすると以下の様になります.
# 以下のif文をrender
This term has 4 characters.
あまり空行が問題になることは無いかとは思いますが,気になる場合は以下の様にワンラインで書く方が良いかも知れません.
{{ if eq 4 (len "abcd") }} This term has 4 characters.{{ else }} This term has not 4 characters.{{ end }}
前編まとめ
このように,いちいちsedでコンフィグを書き換えなくても.tmplファイルを用意すれば簡単に埋め込みが出来ます.
正直これくらいまでがこのツールの良い使い道でこれ以上は無理しすぎではという感じもしますが,一応出来るので後編で紹介していきます.
[ 2017/09/22 追記 ] 後編書きました.
*1:Golangのtemplate機能ではここにあるように-の有無で改行を制御できるのですが,renderではinvalidだと怒られてしまいました. developers.eure.jp
*2:template - The Go Programming Language
*3:別に使い方としてそう決められているわけでは無いですが,主な使い方としてはそうなるでしょう.