简体   繁体   English

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

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

I'm creating a custom terraform provider and I came across this issue.我正在创建一个自定义 terraform 提供程序,我遇到了这个问题。 I was trying to convert a schema.TypeList field into a struct, the TypeList looks something like this:我试图将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,
                        },
},},

and the struct that I'm trying to align to looks something like this:我试图对齐的结构看起来像这样:

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

I tried a couple of solutions, for instance I tried unmarshalling it to json.我尝试了几个解决方案,例如我尝试将其解组为 json。 Something like below:如下所示:

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

however, I'm getting an error json: unsupported type: SchemaSetFunc , which is probably because it's trying to marshal a schema.Schema type instead of map[string]interface{} type, which confuses me.但是,我收到一个错误json: unsupported type: SchemaSetFunc ,这可能是因为它试图编组schema.Schema类型而不是 map[string]interface{} 类型,这让我感到困惑。 I also tried to use gohcl.DecodeBody but I abandoned the idea since it's usage seems more inclined into reading direct tf files rather than *schema.ResourceData types.我也尝试使用gohcl.DecodeBody但我放弃了这个想法,因为它的用法似乎更倾向于读取直接 tf 文件而不是*schema.ResourceData类型。

Does anyone had the same experience dealing with this type of scenario?有没有人在处理这种情况时有相同的经验? Any help or suggestion is appreciated.任何帮助或建议表示赞赏。 Thank you!谢谢!

Terraform's older SDK (SDKv2) is not designed around the paradigm of decoding into a tagged structure, and instead expects you to use d.Get and manually type-assert individual values, which in your case would perhaps look something like this: 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)
  }

The idiomatic style for most Terraform providers is to write logic like this in separate functions for each complex-typed attribute, where each returns an object of the appropriate type in the target platform's SDK.大多数 Terraform 提供者的惯用风格是为每个复杂类型的属性在单独的函数中编写这样的逻辑,其中每个函数都返回目标平台 SDK 中适当类型的对象。 There would typically also be a matching function for going in the opposite direction: given an object from the target platform's SDK, return a map[string]interface{} that can be assigned to this attribute using d.Set .通常还会有一个相反方向的匹配函数:给定来自目标平台的 SDK 的对象,返回一个可以使用d.Set分配给该属性的map[string]interface{}


However, just because there isn't something built in to the SDK to handle this, that doesn't mean you can't use other libraries that are more general utilities for use in any Go programs.然而,仅仅因为 SDK 中没有内置的东西来处理这个问题,这并不意味着您不能使用其他更通用的实用程序库来在任何 Go 程序中使用。

One example library is github.com/mitchellh/mapstructure , which is designed for exactly the goal you have in mind: to take a value of some interface type and try to use reflection to fit it onto a tagged structure type.一个示例库是github.com/mitchellh/mapstructure ,它的设计正是为了实现您的目标:获取某种接口类型的值并尝试使用反射将其拟合到标记的结构类型上。

If you want to use that library then you would need to annotate your structure with mapstructure: , instead of the json: ones, and then pass your raw value to the mapstructure.Decode function :如果要使用该库,则需要使用mapstructure:而不是json:注释结构,然后将raw值传递给mapstructure.Decode函数

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

Since the schema.ResourceData abstraction in SDKv2 guarantees to return specific data types based on the schema you defined, you should not typically get errors from mapstructure.Decode as long as your schema and your target type match, but still a good idea to check for errors anyway because otherwise your t value may not be completely populated, causing confusing broken behavior downstream.由于 SDKv2 中的schema.ResourceData抽象保证根据您定义的架构返回特定的数据类型,因此只要您的架构和目标类型匹配,您通常不会从mapstructure.Decode中得到错误,但检查一下仍然是个好主意无论如何都会出错,因为否则您的t值可能不会完全填充,从而导致下游的混乱行为。

This is not a typical implementation style used in the official providers, but there's no real harm in writing your provider in this way if you find this style more convenient, or easier to maintain.这不是官方提供程序中使用的典型实现样式,但是如果您发现这种样式更方便或更易于维护,那么以这种方式编写您的提供程序并没有真正的危害。


Alternatively, if you are not already deeply invested in SDKv2 then you may wish to consider using Terraform Plugin Framework instead.或者,如果您尚未对 SDKv2 进行深入投资,那么您可能希望考虑使用Terraform 插件框架 As well as being designed around the type system of modern Terraform (whereas SDKv2 was designed for Terraform v0.11 and earlier), it also supports a programming style more like what you are aiming for, with methods like tfsdk.Plan.Get and tfsdk.Plan.GetAttribute that can decode directly into an appropriately-shaped and appropriately tagged "normal" Go value.除了围绕现代 Terraform 的类型系统进行设计(而 SDKv2 是为 Terraform v0.11 及更早版本设计的),它还支持一种更像您的目标的编程风格,使用tfsdk.Plan.Gettfsdk.Plan.GetAttribute等方法tfsdk.Plan.GetAttribute可以直接解码为适当形状和适当标记的“正常”围棋值。

I can't easily show an example of that because it would presume a provider written in quite a different way, but hopefully you can see from the signature of those two functions how they might be used.我不能轻易地展示一个例子,因为它会假定一个提供者以完全不同的方式编写,但希望你能从这两个函数的签名中看到它们是如何使用的。 There's some more commentary and examples in Accessing State, Config, and Plan . Accessing State、Config 和 Plan中有更多评论和示例。

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

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