【问题标题】:Convert terraform resourceData of type map[string]interface{} to struct将 map[string]interface{} 类型的 terraform resourceData 转换为 struct
【发布时间】:2022-07-06 07:32:07
【问题描述】:

我正在创建一个自定义 terraform 提供程序,但遇到了这个问题。 我试图将schema.TypeList 字段转换为结构,TypeList 看起来像这样:

"template": {
                Type:     schema.TypeList,
                Required: true,
                ForceNew: false,
                Elem: &schema.Resource{
                    Schema: map[string]*schema.Schema{
                        "lists_test": {
                            Type:     schema.TypeSet,
                            Required: true,
                            ForceNew: false,
                            Elem: &schema.Schema{
                                Type: schema.TypeString,
                            },
                        },
                        "name_test": {
                            Type:     schema.TypeString,
                            Required: true,
                            ForceNew: false,
                        },
},},

我试图对齐的结构看起来像这样:

type TestStruct struct {
    NameTest string   `json:"name_test"`
    ListsTests   []string `json:"lists_test"`
}

我尝试了几个解决方案,例如我尝试将其解组为 json。如下所示:

template := d.Get("template").([]interface{})[0].(map[string]interface{})
templateStr, err := json.Marshal(template)
templateConverted := &TestStruct{}
json.Unmarshal(template, templateConverted)

但是,我收到错误 json: unsupported type: SchemaSetFunc,这可能是因为它试图编组 schema.Schema 类型而不是 map[string]interface{} 类型,这让我感到困惑。我也尝试使用gohcl.DecodeBody,但我放弃了这个想法,因为它的用法似乎更倾向于直接读取 tf 文件而不是*schema.ResourceData 类型。

有没有人在处理这种情况时有同样的经历?任何帮助或建议表示赞赏。谢谢!

【问题讨论】:

    标签: json go terraform terraform-provider-gcp hcl


    【解决方案1】:

    Terraform 的旧 SDK (SDKv2) 并非围绕解码为标记结构的范例设计,而是希望您使用 d.Get 并手动键入断言单个值,在您的情况下可能看起来像这样:

      raw := d.Get("template").([]interface{})[0].(map[string]interface{})
      t := &TestStruct{
          NameTest: raw["name_test"].(string),
          ListsTests: make([]string, len(raw["lists_test"].([]interface{})),
      }
      for i, itemRaw := range raw["lists_test"].([]interface{}) {
        t.ListsTests[i] = itemRaw.(string)
      }
    

    大多数 Terraform 提供者的惯用风格是为每个复杂类型的属性在单独的函数中编写这样的逻辑,其中每个函数都返回目标平台 SDK 中适当类型的对象。通常还会有一个相反方向的匹配函数:给定来自目标平台 SDK 的对象,返回一个 map[string]interface{},可以使用 d.Set 分配给该属性。


    但是,仅仅因为 SDK 中没有内置的东西来处理这个问题,这并不意味着您不能使用其他更通用的实用程序库来在任何 Go 程序中使用。

    一个示例库是github.com/mitchellh/mapstructure,它的设计正是为了实现您心中的目标:获取某个接口类型的值并尝试使用反射将其适应标记的结构类型。

    如果你想使用那个库,那么你需要用mapstructure:而不是json:注释你的结构,然后将你的raw值传递给the mapstructure.Decode function

      raw := d.Get("template").([]interface{})[0].(map[string]interface{})
      var t TestStruct
      err := mapstructure.Decode(raw, &t)
    

    由于 SDKv2 中的 schema.ResourceData 抽象保证根据您定义的架构返回特定的数据类型,因此只要您的架构和目标类型匹配,您通常不会从 mapstructure.Decode 收到错误,但这仍然是一个好主意无论如何都要检查错误,因为否则您的 t 值可能不会完全填充,从而导致下游的混乱行为。

    这不是官方提供程序中使用的典型实现样式,但是如果您发现这种样式更方便或更易于维护,那么以这种方式编写您的提供程序并没有真正的危害。


    或者,如果您尚未对 SDKv2 进行深入投资,那么您可能希望考虑改用 Terraform Plugin Framework。除了围绕现代 Terraform 的类型系统进行设计(而 SDKv2 是为 Terraform v0.11 及更早版本设计的)之外,它还支持一种更像您的目标的编程风格,使用 tfsdk.Plan.Gettfsdk.Plan.GetAttribute 等方法可以直接解码成适当形状和适当标记的“正常”围棋值。

    我不能轻易地展示一个例子,因为它会假定一个提供者以完全不同的方式编写,但希望你能从这两个函数的签名中看到它们是如何使用的。 Accessing State, Config, and Plan 中还有更多评论和示例。

    【讨论】:

      猜你喜欢
      • 2015-01-14
      • 1970-01-01
      • 2021-08-09
      • 2021-05-26
      • 1970-01-01
      • 2021-05-11
      • 1970-01-01
      • 2019-03-30
      相关资源
      最近更新 更多