英文:
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'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'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
}
->
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("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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论