Skip to content

Commit 5d68910

Browse files
committed
Add validation handling for dynamic underlying null values
1 parent aeeedf9 commit 5d68910

File tree

2 files changed

+179
-38
lines changed

2 files changed

+179
-38
lines changed

internal/fwserver/attribute_validation.go

Lines changed: 88 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -101,47 +101,97 @@ func AttributeValidate(ctx context.Context, a fwschema.Attribute, req ValidateAt
101101
return
102102
}
103103

104-
// Terraform CLI does not automatically perform certain configuration
105-
// checks yet. If it eventually does, this logic should remain at least
106-
// until Terraform CLI versions 0.12 through the release containing the
107-
// checks are considered end-of-life.
108-
// Reference: https://github.com/hashicorp/terraform/issues/30669
109-
if a.IsComputed() && !a.IsOptional() && !attributeConfig.IsNull() {
110-
resp.Diagnostics.AddAttributeError(
111-
req.AttributePath,
112-
"Invalid Configuration for Read-Only Attribute",
113-
"Cannot set value for this attribute as the provider has marked it as read-only. Remove the configuration line setting the value.\n\n"+
114-
"Refer to the provider documentation or contact the provider developers for additional information about configurable and read-only attributes that are supported.",
115-
)
116-
}
104+
// Dynamic values need to perform more logic to check the config value for null/unknown-ness
105+
dynamicValuable, ok := attributeConfig.(basetypes.DynamicValuable)
106+
if ok {
107+
dynamicConfigVal, diags := dynamicValuable.ToDynamicValue(ctx)
108+
resp.Diagnostics.Append(diags...)
109+
if diags.HasError() {
110+
return
111+
}
117112

118-
// Terraform CLI does not automatically perform certain configuration
119-
// checks yet. If it eventually does, this logic should remain at least
120-
// until Terraform CLI versions 0.12 through the release containing the
121-
// checks are considered end-of-life.
122-
// Reference: https://github.com/hashicorp/terraform/issues/30669
123-
if a.IsRequired() && attributeConfig.IsNull() {
124-
resp.Diagnostics.AddAttributeError(
125-
req.AttributePath,
126-
"Missing Configuration for Required Attribute",
127-
fmt.Sprintf("Must set a configuration value for the %s attribute as the provider has marked it as required.\n\n", req.AttributePath.String())+
128-
"Refer to the provider documentation or contact the provider developers for additional information about configurable attributes that are required.",
129-
)
130-
}
113+
// Terraform CLI does not automatically perform certain configuration
114+
// checks yet. If it eventually does, this logic should remain at least
115+
// until Terraform CLI versions 0.12 through the release containing the
116+
// checks are considered end-of-life.
117+
// Reference: https://github.com/hashicorp/terraform/issues/30669
118+
if a.IsComputed() && !a.IsOptional() && !dynamicConfigVal.IsUnderlyingValueNull() {
119+
resp.Diagnostics.AddAttributeError(
120+
req.AttributePath,
121+
"Invalid Configuration for Read-Only Attribute",
122+
"Cannot set value for this attribute as the provider has marked it as read-only. Remove the configuration line setting the value.\n\n"+
123+
"Refer to the provider documentation or contact the provider developers for additional information about configurable and read-only attributes that are supported.",
124+
)
125+
}
131126

132-
// If the client doesn't support write-only attributes (first supported in Terraform v1.11.0), then we raise an early validation error
133-
// to avoid a confusing data consistency error when the provider attempts to return "null" for a write-only attribute in the planned/final state.
134-
//
135-
// Write-only attributes can only be successfully used with a supporting client, so the only option for a practitoner to utilize a write-only attribute
136-
// is to upgrade their Terraform CLI version to v1.11.0 or later.
137-
if !req.ClientCapabilities.WriteOnlyAttributesAllowed && a.IsWriteOnly() && !attributeConfig.IsNull() {
138-
resp.Diagnostics.AddAttributeError(
139-
req.AttributePath,
140-
"WriteOnly Attribute Not Allowed",
141-
fmt.Sprintf("The resource contains a non-null value for WriteOnly attribute %s. Write-only attributes are only supported in Terraform 1.11 and later.", req.AttributePath.String()),
142-
)
143-
}
127+
// Terraform CLI does not automatically perform certain configuration
128+
// checks yet. If it eventually does, this logic should remain at least
129+
// until Terraform CLI versions 0.12 through the release containing the
130+
// checks are considered end-of-life.
131+
// Reference: https://github.com/hashicorp/terraform/issues/30669
132+
if a.IsRequired() && dynamicConfigVal.IsUnderlyingValueNull() {
133+
resp.Diagnostics.AddAttributeError(
134+
req.AttributePath,
135+
"Missing Configuration for Required Attribute",
136+
fmt.Sprintf("Must set a configuration value for the %s attribute as the provider has marked it as required.\n\n", req.AttributePath.String())+
137+
"Refer to the provider documentation or contact the provider developers for additional information about configurable attributes that are required.",
138+
)
139+
}
140+
141+
// If the client doesn't support write-only attributes (first supported in Terraform v1.11.0), then we raise an early validation error
142+
// to avoid a confusing data consistency error when the provider attempts to return "null" for a write-only attribute in the planned/final state.
143+
//
144+
// Write-only attributes can only be successfully used with a supporting client, so the only option for a practitoner to utilize a write-only attribute
145+
// is to upgrade their Terraform CLI version to v1.11.0 or later.
146+
if !req.ClientCapabilities.WriteOnlyAttributesAllowed && a.IsWriteOnly() && !dynamicConfigVal.IsUnderlyingValueNull() {
147+
resp.Diagnostics.AddAttributeError(
148+
req.AttributePath,
149+
"WriteOnly Attribute Not Allowed",
150+
fmt.Sprintf("The resource contains a non-null value for WriteOnly attribute %s. Write-only attributes are only supported in Terraform 1.11 and later.", req.AttributePath.String()),
151+
)
152+
}
153+
} else {
154+
// Terraform CLI does not automatically perform certain configuration
155+
// checks yet. If it eventually does, this logic should remain at least
156+
// until Terraform CLI versions 0.12 through the release containing the
157+
// checks are considered end-of-life.
158+
// Reference: https://github.com/hashicorp/terraform/issues/30669
159+
if a.IsComputed() && !a.IsOptional() && !attributeConfig.IsNull() {
160+
resp.Diagnostics.AddAttributeError(
161+
req.AttributePath,
162+
"Invalid Configuration for Read-Only Attribute",
163+
"Cannot set value for this attribute as the provider has marked it as read-only. Remove the configuration line setting the value.\n\n"+
164+
"Refer to the provider documentation or contact the provider developers for additional information about configurable and read-only attributes that are supported.",
165+
)
166+
}
167+
168+
// Terraform CLI does not automatically perform certain configuration
169+
// checks yet. If it eventually does, this logic should remain at least
170+
// until Terraform CLI versions 0.12 through the release containing the
171+
// checks are considered end-of-life.
172+
// Reference: https://github.com/hashicorp/terraform/issues/30669
173+
if a.IsRequired() && attributeConfig.IsNull() {
174+
resp.Diagnostics.AddAttributeError(
175+
req.AttributePath,
176+
"Missing Configuration for Required Attribute",
177+
fmt.Sprintf("Must set a configuration value for the %s attribute as the provider has marked it as required.\n\n", req.AttributePath.String())+
178+
"Refer to the provider documentation or contact the provider developers for additional information about configurable attributes that are required.",
179+
)
180+
}
144181

182+
// If the client doesn't support write-only attributes (first supported in Terraform v1.11.0), then we raise an early validation error
183+
// to avoid a confusing data consistency error when the provider attempts to return "null" for a write-only attribute in the planned/final state.
184+
//
185+
// Write-only attributes can only be successfully used with a supporting client, so the only option for a practitoner to utilize a write-only attribute
186+
// is to upgrade their Terraform CLI version to v1.11.0 or later.
187+
if !req.ClientCapabilities.WriteOnlyAttributesAllowed && a.IsWriteOnly() && !attributeConfig.IsNull() {
188+
resp.Diagnostics.AddAttributeError(
189+
req.AttributePath,
190+
"WriteOnly Attribute Not Allowed",
191+
fmt.Sprintf("The resource contains a non-null value for WriteOnly attribute %s. Write-only attributes are only supported in Terraform 1.11 and later.", req.AttributePath.String()),
192+
)
193+
}
194+
}
145195
req.AttributeConfig = attributeConfig
146196

147197
switch attributeWithValidators := a.(type) {

internal/fwserver/attribute_validation_test.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,29 @@ func TestAttributeValidate(t *testing.T) {
181181
},
182182
},
183183
},
184+
"config-computed-dynamic-underlying-null-value": {
185+
req: ValidateAttributeRequest{
186+
AttributePath: path.Root("test"),
187+
Config: tfsdk.Config{
188+
Raw: tftypes.NewValue(tftypes.Object{
189+
AttributeTypes: map[string]tftypes.Type{
190+
"test": tftypes.DynamicPseudoType,
191+
},
192+
}, map[string]tftypes.Value{
193+
"test": tftypes.NewValue(tftypes.String, nil),
194+
}),
195+
Schema: testschema.Schema{
196+
Attributes: map[string]fwschema.Attribute{
197+
"test": testschema.Attribute{
198+
Computed: true,
199+
Type: types.DynamicType,
200+
},
201+
},
202+
},
203+
},
204+
},
205+
resp: ValidateAttributeResponse{},
206+
},
184207
"config-optional-computed-null": {
185208
req: ValidateAttributeRequest{
186209
AttributePath: path.Root("test"),
@@ -331,6 +354,38 @@ func TestAttributeValidate(t *testing.T) {
331354
},
332355
resp: ValidateAttributeResponse{},
333356
},
357+
"config-required-dynamic-underlying-null-value": {
358+
req: ValidateAttributeRequest{
359+
AttributePath: path.Root("test"),
360+
Config: tfsdk.Config{
361+
Raw: tftypes.NewValue(tftypes.Object{
362+
AttributeTypes: map[string]tftypes.Type{
363+
"test": tftypes.DynamicPseudoType,
364+
},
365+
}, map[string]tftypes.Value{
366+
"test": tftypes.NewValue(tftypes.String, nil),
367+
}),
368+
Schema: testschema.Schema{
369+
Attributes: map[string]fwschema.Attribute{
370+
"test": testschema.Attribute{
371+
Required: true,
372+
Type: types.DynamicType,
373+
},
374+
},
375+
},
376+
},
377+
},
378+
resp: ValidateAttributeResponse{
379+
Diagnostics: diag.Diagnostics{
380+
diag.NewAttributeErrorDiagnostic(
381+
path.Root("test"),
382+
"Missing Configuration for Required Attribute",
383+
"Must set a configuration value for the test attribute as the provider has marked it as required.\n\n"+
384+
"Refer to the provider documentation or contact the provider developers for additional information about configurable attributes that are required.",
385+
),
386+
},
387+
},
388+
},
334389
"no-validation": {
335390
req: ValidateAttributeRequest{
336391
AttributePath: path.Root("test"),
@@ -1763,6 +1818,42 @@ func TestAttributeValidate(t *testing.T) {
17631818
},
17641819
},
17651820
},
1821+
"write-only-attr-with-required-dynamic-underlying-null-value": {
1822+
req: ValidateAttributeRequest{
1823+
ClientCapabilities: validator.ValidateSchemaClientCapabilities{
1824+
WriteOnlyAttributesAllowed: true,
1825+
},
1826+
AttributePath: path.Root("test"),
1827+
Config: tfsdk.Config{
1828+
Raw: tftypes.NewValue(tftypes.Object{
1829+
AttributeTypes: map[string]tftypes.Type{
1830+
"test": tftypes.DynamicPseudoType,
1831+
},
1832+
}, map[string]tftypes.Value{
1833+
"test": tftypes.NewValue(tftypes.String, nil),
1834+
}),
1835+
Schema: testschema.Schema{
1836+
Attributes: map[string]fwschema.Attribute{
1837+
"test": testschema.Attribute{
1838+
Type: types.DynamicType,
1839+
WriteOnly: true,
1840+
Required: true,
1841+
},
1842+
},
1843+
},
1844+
},
1845+
},
1846+
resp: ValidateAttributeResponse{
1847+
Diagnostics: diag.Diagnostics{
1848+
diag.NewAttributeErrorDiagnostic(
1849+
path.Root("test"),
1850+
"Missing Configuration for Required Attribute",
1851+
"Must set a configuration value for the test attribute as the provider has marked it as required.\n\n"+
1852+
"Refer to the provider documentation or contact the provider developers for additional information about configurable attributes that are required.",
1853+
),
1854+
},
1855+
},
1856+
},
17661857
"write-only-attr-with-optional": {
17671858
req: ValidateAttributeRequest{
17681859
ClientCapabilities: validator.ValidateSchemaClientCapabilities{

0 commit comments

Comments
 (0)