英文:
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.Body
和hcl.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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论