Skip to content

Commit 3abf12e

Browse files
committed
dynamics
1 parent a6a27b8 commit 3abf12e

File tree

2 files changed

+122
-17
lines changed

2 files changed

+122
-17
lines changed

resource/schema/dynamicplanmodifier/use_state_for_unknown.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,8 @@ func (m useStateForUnknownModifier) MarkdownDescription(_ context.Context) strin
3636

3737
// PlanModifyDynamic implements the plan modification logic.
3838
func (m useStateForUnknownModifier) PlanModifyDynamic(ctx context.Context, req planmodifier.DynamicRequest, resp *planmodifier.DynamicResponse) {
39-
// Do nothing if there is no state value.
40-
// This also requires checking if the underlying value is null.
41-
if req.StateValue.IsNull() || req.StateValue.IsUnderlyingValueNull() {
39+
// Do nothing if there is no state (resource is being created).
40+
if req.State.Raw.IsNull() {
4241
return
4342
}
4443

resource/schema/dynamicplanmodifier/use_state_for_unknown_test.go

Lines changed: 120 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@ import (
1010
"github.com/google/go-cmp/cmp"
1111
"github.com/hashicorp/terraform-plugin-framework/resource/schema/dynamicplanmodifier"
1212
"github.com/hashicorp/terraform-plugin-framework/resource/schema/planmodifier"
13+
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
1314
"github.com/hashicorp/terraform-plugin-framework/types"
15+
"github.com/hashicorp/terraform-plugin-go/tftypes"
1416
)
1517

1618
func TestUseStateForUnknownModifierPlanModifyDynamic(t *testing.T) {
@@ -23,6 +25,16 @@ func TestUseStateForUnknownModifierPlanModifyDynamic(t *testing.T) {
2325
"null-state": {
2426
// when we first create the resource, use the unknown value
2527
request: planmodifier.DynamicRequest{
28+
State: tfsdk.State{
29+
Raw: tftypes.NewValue(
30+
tftypes.Object{
31+
AttributeTypes: map[string]tftypes.Type{
32+
"attr": tftypes.DynamicPseudoType,
33+
},
34+
},
35+
nil,
36+
),
37+
},
2638
StateValue: types.DynamicNull(),
2739
PlanValue: types.DynamicUnknown(),
2840
ConfigValue: types.DynamicNull(),
@@ -31,23 +43,23 @@ func TestUseStateForUnknownModifierPlanModifyDynamic(t *testing.T) {
3143
PlanValue: types.DynamicUnknown(),
3244
},
3345
},
34-
"null-underlying-state-value": {
35-
// if the state value has a known underlying type, but a null underlying value,
36-
// use the unknown value
37-
request: planmodifier.DynamicRequest{
38-
StateValue: types.DynamicValue(types.StringNull()),
39-
PlanValue: types.DynamicUnknown(),
40-
ConfigValue: types.DynamicNull(),
41-
},
42-
expected: &planmodifier.DynamicResponse{
43-
PlanValue: types.DynamicUnknown(),
44-
},
45-
},
4646
"known-plan": {
4747
// this would really only happen if we had a plan
4848
// modifier setting the value before this plan modifier
4949
// got to it. We still want to preserve that value, in this case
5050
request: planmodifier.DynamicRequest{
51+
State: tfsdk.State{
52+
Raw: tftypes.NewValue(
53+
tftypes.Object{
54+
AttributeTypes: map[string]tftypes.Type{
55+
"attr": tftypes.DynamicPseudoType,
56+
},
57+
},
58+
map[string]tftypes.Value{
59+
"attr": tftypes.NewValue(tftypes.String, "other"),
60+
},
61+
),
62+
},
5163
StateValue: types.DynamicValue(types.StringValue("other")),
5264
PlanValue: types.DynamicValue(types.StringValue("test")),
5365
ConfigValue: types.DynamicNull(),
@@ -61,6 +73,18 @@ func TestUseStateForUnknownModifierPlanModifyDynamic(t *testing.T) {
6173
// modifier setting the value before this plan modifier
6274
// got to it. We still want to preserve that value, in this case
6375
request: planmodifier.DynamicRequest{
76+
State: tfsdk.State{
77+
Raw: tftypes.NewValue(
78+
tftypes.Object{
79+
AttributeTypes: map[string]tftypes.Type{
80+
"attr": tftypes.DynamicPseudoType,
81+
},
82+
},
83+
map[string]tftypes.Value{
84+
"attr": tftypes.NewValue(tftypes.String, "other"),
85+
},
86+
),
87+
},
6488
StateValue: types.DynamicValue(types.StringValue("other")),
6589
PlanValue: types.DynamicNull(),
6690
ConfigValue: types.DynamicNull(),
@@ -74,6 +98,18 @@ func TestUseStateForUnknownModifierPlanModifyDynamic(t *testing.T) {
7498
// modifier setting the value before this plan modifier
7599
// got to it. We still want to preserve that value, in this case
76100
request: planmodifier.DynamicRequest{
101+
State: tfsdk.State{
102+
Raw: tftypes.NewValue(
103+
tftypes.Object{
104+
AttributeTypes: map[string]tftypes.Type{
105+
"attr": tftypes.DynamicPseudoType,
106+
},
107+
},
108+
map[string]tftypes.Value{
109+
"attr": tftypes.NewValue(tftypes.String, "other"),
110+
},
111+
),
112+
},
77113
StateValue: types.DynamicValue(types.StringValue("other")),
78114
PlanValue: types.DynamicValue(types.StringNull()),
79115
ConfigValue: types.DynamicNull(),
@@ -82,9 +118,21 @@ func TestUseStateForUnknownModifierPlanModifyDynamic(t *testing.T) {
82118
PlanValue: types.DynamicValue(types.StringNull()),
83119
},
84120
},
85-
"non-null-state-unknown-plan": {
121+
"non-null-state-value-unknown-plan": {
86122
// this is the situation we want to preserve the state in
87123
request: planmodifier.DynamicRequest{
124+
State: tfsdk.State{
125+
Raw: tftypes.NewValue(
126+
tftypes.Object{
127+
AttributeTypes: map[string]tftypes.Type{
128+
"attr": tftypes.DynamicPseudoType,
129+
},
130+
},
131+
map[string]tftypes.Value{
132+
"attr": tftypes.NewValue(tftypes.String, "test"),
133+
},
134+
),
135+
},
88136
StateValue: types.DynamicValue(types.StringValue("test")),
89137
PlanValue: types.DynamicUnknown(),
90138
ConfigValue: types.DynamicNull(),
@@ -93,10 +141,22 @@ func TestUseStateForUnknownModifierPlanModifyDynamic(t *testing.T) {
93141
PlanValue: types.DynamicValue(types.StringValue("test")),
94142
},
95143
},
96-
"non-null-state-unknown-underlying-plan-value": {
144+
"non-null-state-value-unknown-underlying-plan-value": {
97145
// if the plan value has a known underlying type, but an unknown underlying value
98146
// we want to preserve the state
99147
request: planmodifier.DynamicRequest{
148+
State: tfsdk.State{
149+
Raw: tftypes.NewValue(
150+
tftypes.Object{
151+
AttributeTypes: map[string]tftypes.Type{
152+
"attr": tftypes.DynamicPseudoType,
153+
},
154+
},
155+
map[string]tftypes.Value{
156+
"attr": tftypes.NewValue(tftypes.String, "test"),
157+
},
158+
),
159+
},
100160
StateValue: types.DynamicValue(types.StringValue("test")),
101161
PlanValue: types.DynamicValue(types.StringUnknown()),
102162
ConfigValue: types.DynamicNull(),
@@ -105,6 +165,52 @@ func TestUseStateForUnknownModifierPlanModifyDynamic(t *testing.T) {
105165
PlanValue: types.DynamicValue(types.StringValue("test")),
106166
},
107167
},
168+
"null-state-value-unknown-plan": {
169+
// Null state values are still known, so we should preserve this as well.
170+
request: planmodifier.DynamicRequest{
171+
State: tfsdk.State{
172+
Raw: tftypes.NewValue(
173+
tftypes.Object{
174+
AttributeTypes: map[string]tftypes.Type{
175+
"attr": tftypes.DynamicPseudoType,
176+
},
177+
},
178+
map[string]tftypes.Value{
179+
"attr": tftypes.NewValue(tftypes.DynamicPseudoType, nil),
180+
},
181+
),
182+
},
183+
StateValue: types.DynamicNull(),
184+
PlanValue: types.DynamicUnknown(),
185+
ConfigValue: types.DynamicNull(),
186+
},
187+
expected: &planmodifier.DynamicResponse{
188+
PlanValue: types.DynamicNull(),
189+
},
190+
},
191+
"null-underlying-state-value-unknown-plan": {
192+
// if the state value has a known underlying type, but a null underlying value, we should preserve this as well.
193+
request: planmodifier.DynamicRequest{
194+
State: tfsdk.State{
195+
Raw: tftypes.NewValue(
196+
tftypes.Object{
197+
AttributeTypes: map[string]tftypes.Type{
198+
"attr": tftypes.DynamicPseudoType,
199+
},
200+
},
201+
map[string]tftypes.Value{
202+
"attr": tftypes.NewValue(tftypes.String, nil),
203+
},
204+
),
205+
},
206+
StateValue: types.DynamicValue(types.StringNull()),
207+
PlanValue: types.DynamicUnknown(),
208+
ConfigValue: types.DynamicNull(),
209+
},
210+
expected: &planmodifier.DynamicResponse{
211+
PlanValue: types.DynamicValue(types.StringNull()),
212+
},
213+
},
108214
"unknown-config": {
109215
// this is the situation in which a user is
110216
// interpolating into a field. We want that to still

0 commit comments

Comments
 (0)