hashicorp / hashicorp/terraform-plugin-framework
SetNestedAttributes plan does not like single nested compute null values
- Dominant language
- Go
- Stars
- 384
- Forks
- 107
- Avg merge
- 3m
- Merged PRs (30d)
- 1
Description
I have very hard times to make SetNestedAttributes properly working when it has optional nested attributes and with optional computed attributes.
It looks like null values are not properly recognised or ignored.
I am not sure if am doing something wrong here, I tried a couple of combinations with UseStateForUnknown modifiers, but it does not really change anything.
### Module version
```
github.com/hashicorp/terraform-plugin-framework v0.6.1
```
### Relevant provider source code
```go
type (
podResourceBug struct{}
podResourceBugType struct{}
podResourceBugData struct {
Mounts []*podResourceBugDataMount `tfsdk:"mounts"`
}
podResourceBugDataMount struct {
Name types.String `tfsdk:"name"`
A *podResourceBugDataNested `tfsdk:"a"`
B *podResourceBugDataNested `tfsdk:"b"`
}
podResourceBugDataNested struct {
Path types.String `tfsdk:"path"`
Optional types.Bool `tfsdk:"optional"`
}
)
func (t podResourceBugType) GetSchema(ctx context.Context) (tfsdk.Schema, diag.Diagnostics) {
return tfsdk.Schema{
Attributes: map[string]tfsdk.Attribute{
"mounts": {
Optional: true,
PlanModifiers: tfsdk.AttributePlanModifiers{
tfsdk.RequiresReplace(),
},
Attributes: tfsdk.SetNestedAttributes(
map[string]tfsdk.Attribute{
"name": {
Type: types.StringType,
Required: true,
PlanModifiers: tfsdk.AttributePlanModifiers{
tfsdk.RequiresReplace(),
},
},
"a": {
Computed: true,
Optional: true,
Attributes: tfsdk.SingleNestedAttributes(
map[string]tfsdk.Attribute{
"path": {
Type: types.StringType,
Required: true,
PlanModifiers: tfsdk.AttributePlanModifiers{
tfsdk.RequiresReplace(),
},
},
"optional": {
Type: types.BoolType,
Optional: true,
Computed: true,
PlanModifiers: tfsdk.AttributePlanModifiers{
tfsdk.RequiresReplace(),
},
},
},
),
},
"b": {
Computed: true,
Optional: true,
Attributes: tfsdk.SingleNestedAttributes(
map[string]tfsdk.Attribute{
"path": {
Type: types.StringType,
Required: true,
PlanModifiers: tfsdk.AttributePlanModifiers{
tfsdk.RequiresReplace(),
},
},
"optional": {
Type: types.BoolType,
Optional: true,
Computed: true,
PlanModifiers: tfsdk.AttributePlanModifiers{
tfsdk.RequiresReplace(),
},
},
},
),
},
},
tfsdk.SetNestedAttributesOptions{},
),
},
},
}, nil
}
func (t podResourceBugType) NewResource(ctx context.Context, in tfsdk.Provider) (tfsdk.Resource, diag.Diagnostics) {
return podResourceBug{}, nil
}
func (r podResourceBug) Create(ctx context.Context, req tfsdk.CreateResourceRequest, resp *tfsdk.CreateResourceResponse) {
var data podResourceBugData
resp.Diagnostics.Append(
req.Config.Get(ctx, &data)...,
)
if resp.Diagnostics.HasError() {
return
}
// emulate "computed" value
data.Mounts[0].A.Optional.Null = false
// Set state
resp.Diagnostics.Append(
resp.State.Set(ctx, &data)...,
)
}
func (r podResourceBug) Read(ctx context.Context, req tfsdk.ReadResourceRequest, resp *tfsdk.ReadResourceResponse) {
var data podResourceBugData
resp.Diagnostics.Append(
req.State.Get(ctx, &data)...,
)
// Set state
resp.Diagnostics.Append(
resp.State.Set(ctx, &data)...,
)
}
```
### Terraform Configuration Files
```hcl
resource "resource_bug" "test" {
mounts = [
{
name = "test"
// or omit b
b = null
a = {
path = "x"
}
},
]
}
```
### Expected Behavior
No change on update, but it triggers replacement all the time
### Actual Behavior
Replacements, even the resource did not change at all.
I actual face two different problem based on the settings:
When null is explicitly set for `b` :
diff:
```
~ mounts = [
+ { # forces replacement
+ a = {
+ optional = (known after apply)
+ path = "x"
}
+ b = {
+ optional = (known after apply)
+ path = (known after apply)
}
+ name = "test"
},
- { # forces replacement
- a = {
- optional = false -> null
- path = "x" -> null
}
- name = "test" -> null
},
]
```
### References
I think it might be related to #293, but not sure.
Contributor guide
Assessment
This issue has not been assessed yet.