首页 > 解决方案 > 无法将 terraform variables.tf 文件读入 may go 程序

问题描述

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

代码:

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

堆栈驱动程序问题可悲的是 hcl1

我正在参考的博客文章。

标签: goterraformhcl

解决方案


看起来它是库的错误/功能,因为一旦您更改string"string",例如,

variable "image_id" {
  type        = string
  ...

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

gohcl.DecodeBody成功。

- - 更新 - -

所以,他们确实在 Terraform 中使用了这个包,但是他们自定义解析配置,即他们不使用gohcl.DecodeBody. 他们还通过使用(比较)来自定义处理 属性。正如您所假设的,它们确实使用了自定义类型for ,但使用自定义解析您不必这样做。typehcl.ExprAsKeyworddescriptiontype

下面是一个工作示例:

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
}

推荐阅读