HCL解码:具有多个标签的块

huangapple go评论82阅读模式
英文:

HCL Decoding: Blocks with multiple labels

问题

我的目标是解析HCL配置(Terraform配置),然后将收集到的关于变量、输出、资源块和数据块的数据写入Markdown文件。

变量和输出没有问题,但是当我尝试解码具有多个标签的资源块时出现问题。

可以工作的示例:

variable "foo" {
  type = "bar"
}

无法工作的示例:

resource "foo" "bar" {
 name = "biz"
}

错误信息:Extraneous label for resource; Only 1 labels (name) are expected for resource blocks.

类型声明代码:

import (
	"log"
	"os"
	"strconv"

	"github.com/hashicorp/hcl/v2"
	"github.com/hashicorp/hcl/v2/gohcl"
	"github.com/hashicorp/hcl/v2/hclsyntax"
)

type Variable struct {
	Name        string         `hcl:",label"`
	Description string         `hcl:"description,optional"`
	Sensitive   bool           `hcl:"sensitive,optional"`
	Type        *hcl.Attribute `hcl:"type,optional"`
	Default     *hcl.Attribute `hcl:"default,optional"`
	Options     hcl.Body       `hcl:",remain"`
}

type Output struct {
	Name        string   `hcl:",label"`
	Description string   `hcl:"description,optional"`
	Sensitive   bool     `hcl:"sensitive,optional"`
	Value       string   `hcl:"value,optional"`
	Options     hcl.Body `hcl:",remain"`
}

type Resource struct {
	Name    string   `hcl:"name,label"`
	Options hcl.Body `hcl:",remain"`
}

type Data struct {
	Name    string   `hcl:"name,label"`
	Options hcl.Body `hcl:",remain"`
}

type Config struct {
	Outputs   []*Output   `hcl:"output,block"`
	Variables []*Variable `hcl:"variable,block"`
	Resources []*Resource `hcl:"resource,block"`
	Data      []*Data     `hcl:"data,block"`
}

解码代码:

func createDocs(hclPath string) map[string][]map[string]string {
	var variables, outputs []map[string]string

	parsedConfig := make(map[string][]map[string]string)
	hclConfig := make(map[string][]byte)

	c := &Config{}

	// 遍历所有Terraform文件,并将内容保存在hclConfig映射中
	for _, file := range filesInDirectory(hclPath, ".tf") {
		fileContent, err := os.ReadFile(hclPath + "/" + file.Name())
		if err != nil {
			log.Fatal(err)
		}
		hclConfig[file.Name()] = fileContent
	}

	// 遍历所有文件内容
	for k, v := range hclConfig {
		parsedConfig, diags := hclsyntax.ParseConfig(v, k, hcl.Pos{Line: 1, Column: 1})
		if diags.HasErrors() {
			log.Fatal(diags)
		}

		diags = gohcl.DecodeBody(parsedConfig.Body, nil, c)
		if diags.HasErrors() {
			log.Fatal(diags)
		}
	}

	for _, v := range c.Variables {
		var variableType string
		var variableDefault string

		if v.Type != nil {
			variableType = (v.Type.Expr).Variables()[0].RootName()
		}

		if v.Default != nil {
			variableDefault = (v.Default.Expr).Variables()[0].RootName()
		}

		variables = append(variables, map[string]string{"name": v.Name, "description": v.Description,
			"sensitive": strconv.FormatBool(v.Sensitive), "type": variableType, "default": variableDefault})
	}

	for _, v := range c.Outputs {
		outputs = append(outputs, map[string]string{"name": v.Name, "description": v.Description,
			"sensitive": strconv.FormatBool(v.Sensitive), "value": v.Value})
	}

	parsedConfig["variables"], parsedConfig["outputs"] = variables, outputs

	return parsedConfig
}

问题: 如何解析资源块中的多个标签?

英文:

My goal is to parse a HCL configuration (Terraform Configuration) and then write the collected data about variables, outputs, resources blocks and data blocks into a Markdown file.

Variables and outputs are no problem, however, as soon as I trying to decode resource blocks, which have multiple labels.

Works:

variable "foo" {
  type = "bar"
}

Doesn't Work:

resource "foo" "bar" {
 name = "biz"
}

Error: Extraneous label for resource; Only 1 labels (name) are expected for resource blocks.

Type declaration Code:

import (
"log"
"os"
"strconv"
"github.com/hashicorp/hcl/v2"
"github.com/hashicorp/hcl/v2/gohcl"
"github.com/hashicorp/hcl/v2/hclsyntax"
)
type Variable struct {
Name        string         `hcl:",label"`
Description string         `hcl:"description,optional"`
Sensitive   bool           `hcl:"sensitive,optional"`
Type        *hcl.Attribute `hcl:"type,optional"`
Default     *hcl.Attribute `hcl:"default,optional"`
Options     hcl.Body       `hcl:",remain"`
}
type Output struct {
Name        string   `hcl:",label"`
Description string   `hcl:"description,optional"`
Sensitive   bool     `hcl:"sensitive,optional"`
Value       string   `hcl:"value,optional"`
Options     hcl.Body `hcl:",remain"`
}
type Resource struct {
Name    string   `hcl:"name,label"`
Options hcl.Body `hcl:",remain"`
}
type Data struct {
Name    string   `hcl:"name,label"`
Options hcl.Body `hcl:",remain"`
}
type Config struct {
Outputs   []*Output   `hcl:"output,block"`
Variables []*Variable `hcl:"variable,block"`
Resources []*Resource `hcl:"resource,block"`
Data      []*Data     `hcl:"data,block"`
}

Decoding Code:

func createDocs(hclPath string) map[string][]map[string]string {
var variables, outputs []map[string]string
parsedConfig := make(map[string][]map[string]string)
hclConfig := make(map[string][]byte)
c := &Config{}
// Iterate all Terraform files and safe the contents in the hclConfig map
for _, file := range filesInDirectory(hclPath, ".tf") {
fileContent, err := os.ReadFile(hclPath + "/" + file.Name())
if err != nil {
log.Fatal(err)
}
hclConfig[file.Name()] = fileContent
}
// Iterate all file contents
for k, v := range hclConfig {
parsedConfig, diags := hclsyntax.ParseConfig(v, k, hcl.Pos{Line: 1, Column: 1})
if diags.HasErrors() {
log.Fatal(diags)
}
diags = gohcl.DecodeBody(parsedConfig.Body, nil, c)
if diags.HasErrors() {
log.Fatal(diags)
}
}
for _, v := range c.Variables {
var variableType string
var variableDefault string
if v.Type != nil {
variableType = (v.Type.Expr).Variables()[0].RootName()
}
if v.Default != nil {
variableDefault = (v.Default.Expr).Variables()[0].RootName()
}
variables = append(variables, map[string]string{"name": v.Name, "description": v.Description,
"sensitive": strconv.FormatBool(v.Sensitive), "type": variableType, "default": variableDefault})
}
for _, v := range c.Outputs {
outputs = append(outputs, map[string]string{"name": v.Name, "description": v.Description,
"sensitive": strconv.FormatBool(v.Sensitive), "value": v.Value})
}
parsedConfig["variables"], parsedConfig["outputs"] = variables, outputs
return parsedConfig
}

Question: How can I parse multiple labels from resource blocks?

答案1

得分: 1

你分享的错误是由于type Resource的定义引起的。Terraform中的resource块(和data块)需要两个标签,表示资源类型和名称。为了与你暗示的这些结构类型的模式匹配,你需要定义两个被标记为label的字段:

type Resource struct {
    Type    string   `hcl:"type,label"`
    Name    string   `hcl:"name,label"`
    Options hcl.Body `hcl:",remain"`
}

type Data struct {
    Type    string   `hcl:"type,label"`
    Name    string   `hcl:"name,label"`
    Options hcl.Body `hcl:",remain"`
}

尽管这对于你在这里展示的有限输入应该可以工作,但我想提醒你,你正在使用更高级的gohcl API,它只能解码与Go结构类型很好映射的HCL子集。Terraform本身使用较低级别的hcl.Bodyhcl.Expression API,这使得Terraform语言可以包含一些gohcl API无法直接表示的HCL特性。

根据你的目标,你可能会发现使用官方库terraform-config-inspect更好,它可以解析、解码和描述Terraform语言的一个子集,比HCL API本身提供了更高级的抽象层次。它还支持为Terraform v0.11及更高版本编写的模块,并且是Terraform Registry对模块进行分析的实现。

英文:

The error you shared is due to the definition of type Resource. resource blocks (and data blocks) in Terraform expect two labels, indicating the resource type and name. To match that in the schema you're implying with these struct types, you'll need to define to fields that are tagged as label:

type Resource struct {
Type    string   `hcl:"type,label"`
Name    string   `hcl:"name,label"`
Options hcl.Body `hcl:",remain"`
}
type Data struct {
Type    string   `hcl:"type,label"`
Name    string   `hcl:"name,label"`
Options hcl.Body `hcl:",remain"`
}

Although this should work for the limited input you showed here, I want to caution that you are using the higher-level gohcl API which can decode only a subset of HCL that maps well onto Go's struct types. Terraform itself uses the lower-level APIs of hcl.Body and hcl.Expression directly, which allows the Terraform language to include some HCL features that the gohcl API cannot directly represent.

Depending on what your goal is, you may find it better to use the official library terraform-config-inspect, which can parse, decode, and describe a subset of the Terraform language at a higher level of abstraction than the HCL API itself. It also supports modules written for Terraform versions going all the way back to Terraform v0.11, and is the implementation that backs the analysis of modules done by Terraform Registry.

huangapple
  • 本文由 发表于 2022年5月11日 15:42:33
  • 转载请务必保留本文链接:https://go.coder-hub.com/72197201.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定