💨
HCLファイルを hashicorp/hcl で読み書きする
HCLファイルをプログラムから読み書きする方法として hashicorp/hcl がある。
ここのパッケージに一通りの関数がそろっているのでこれを試してみる。
hclsimple
hclsimpleはHCLファイルをパースしてGoの構造体に読み込むパッケージ。
タグ情報付きの構造体を渡して読み込むので、事前にファイルの構造を定義しておく必要がある。
type Config struct {
LogLevel string `hcl:"log_level"`
}
var config Config
// config.hclファイルから読んでデコードする
err := hclsimple.DecodeFile("config.hcl", nil, &config)
if err != nil {
fmt.Printf("Failed to load configuration: %s\n", err)
return
}
fmt.Printf("Configuration in config.hcl is %#v\n", config)
ファイルからではなく変数から読み込む場合はDecode関数を利用する。
type Config struct {
LogLevel string `hcl:"log_level"`
}
var config Config
var hclBody = `
log_level = "info"
`
// hclBody変数から読んでデコードする
err = hclsimple.Decode("config.hcl", []byte(hclBody), nil, &config)
if err != nil {
fmt.Printf("Failed to load configuration: %s\n", err)
return
}
fmt.Printf("Configuration is %#v\n", config)
hclparse
hclparseはファイルをパースしてHCLのシンタックスチェックを行う。また、パースしたファイル名と内容の対応関係を管理する。
シンタックスチェック自体はhclsyntaxパッケージで行うが、基本的にはhclparse経由で利用する。
p := hclparse.NewParser()
file, diags := p.ParseHCLFile("sample.hcl")
if diags.HasErrors() {
fmt.Println(diags.Error())
return
}
fmt.Printf("%s", file.Bytes)
// Filesではファイル名とコンテンツのmapが返る
for k, v := range p.Files() {
fmt.Printf("filename: %s\n", k)
fmt.Printf("contents: %s\n", v.Bytes)
}
gohcl
gohcl はHCLデータをパースしてGoの構造体に読み込むパッケージ。
hclsimpleはファイルや[]byteのデータを読み込んでパースするが、gohclはhcl.Bodyからデータを読み込んでパースする点が異なる。
var config Config
var varConfig = `
log_level = "info"
`
file, diags := hclsyntax.ParseConfig([]byte(varConfig), "config.hcl", hcl.Pos{Line: 1, Column: 1})
if diags.HasErrors() {
fmt.Printf("failed to parse file %s\n", diags)
return
}
diags = gohcl.DecodeBody(file.Body, nil, &config)
if diags.HasErrors() {
fmt.Printf("failed to decode %s\n", diags)
return
}
fmt.Printf("config file is %#v\n", config)
hclwrite
hclwriteはHCLデータを生成するたに利用するパッケージ。
key = value
形式のAttribute、res {...}
形式のBlock、複数のAttributeやBlockで構成されるBodyでHCLデータを構築する。
また、型システムを実現するためにctyを利用しており、hclwriteでは特にctyで型を指定してデータを構築する。
f := hclwrite.NewEmptyFile()
rootBody := f.Body()
// foo = "bar" を構築する
rootBody.SetAttributeValue("foo", cty.StringVal("bar"))
// 以下のHCLを構築する
// resource "aws_s3_bucket" "this" {
// bucket = "sample-bucket-name"
// tags = {
// baz = true
// foo = 10
// }
// }
block := hclwrite.NewBlock("resource", []string{"aws_s3_bucket", "this"})
blockBody := block.Body()
blockBody.SetAttributeValue("bucket", cty.StringVal("sample-bucket-name"))
blockBody.SetAttributeValue("tags", cty.ObjectVal(map[string]cty.Value{
"baz": cty.True,
"foo": cty.NumberIntVal(10),
}))
rootBody.AppendBlock(block)
fmt.Printf("%s", f.Bytes())
Discussion