繁体   English   中英

将 map[string]interface{} 类型的 terraform resourceData 转换为 struct

[英]Convert terraform resourceData of type map[string]interface{} to struct

我正在创建一个自定义 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类型。

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

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 的对象,返回一个可以使用d.Set分配给该属性的map[string]interface{}


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

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

如果要使用该库,则需要使用mapstructure:而不是json:注释结构,然后将raw值传递给mapstructure.Decode函数

  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 插件框架 除了围绕现代 Terraform 的类型系统进行设计(而 SDKv2 是为 Terraform v0.11 及更早版本设计的),它还支持一种更像您的目标的编程风格,使用tfsdk.Plan.Gettfsdk.Plan.GetAttribute等方法tfsdk.Plan.GetAttribute可以直接解码为适当形状和适当标记的“正常”围棋值。

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM