Go で HCL ファイルを読み取る
2025-04-22
HCL は Terraform で採用されているフォーマット。 構造化したり改行を挟めるので、人間にとって読みやすい、と個人的に思う。 プログラムで扱うにはどうすればいいのかと思い、Go 言語で HCL ファイルを読み取ってみた。
ライブラリ
hashicorp/hcl を用いて HCL をパースする。 HashiCorp 公式のリポジトリ。おそらく Terraform の内部でも使用されている (はず)
コード
</>
メモ
サンプル
この HCL ファイルを読み取ってみる
struct tag でフィールドを指定する
オプショナルに。
sample.hcl を読み取る
parser にバイト列を渡すこともできる。
デコード
Variable を実現するには?
Partial Decoding をすれば、Terraform の `variable` のように値を参照してセットすることができる。
</>
// 例えば..
const "apikey" {
value = "aaa"
}
service {
apikey = const.apikey
}
下記のページや issue のコメントが分かりやすくありがたい。
サンプルコードを書いてみた。
</>
package main
import (
"fmt"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2/hclparse"
"github.com/zclconf/go-cty/cty"
)
var mainhcl = `
const "apikey" {
value = "aaa"
}
service {
apikey = const.apikey
}
`
type Const struct {
Name string `hcl:"name,label"`
Value string `hcl:"value"`
}
type Service struct {
Apikey string `hcl:"apikey"`
}
func Parse() {
parser := hclparse.NewParser()
file, diags := parser.ParseHCL([]byte(mainhcl), "main.hcl")
if diags.HasErrors() {
panic(diags)
}
// 先に const だけデコードする
type PartialConfig struct {
Consts []Const `hcl:"const,block"`
Remain hcl.Body `hcl:",remain"` // see https://hcl.readthedocs.io/en/latest/go_decoding_gohcl.html#partial-decoding
}
var partial PartialConfig
if diags := gohcl.DecodeBody(file.Body, nil, &partial); diags.HasErrors() {
panic(diags)
}
// デコードした値を hcl.EvalContext にマッピング
constmap := make(map[string]cty.Value)
for _, c := range partial.Consts {
constmap[c.Name] = cty.StringVal(c.Value)
}
evar := &hcl.EvalContext{
Variables: map[string]cty.Value{
"const": cty.ObjectVal(constmap),
},
}
type Config struct {
Service Service `hcl:"service,block"`
Consts []Const `hcl:"const,block"`
}
var config Config
// 第2引数で hcl.EvalContext を渡す
if diags := gohcl.DecodeBody(file.Body, evar, &config); diags.HasErrors() {
panic(diags)
}
fmt.Println(config.Service.Apikey) // aaa
}
他にも方法があるかもしれない。 また Terraform の場合は、リソースに依存関係があるので、もっと高度な実装がされていると思う。