为什么在Terraform中添加schema.TypeSet的元素会强制执行替换操作?

huangapple go评论55阅读模式
英文:

Why does adding elements of schema.TypeSet forces replacement in Terraform?

问题

下面是提供的代码部分的中文翻译:

我们正在构建一个新的TF提供程序。

我们的模式定义如下:

"foo": {
Type: schema.TypeInt,
...
},
"bar": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"xxx": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},
"yyy": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},
"zzz": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
},
},
},


所以在顶层的`bar`属性上没有`ForceNew: true`,但是当我从以下更新我的资源时:

resource "aaa" "before" {
foo = 2
}

->

resource "aaa" "before" {
foo = 2
bar {
xxx = "aaa"
yyy = "bbb"
zzz = 3
}
}


然而我可以看到
  • bar { # 强制替换
    + xxx = "aaa"
    + yyy = "bbb"
    + zzz = 3
    }

<details>
<summary>英文:</summary>

Context: we&#39;re building a new TF provider.

Our schema definition looks as follows:

"foo": {
Type: schema.TypeInt,
...
},
"bar": {
Type: schema.TypeSet,
Optional: true,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
"xxx": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},
"yyy": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
ValidateFunc: validation.StringIsNotEmpty,
},
"zzz": {
Type: schema.TypeInt,
Required: true,
ForceNew: true,
},
},
},
},


So there&#39;s no `ForceNew:     true,` on for a `bar` attribute on a top level but when I update my resource from

resource "aaa" "before" {
foo = 2
}

-&gt;

resource "aaa" "before" {
foo = 2
bar {
xxx = "aaa"
yyy = "bbb"
zzz = 3
}
}


and yet I can see 
  • bar { # forces replacement
    + xxx = "aaa"
    + yyy = "bbb"
    + zzz = 3
    }

</details>


# 答案1
**得分**: 2

The SDKv2 "ForceNew" feature is a convenience helper for a common case where any change to a particular argument requires replacing an object.

It seems like in your case you need a more specific rule. From what you've described it seems like you'd need to replace only if there's an item being _removed_ from the set, because adding new items and removing old items are the only two possible changes to a set.

Any time when the built-in helpers don't work you can typically implement a more specific rule using arbitrary code by implementing [a `CustomizeDiff` function](https://developer.hashicorp.com/terraform/plugin/sdkv2/resources/customizing-differences).

A `CustomizeDiff` function with similar behavior to `ForceNew` would have a structure like the following:

```go
func exampleCustomizeDiff(d *ResourceDiff, meta interface{}) error {
    old, new := d.GetChange("bar")
    if barChangeNeedsReplacement(old, new) {
        d.ForceNew("bar")
    }
    return nil
}

You will need to provide a definition of barChangeNeedsReplacement that implements whatever logic decides that replacement is needed.

I believe an attribute of TypeSet will appear in old and new as *schema.Set values, and so if you type-assert to that type then you can use the methods of that type to perform standard set operations to make your calculation.

For example, if the rule were to require replacement only if there is an item in old that isn't in new then you could perhaps write it like this:

func barChangeNeedsReplacement(old, new any) bool {
    oldSet := old.(*schema.Set)
    newSet := new.(*schema.Set)

    removedSet := oldSet.Difference(newSet)
    return removedSet.Len() > 0
}

If that isn't quite the rule you wanted then hopefully you can see how to modify this example to implement the rule you need.

英文:

The SDKv2 ForceNew feature is a convenience helper for a common case where any change to a particular argument requires replacing an object.

It seems like in your case you need a more specific rule. From what you've described it seems like you'd need to replace only if there's an item being removed from the set, because adding new items and removing old items are the only two possible changes to a set.

Any time when the built-in helpers don't work you can typically implement a more specific rule using arbitrary code by implementing a CustomizeDiff function.

A CustomizeDiff function with similar behavior to ForceNew would have a structure like the following:

func exampleCustomizeDiff(d *ResourceDiff, meta interface{}) error {
    old, new := d.GetChange(&quot;bar&quot;)
    if barChangeNeedsReplacement(old, new) {
        d.ForceNew(&quot;bar&quot;)
    }
    return nil
}

You will need to provide a definition of barChangeNeedsReplacement that implements whatever logic decides that replacement is needed.

I believe an attribute of TypeSet will appear in old and new as *schema.Set values, and so if you type-assert to that type then you can use the methods of that type to perform standard set operations to make your calculation.

For example, if the rule were to require replacement only if there is an item in old that isn't in new then you could perhaps write it like this:

func barChangeNeedsReplacement(old, new any) bool {
    oldSet := old.(*schema.Set)
    newSet := new.(*schema.Set)

    removedSet := oldSet.Difference(newSet)
    return removedSet.Len() &gt; 0
}

If that isn't quite the rule you wanted then hopefully you can see how to modify this example to implement the rule you need.

huangapple
  • 本文由 发表于 2023年2月9日 01:11:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/75389347.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定