【问题标题】:Unable to read terraform variables.tf files into may go program无法将 terraform variables.tf 文件读入 may go 程序
【发布时间】:2021-06-11 15:54:57
【问题描述】:

我正在尝试编写一个 go 程序,该程序读取 terraform variables.tf 并填充一个结构以供以后操作。但是,尝试“解析”文件时出现错误。我希望有人能告诉我我做错了什么:

代码:

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "os"

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

type Config struct {
    Upstreams []*TfVariable `hcl:"variable,block"`
}

type TfVariable struct {
    Name string `hcl:",label"`
    // Default     string `hcl:"default,optional"`
    Type        string `hcl:"type"`
    Description string `hcl:"description,attr"`
    // validation block
    Sensitive bool `hcl:"sensitive,optional"`
}

func main() {
    readHCLFile("examples/string.tf")
}

// Exits program by sending error message to standard error and specified error code.
func abort(errorMessage string, exitcode int) {
    fmt.Fprintln(os.Stderr, errorMessage)
    os.Exit(exitcode)
}

func readHCLFile(filePath string) {
    content, err := ioutil.ReadFile(filePath)
    if err != nil {
        log.Fatal(err)
    }

    fmt.Printf("File contents: %s", content) // TODO: Remove me

    file, diags := hclsyntax.ParseConfig(content, filePath, hcl.Pos{Line: 1, Column: 1})
    if diags.HasErrors() {
        log.Fatal(fmt.Errorf("ParseConfig: %w", diags))
    }

    c := &Config{}
    diags = gohcl.DecodeBody(file.Body, nil, c)
    if diags.HasErrors() {
        log.Fatal(fmt.Errorf("DecodeBody: %w", diags))
    }

    fmt.Println(c) // TODO: Remove me
}

错误

File contents: variable "image_id" {
  type        = string
  description = "The id of the machine image (AMI) to use for the server."
  sensitive   = false
}

variable "other_id" {
  type        = string
  description = "The id of the machine image (AMI) to use for the server."
  sensitive   = true
}
2021/03/13 19:55:49 DecodeBody: examples/string.tf:2,17-23: Variables not allowed; Variables may not be used here., and 3 other diagnostic(s)
exit status 1

堆栈驱动程序question 可悲的是 hcl1

Blog post我在引用。

【问题讨论】:

    标签: go terraform hcl


    【解决方案1】:

    看起来这是库的错误/功能,因为只要您将 string 更改为 "string",例如,

    variable "image_id" {
      type        = string
      ...
    

    variable "image_id" {
      type        = "string"
      ...
    

    gohcl.DecodeBody 成功。

    --- 更新 ---

    所以,他们确实在 Terraform 中使用了这个包,但他们 custom-parse configs,即他们不使用 gohcl.DecodeBody。他们还使用hcl.ExprAsKeywordcomparedescription)来custom-treattype 属性。正如您所假设的,他们确实使用custom type 来代替type,但通过自定义解析,您不必这样做。

    下面是一个工作示例:

    package main
    
    import (
        "fmt"
        "log"
        "os"
    
        "github.com/hashicorp/hcl/v2"
        "github.com/hashicorp/hcl/v2/gohcl"
        "github.com/hashicorp/hcl/v2/hclsyntax"
    )
    
    var (
        configFileSchema = &hcl.BodySchema{
            Blocks: []hcl.BlockHeaderSchema{
                {
                    Type:       "variable",
                    LabelNames: []string{"name"},
                },
            },
        }
    
        variableBlockSchema = &hcl.BodySchema{
            Attributes: []hcl.AttributeSchema{
                {
                    Name: "description",
                },
                {
                    Name: "type",
                },
                {
                    Name: "sensitive",
                },
            },
        }
    )
    
    type Config struct {
        Variables []*Variable
    }
    
    type Variable struct {
        Name        string
        Description string
        Type        string
        Sensitive   bool
    }
    
    func main() {
        config := configFromFile("examples/string.tf")
        for _, v := range config.Variables {
            fmt.Printf("%+v\n", v)
        }
    }
    
    func configFromFile(filePath string) *Config {
        content, err := os.ReadFile(filePath) // go 1.16
        if err != nil {
            log.Fatal(err)
        }
    
        file, diags := hclsyntax.ParseConfig(content, filePath, hcl.Pos{Line: 1, Column: 1})
        if diags.HasErrors() {
            log.Fatal("ParseConfig", diags)
        }
    
        bodyCont, diags := file.Body.Content(configFileSchema)
        if diags.HasErrors() {
            log.Fatal("file content", diags)
        }
    
        res := &Config{}
    
        for _, block := range bodyCont.Blocks {
            v := &Variable{
                Name: block.Labels[0],
            }
    
            blockCont, diags := block.Body.Content(variableBlockSchema)
            if diags.HasErrors() {
                log.Fatal("block content", diags)
            }
    
            if attr, exists := blockCont.Attributes["description"]; exists {
                diags := gohcl.DecodeExpression(attr.Expr, nil, &v.Description)
                if diags.HasErrors() {
                    log.Fatal("description attr", diags)
                }
            }
    
            if attr, exists := blockCont.Attributes["sensitive"]; exists {
                diags := gohcl.DecodeExpression(attr.Expr, nil, &v.Sensitive)
                if diags.HasErrors() {
                    log.Fatal("sensitive attr", diags)
                }
            }
    
            if attr, exists := blockCont.Attributes["type"]; exists {
                v.Type = hcl.ExprAsKeyword(attr.Expr)
                if v.Type == "" {
                    log.Fatal("type attr", "invalid value")
                }
            }
    
            res.Variables = append(res.Variables, v)
        }
        return res
    }
    

    为完整性添加,example/string.tf:

    variable "image_id" {
      type        = string
      description = "The id of the machine image (AMI) to use for the server."
      sensitive   = false
    }
    
    variable "other_id" {
      type        = string
      description = "The id of the machine image (AMI) to use for the server."
      sensitive   = true
    }
    

    【讨论】:

    • ` type = "string"` 在较新的 terraforms 0.12+ 中是不允许的,它必须是 type = string
    • 另外,variable.tf 文件是一个有效的 terraform 文件,并且 terraform 使用了 hcl 库(基于代码的两个代码都来自同一家公司)。感觉好像我做错了什么可能是Type string hcl:"type"`` 的结构条目不应该是字符串,如果是这样,我不确定它应该是什么。类型也可以等于numberlist(string)
    • @AnthonyT 我已经更新了答案,请看一下。
    • 非常感谢,我会在不久的将来尝试。
    • 非常感谢,我真的很想知道这个。
    【解决方案2】:

    由于 Terraform 语言广泛使用需要使用低级 HCL API 进行自定义编程的各种 HCL 功能,因此 Terraform 团队维护了一个 Go 库 terraform-config-inspect,该库对 Terraform 语言的理解足以提取有关顶级的静态元数据对象,包括变量。它还处理了 Terraform 允许在任何 .tf.tf.json 文件中与其他声明交错的变量定义这一事实;将它们放入variables.tf 只是一种约定。

    例如:

    mod, diags := tfconfig.LoadModule("examples")
    if diags.HasErrors() {
        log.Fatalf(diags.Error())
    }
    for _, variable := range mod.Variables {
        fmt.Printf("%#v\n", variable)
    }
    

    这个库与 Terraform Registry 用来生成 the documentation about module input variables 的代码相同,因此它支持 Terraform Registry 支持的所有 Terraform 语言版本(在撰写本文时,回到 Terraform v0.10 语言,因为这是第一个可以从注册表安装模块的版本)并且支持 HCL 原生语法和 Terraform 语言的 JSON 表示。

    【讨论】:

      猜你喜欢
      • 2017-04-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-06-23
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多