本日も乙

ただの自己満足な備忘録。

Go言語でHCL(HashiCorp Configuration Language)をパース/生成する

[toc]

Terraformでオートスケール設定をしています。設定の記述が簡単なのが良いですね。 パッケージやOSのアップデートなどにBlue-Green Deploymentにしたいなと考えていまして、 BlueとGreenのオートスケーリンググループ(ASG)の台数を設定ファイルに外だし(variables.tfvarsなど)にしたいと考えていました。

ASGの台数はデプロイ状況によって可変であるため、スクリプトで設定ファイルを変更しようと考えていました。 しかし、Terraformの設定ファイルはHCL(HashiCorp Configuration Language)というJSON互換でありながらも形式が全く異なるため、パースをどうするかを考えていました。 Terraform及び周辺ツールのソースやドキュメントを漁りながら調べた結果、Go言語で書かれているHashicorp製のHCLライブラリが使えることがわかりました。 今回は、そのライブラリを使ってHCLのパースと生成方法についてサンプルスクリプトを元に紹介します。

サンプル

サンプルはGitHubに公開しています。 https://github.com/ohsawa0515/hcl_parse_sample

試し方は簡単です。 パッケージ管理ツールであるgolang/depを使いますので、まだ入れていない場合はgo getします。

$ go get -u github.com/golang/dep/cmd/dep

dep ensureすると依存パッケージ(hashicorp/hcl)がvendorディレクトリ以下にダウンロードされます。

$ dep ensure

あとはgo runすれば実行されます。 variables.tfvarsをパース、中身を編集して、HCLを生成した結果を標準出力します。

パース

jsonyamlと同じようにUnmarshal関数を使うだけなので簡単です。 https://github.com/ohsawa0515/hcl_parse_sample/blob/master/main.go#L23-L30

d, err := ioutil.ReadFile("./variables.tfvars")
if err != nil {
    log.Fatal(err)
}
variables := Variables{}
hcl.Unmarshal(d, &variables)

値を書き換えやすくするため、データ構造に合わせて構造体を定義していましたが、ただパースするだけならinterface型で十分です。

生成

パースに比べて生成が若干面倒くさいです。というのも、jsonyamlのようなMarshal関数がないからです。また、Marshal相当の上手いこと生成してくれる関数は実装されていませんでした。 なので、一旦JSON型として生成した後、HCL型にパースし直して整形&出力するといった方法をとりました。 回りくどい方法ですが、これ以外の方法が見つからなかったです。もっと効率の良いやり方があればコメントください。 json2hclというJSONとHCLを相互変換するツールを参考にしています。

https://github.com/ohsawa0515/hcl_parse_sample/blob/master/main.go#L52-L66

b, err := json.Marshal(variables)
if err != nil {
    log.Fatal(err.Error())
}

ast, err := hcl.ParseBytes(b)
if err != nil {
    log.Fatal(err.Error())
}

out := new(bytes.Buffer)
if err := printer.Fprint(out, ast); err != nil {
    log.Fatal(err.Error())
}
fmt.Println(out)

最後に

HCLファイルをパース及び生成する方法を紹介しました。 ただ、Keyにダブルクオーテーションが入ったりと若干不満があるので、hcl.Marshalを早く実装してもらいたいものです。

参考