Skip to content

Commit

Permalink
internal/fwserver: Add Attribute validation for missing required and …
Browse files Browse the repository at this point in the history
…invalid read-only configurations (#370)

Reference: #276
  • Loading branch information
bflad authored Jun 13, 2022
1 parent dd9ee86 commit c76b9a4
Show file tree
Hide file tree
Showing 3 changed files with 268 additions and 0 deletions.
3 changes: 3 additions & 0 deletions .changelog/370.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
```release-notes:enhancement
internal/fwserver: Added `Attribute` validation for missing required configurations and invalid read-only configurations
```
28 changes: 28 additions & 0 deletions internal/fwserver/attribute_validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,34 @@ func AttributeValidate(ctx context.Context, a tfsdk.Attribute, req tfsdk.Validat
return
}

// Terraform CLI does not automatically perform certain configuration
// checks yet. If it eventually does, this logic should remain at least
// until Terraform CLI versions 0.12 through the release containing the
// checks are considered end-of-life.
// Reference: https://github.com/hashicorp/terraform/issues/30669
if a.Computed && !a.Optional && !attributeConfig.IsNull() {
resp.Diagnostics.AddAttributeError(
req.AttributePath,
"Invalid Configuration for Read-Only Attribute",
"Cannot set value for this attribute as the provider has marked it as read-only. Remove the configuration line setting the value.\n\n"+
"Refer to the provider documentation or contact the provider developers for additional information about configurable and read-only attributes that are supported.",
)
}

// Terraform CLI does not automatically perform certain configuration
// checks yet. If it eventually does, this logic should remain at least
// until Terraform CLI versions 0.12 through the release containing the
// checks are considered end-of-life.
// Reference: https://github.com/hashicorp/terraform/issues/30669
if a.Required && attributeConfig.IsNull() {
resp.Diagnostics.AddAttributeError(
req.AttributePath,
"Missing Configuration for Required Attribute",
fmt.Sprintf("Must set a configuration value for the %s attribute as the provider has marked it as required.\n\n", req.AttributePath.String())+
"Refer to the provider documentation or contact the provider developers for additional information about configurable attributes that are required.",
)
}

req.AttributeConfig = attributeConfig

for _, validator := range a.Validators {
Expand Down
237 changes: 237 additions & 0 deletions internal/fwserver/attribute_validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,243 @@ func TestAttributeValidate(t *testing.T) {
},
},
},
"config-computed-null": {
req: tfsdk.ValidateAttributeRequest{
AttributePath: tftypes.NewAttributePath().WithAttributeName("test"),
Config: tfsdk.Config{
Raw: tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test": tftypes.String,
},
}, map[string]tftypes.Value{
"test": tftypes.NewValue(tftypes.String, nil),
}),
Schema: tfsdk.Schema{
Attributes: map[string]tfsdk.Attribute{
"test": {
Computed: true,
Type: types.StringType,
},
},
},
},
},
resp: tfsdk.ValidateAttributeResponse{},
},
"config-computed-unknown": {
req: tfsdk.ValidateAttributeRequest{
AttributePath: tftypes.NewAttributePath().WithAttributeName("test"),
Config: tfsdk.Config{
Raw: tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test": tftypes.String,
},
}, map[string]tftypes.Value{
"test": tftypes.NewValue(tftypes.String, tftypes.UnknownValue),
}),
Schema: tfsdk.Schema{
Attributes: map[string]tfsdk.Attribute{
"test": {
Computed: true,
Type: types.StringType,
},
},
},
},
},
resp: tfsdk.ValidateAttributeResponse{
Diagnostics: diag.Diagnostics{
diag.NewAttributeErrorDiagnostic(
tftypes.NewAttributePath().WithAttributeName("test"),
"Invalid Configuration for Read-Only Attribute",
"Cannot set value for this attribute as the provider has marked it as read-only. Remove the configuration line setting the value.\n\n"+
"Refer to the provider documentation or contact the provider developers for additional information about configurable and read-only attributes that are supported.",
),
},
},
},
"config-computed-value": {
req: tfsdk.ValidateAttributeRequest{
AttributePath: tftypes.NewAttributePath().WithAttributeName("test"),
Config: tfsdk.Config{
Raw: tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test": tftypes.String,
},
}, map[string]tftypes.Value{
"test": tftypes.NewValue(tftypes.String, "testvalue"),
}),
Schema: tfsdk.Schema{
Attributes: map[string]tfsdk.Attribute{
"test": {
Computed: true,
Type: types.StringType,
},
},
},
},
},
resp: tfsdk.ValidateAttributeResponse{
Diagnostics: diag.Diagnostics{
diag.NewAttributeErrorDiagnostic(
tftypes.NewAttributePath().WithAttributeName("test"),
"Invalid Configuration for Read-Only Attribute",
"Cannot set value for this attribute as the provider has marked it as read-only. Remove the configuration line setting the value.\n\n"+
"Refer to the provider documentation or contact the provider developers for additional information about configurable and read-only attributes that are supported.",
),
},
},
},
"config-optional-computed-null": {
req: tfsdk.ValidateAttributeRequest{
AttributePath: tftypes.NewAttributePath().WithAttributeName("test"),
Config: tfsdk.Config{
Raw: tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test": tftypes.String,
},
}, map[string]tftypes.Value{
"test": tftypes.NewValue(tftypes.String, nil),
}),
Schema: tfsdk.Schema{
Attributes: map[string]tfsdk.Attribute{
"test": {
Computed: true,
Optional: true,
Type: types.StringType,
},
},
},
},
},
resp: tfsdk.ValidateAttributeResponse{},
},
"config-optional-computed-unknown": {
req: tfsdk.ValidateAttributeRequest{
AttributePath: tftypes.NewAttributePath().WithAttributeName("test"),
Config: tfsdk.Config{
Raw: tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test": tftypes.String,
},
}, map[string]tftypes.Value{
"test": tftypes.NewValue(tftypes.String, tftypes.UnknownValue),
}),
Schema: tfsdk.Schema{
Attributes: map[string]tfsdk.Attribute{
"test": {
Computed: true,
Optional: true,
Type: types.StringType,
},
},
},
},
},
resp: tfsdk.ValidateAttributeResponse{},
},
"config-optional-computed-value": {
req: tfsdk.ValidateAttributeRequest{
AttributePath: tftypes.NewAttributePath().WithAttributeName("test"),
Config: tfsdk.Config{
Raw: tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test": tftypes.String,
},
}, map[string]tftypes.Value{
"test": tftypes.NewValue(tftypes.String, "testvalue"),
}),
Schema: tfsdk.Schema{
Attributes: map[string]tfsdk.Attribute{
"test": {
Computed: true,
Optional: true,
Type: types.StringType,
},
},
},
},
},
resp: tfsdk.ValidateAttributeResponse{},
},
"config-required-null": {
req: tfsdk.ValidateAttributeRequest{
AttributePath: tftypes.NewAttributePath().WithAttributeName("test"),
Config: tfsdk.Config{
Raw: tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test": tftypes.String,
},
}, map[string]tftypes.Value{
"test": tftypes.NewValue(tftypes.String, nil),
}),
Schema: tfsdk.Schema{
Attributes: map[string]tfsdk.Attribute{
"test": {
Required: true,
Type: types.StringType,
},
},
},
},
},
resp: tfsdk.ValidateAttributeResponse{
Diagnostics: diag.Diagnostics{
diag.NewAttributeErrorDiagnostic(
tftypes.NewAttributePath().WithAttributeName("test"),
"Missing Configuration for Required Attribute",
"Must set a configuration value for the AttributeName(\"test\") attribute as the provider has marked it as required.\n\n"+
"Refer to the provider documentation or contact the provider developers for additional information about configurable attributes that are required.",
),
},
},
},
"config-required-unknown": {
req: tfsdk.ValidateAttributeRequest{
AttributePath: tftypes.NewAttributePath().WithAttributeName("test"),
Config: tfsdk.Config{
Raw: tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test": tftypes.String,
},
}, map[string]tftypes.Value{
"test": tftypes.NewValue(tftypes.String, tftypes.UnknownValue),
}),
Schema: tfsdk.Schema{
Attributes: map[string]tfsdk.Attribute{
"test": {
Required: true,
Type: types.StringType,
},
},
},
},
},
resp: tfsdk.ValidateAttributeResponse{},
},
"config-required-value": {
req: tfsdk.ValidateAttributeRequest{
AttributePath: tftypes.NewAttributePath().WithAttributeName("test"),
Config: tfsdk.Config{
Raw: tftypes.NewValue(tftypes.Object{
AttributeTypes: map[string]tftypes.Type{
"test": tftypes.String,
},
}, map[string]tftypes.Value{
"test": tftypes.NewValue(tftypes.String, "testvalue"),
}),
Schema: tfsdk.Schema{
Attributes: map[string]tfsdk.Attribute{
"test": {
Required: true,
Type: types.StringType,
},
},
},
},
},
resp: tfsdk.ValidateAttributeResponse{},
},
"no-validation": {
req: tfsdk.ValidateAttributeRequest{
AttributePath: tftypes.NewAttributePath().WithAttributeName("test"),
Expand Down

0 comments on commit c76b9a4

Please sign in to comment.