Skip to content

Commit 4b722e0

Browse files
committed
add validation for older terraform client versions
1 parent f4f97bd commit 4b722e0

File tree

2 files changed

+71
-0
lines changed

2 files changed

+71
-0
lines changed

internal/fwserver/attribute_validation.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,19 @@ func AttributeValidate(ctx context.Context, a fwschema.Attribute, req ValidateAt
130130
)
131131
}
132132

133+
// If the client doesn't support write-only attributes (first supported in Terraform v1.11.0), then we raise an early validation error
134+
// to avoid a confusing data consistency error when the provider attempts to return "null" for a write-only attribute in the planned/final state.
135+
//
136+
// 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
137+
// is to upgrade their Terraform CLI version to v1.11.0 or later.
138+
if !req.ClientCapabilities.WriteOnlyAttributesAllowed && a.IsWriteOnly() && !attributeConfig.IsNull() {
139+
resp.Diagnostics.AddAttributeError(
140+
req.AttributePath,
141+
"WriteOnly Attribute Not Allowed",
142+
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()),
143+
)
144+
}
145+
133146
req.AttributeConfig = attributeConfig
134147

135148
switch attributeWithValidators := a.(type) {

internal/fwserver/attribute_validation_test.go

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1702,6 +1702,9 @@ func TestAttributeValidate(t *testing.T) {
17021702
},
17031703
"write-only-attr-with-required": {
17041704
req: ValidateAttributeRequest{
1705+
ClientCapabilities: validator.ValidateSchemaClientCapabilities{
1706+
WriteOnlyAttributesAllowed: true,
1707+
},
17051708
AttributePath: path.Root("test"),
17061709
Config: tfsdk.Config{
17071710
Raw: tftypes.NewValue(tftypes.Object{
@@ -1726,6 +1729,9 @@ func TestAttributeValidate(t *testing.T) {
17261729
},
17271730
"write-only-attr-with-required-null-value": {
17281731
req: ValidateAttributeRequest{
1732+
ClientCapabilities: validator.ValidateSchemaClientCapabilities{
1733+
WriteOnlyAttributesAllowed: true,
1734+
},
17291735
AttributePath: path.Root("test"),
17301736
Config: tfsdk.Config{
17311737
Raw: tftypes.NewValue(tftypes.Object{
@@ -1750,6 +1756,9 @@ func TestAttributeValidate(t *testing.T) {
17501756
},
17511757
"write-only-attr-with-optional": {
17521758
req: ValidateAttributeRequest{
1759+
ClientCapabilities: validator.ValidateSchemaClientCapabilities{
1760+
WriteOnlyAttributesAllowed: true,
1761+
},
17531762
AttributePath: path.Root("test"),
17541763
Config: tfsdk.Config{
17551764
Raw: tftypes.NewValue(tftypes.Object{
@@ -1774,6 +1783,9 @@ func TestAttributeValidate(t *testing.T) {
17741783
},
17751784
"write-only-attr-with-computed": {
17761785
req: ValidateAttributeRequest{
1786+
ClientCapabilities: validator.ValidateSchemaClientCapabilities{
1787+
WriteOnlyAttributesAllowed: true,
1788+
},
17771789
AttributePath: path.Root("test"),
17781790
Config: tfsdk.Config{
17791791
Raw: tftypes.NewValue(tftypes.Object{
@@ -1806,6 +1818,9 @@ func TestAttributeValidate(t *testing.T) {
18061818
},
18071819
"write-only-attr-missing-required-and-optional": {
18081820
req: ValidateAttributeRequest{
1821+
ClientCapabilities: validator.ValidateSchemaClientCapabilities{
1822+
WriteOnlyAttributesAllowed: true,
1823+
},
18091824
AttributePath: path.Root("test"),
18101825
Config: tfsdk.Config{
18111826
Raw: tftypes.NewValue(tftypes.Object{
@@ -1837,6 +1852,9 @@ func TestAttributeValidate(t *testing.T) {
18371852
},
18381853
"write-only-attr-with-required-and-optional": {
18391854
req: ValidateAttributeRequest{
1855+
ClientCapabilities: validator.ValidateSchemaClientCapabilities{
1856+
WriteOnlyAttributesAllowed: true,
1857+
},
18401858
AttributePath: path.Root("test"),
18411859
Config: tfsdk.Config{
18421860
Raw: tftypes.NewValue(tftypes.Object{
@@ -1870,6 +1888,9 @@ func TestAttributeValidate(t *testing.T) {
18701888
},
18711889
"write-only-attr-with-computed-required-and-optional": {
18721890
req: ValidateAttributeRequest{
1891+
ClientCapabilities: validator.ValidateSchemaClientCapabilities{
1892+
WriteOnlyAttributesAllowed: true,
1893+
},
18731894
AttributePath: path.Root("test"),
18741895
Config: tfsdk.Config{
18751896
Raw: tftypes.NewValue(tftypes.Object{
@@ -1907,6 +1928,43 @@ func TestAttributeValidate(t *testing.T) {
19071928
},
19081929
},
19091930
},
1931+
"write-only-attr-set-no-client-capability": {
1932+
req: ValidateAttributeRequest{
1933+
ClientCapabilities: validator.ValidateSchemaClientCapabilities{
1934+
// Client indicating it doesn't support write-only attributes
1935+
WriteOnlyAttributesAllowed: false,
1936+
},
1937+
AttributePath: path.Root("test"),
1938+
Config: tfsdk.Config{
1939+
Raw: tftypes.NewValue(tftypes.Object{
1940+
AttributeTypes: map[string]tftypes.Type{
1941+
"test": tftypes.String,
1942+
},
1943+
}, map[string]tftypes.Value{
1944+
"test": tftypes.NewValue(tftypes.String, "hello world!"),
1945+
}),
1946+
Schema: testschema.Schema{
1947+
Attributes: map[string]fwschema.Attribute{
1948+
"test": testschema.Attribute{
1949+
Required: true,
1950+
WriteOnly: true,
1951+
Type: types.StringType,
1952+
},
1953+
},
1954+
},
1955+
},
1956+
},
1957+
resp: ValidateAttributeResponse{
1958+
Diagnostics: diag.Diagnostics{
1959+
diag.NewAttributeErrorDiagnostic(
1960+
path.Root("test"),
1961+
"WriteOnly Attribute Not Allowed",
1962+
"The resource contains a non-null value for WriteOnly attribute test. "+
1963+
"Write-only attributes are only supported in Terraform 1.11 and later.",
1964+
),
1965+
},
1966+
},
1967+
},
19101968
}
19111969

19121970
for name, tc := range testCases {

0 commit comments

Comments
 (0)