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

然而,只要我try 解码具有多个标签的资源块,变量和输出就没有问题.

Works:

variable "foo" {
  type = "bar"
}

Doesn't Work:

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

错误: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:如何解析资源块中的多个标签?

推荐答案

您共享的错误是由于type Resource的定义造成的.Terraform中的resource个区块(和data个区块)需要two个标签,指示资源类型和名称.为了在模式中与这些 struct 类型相匹配,需要定义标记为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,它只能解码HCL的一个子集,而HCL很好地映射到Go的 struct 类型上.Terraform本身直接使用较低级别的API hcl.Bodyhcl.Expression,这允许Terraform语言包含一些gohcl API无法直接表示的HCL特性.

根据您的目标,您可能会发现使用官方库terraform-config-inspect更好,它可以在比HCL API本身更高的抽象级别上解析、解码和描述Terraform语言的子集.它还支持为Terraform v0版本编写的模块.是支持Terraform Registry对模块进行分析的实现.

Go相关问答推荐

JWT库返回及时无效的令牌有效

Go Net/http路由

向API网关终结点发出POST请求时出现AWS Lambda With Go:";Rune me.InvalidEntrypoint";错误

Go协议缓冲区导入问题

在Golang中,@LATEST和@UPGRADE特殊查询有什么不同?

编辑时保留YAML文件中的单引号

ChromeDriver不存在(高朗selenium)

Docker Compose Health Check未退出,错误为无法启动

重新赋值变量时未清除动态类型-这是错误吗?

转到 bufio.Writer、gzip.Writer 并上传到内存中的 AWS S3

如何将验证器标记添加到嵌套字段

Go 中的 Azure JWT 验证不起作用

如何以干净的方式在中间件中注入 repo 或服务?

Golang 中的泛型类型转换

Go:从 ssl 证书中获取 'subject/unstructeredName' 的值

在 Golang 模板中计算时间/持续时间

测试包外文件时的 Golang 测试覆盖率

函数参数的判断顺序是什么?

comparable和any有什么区别?

如何将类型转换为字节数组golang