Skip to content

Commit e1e6866

Browse files
SBGoodsaustinvalle
andauthored
Add support for write only attributes (#1044)
* initial ephemeral resource interfaces * add ephemeral resource configure data * attribute implementations * uncomment custom type tests * added block implementations * add nested attribute implementations * add schema test * remove todo * doc updates, renames, removals * initial protov5 + fwserver implementation (protov6 stubbed) * add fromproto5 tests * add toproto5 tests * add proto5server tests * implement protov6 * schema + metadata tests * add close proto5/6 tests * add fwserver tests for schema/metadata * prevent random false positives * validate fwserver tests * open/renew/close fwserver tests * update error message * update plugin go * Update `terraform-plugin-go` dependency * remove `config` from renew * Implement write only attributes in the `resource/schema` package * Implement write only attributes in the `datasource/schema` package * Implement write only attributes in the `provider/schema` and `provider/metaschema` packages * Implement write only attributes in the `internal/testing/testschema` package * Update `terraform-plugin-go` dependency * Implement write only attributes in the `ephemeral/schema` package * Populate writeOnly fields in `internal/toproto5` and `internal/toproto6` * Implement `ValidateResourceConfigClientCapabilities` in the `ValidateResourceConfig` RPC * Add attribute validation for write only attributes * Initial `RequiredWriteOnlyNilsAttributePaths()` implementation * Complete `RequiredWriteOnlyNilsAttributePaths()` implementation * Implement `validator.ValidateSchemaClientCapabilities` * Implement automatic write-only value nullification during `ApplyResourceState` RPC * Explicitly set `ValidateSchemaClientCapabilities` during `ValidateDataSourceConfig`, `ValidateEphemeralResourceConfig`, and `ValidateProviderResourceConfig` RPCs * Nullify write-only attributes during Plan and Apply regardless of client capability * remove apply client capability * add validation for older terraform client versions * add client capabilities to nested attribute validation * Update wording of `IsWriteOnly` comment for `ephemeral/schema`, `provider/schema`, and `provider/metaschema` * Update various comments for wording * Update test cases * Update wording for `write-only` attribute validation errors * Refactor write_only_nested_attribute_validation.go and write_only_nested_attribute_validation_test.go * Move `Required` + `WriteOnly` validations from `PlanResourceChange` RPC to `ValidateResourceConfig` RPC * Add write-only value nullification to `ReadResource`, `ImportResourceState`, `UpgradeResourceState`, and `MoveResourceState` RPCs * Add missing `IsWriteOnly()` unit tests for `ephemeral/schema` package * Add godoc comment to `NullifyWriteOnlyAttributes()` * Add testing for nested types for `NullifyWriteOnlyAttributes()` * Add website documentation * Add recommendation to use private state to store hashes * Add changelog entries --------- Co-authored-by: Austin Valle <[email protected]>
1 parent 3c0bf49 commit e1e6866

File tree

277 files changed

+9882
-204
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

277 files changed

+9882
-204
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
kind: FEATURES
2+
body: 'resource/schema: Added `WriteOnly` schema field for managed resource schemas to indicate a write-only attribute.
3+
Write-only attribute values are not saved to the Terraform plan or state artifacts.'
4+
time: 2025-02-06T11:47:00.176842-05:00
5+
custom:
6+
Issue: "1044"
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
kind: NOTES
2+
body: Write-only attribute support is in technical preview and offered without compatibility promises until Terraform 1.11 is generally available.
3+
time: 2025-02-06T11:44:36.156747-05:00
4+
custom:
5+
Issue: "1044"

datasource/schema/bool_attribute.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
package schema
55

66
import (
7+
"github.com/hashicorp/terraform-plugin-go/tftypes"
8+
79
"github.com/hashicorp/terraform-plugin-framework/attr"
810
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
911
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema"
1012
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1113
"github.com/hashicorp/terraform-plugin-framework/types"
1214
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
13-
"github.com/hashicorp/terraform-plugin-go/tftypes"
1415
)
1516

1617
// Ensure the implementation satisifies the desired interfaces.
@@ -181,6 +182,11 @@ func (a BoolAttribute) IsRequired() bool {
181182
return a.Required
182183
}
183184

185+
// IsWriteOnly returns false as write-only attributes are not supported in data source schemas.
186+
func (a BoolAttribute) IsWriteOnly() bool {
187+
return false
188+
}
189+
184190
// IsSensitive returns the Sensitive field value.
185191
func (a BoolAttribute) IsSensitive() bool {
186192
return a.Sensitive

datasource/schema/bool_attribute_test.go

+30-1
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ import (
99
"testing"
1010

1111
"github.com/google/go-cmp/cmp"
12+
"github.com/hashicorp/terraform-plugin-go/tftypes"
13+
1214
"github.com/hashicorp/terraform-plugin-framework/attr"
1315
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
1416
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
1517
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema"
1618
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes"
1719
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1820
"github.com/hashicorp/terraform-plugin-framework/types"
19-
"github.com/hashicorp/terraform-plugin-go/tftypes"
2021
)
2122

2223
func TestBoolAttributeApplyTerraform5AttributePathStep(t *testing.T) {
@@ -423,3 +424,31 @@ func TestBoolAttributeIsSensitive(t *testing.T) {
423424
})
424425
}
425426
}
427+
428+
func TestBoolAttributeIsWriteOnly(t *testing.T) {
429+
t.Parallel()
430+
431+
testCases := map[string]struct {
432+
attribute schema.BoolAttribute
433+
expected bool
434+
}{
435+
"not-writeOnly": {
436+
attribute: schema.BoolAttribute{},
437+
expected: false,
438+
},
439+
}
440+
441+
for name, testCase := range testCases {
442+
name, testCase := name, testCase
443+
444+
t.Run(name, func(t *testing.T) {
445+
t.Parallel()
446+
447+
got := testCase.attribute.IsWriteOnly()
448+
449+
if diff := cmp.Diff(got, testCase.expected); diff != "" {
450+
t.Errorf("unexpected difference: %s", diff)
451+
}
452+
})
453+
}
454+
}

datasource/schema/dynamic_attribute.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
package schema
55

66
import (
7+
"github.com/hashicorp/terraform-plugin-go/tftypes"
8+
79
"github.com/hashicorp/terraform-plugin-framework/attr"
810
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
911
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema"
1012
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1113
"github.com/hashicorp/terraform-plugin-framework/types"
1214
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
13-
"github.com/hashicorp/terraform-plugin-go/tftypes"
1415
)
1516

1617
// Ensure the implementation satisifies the desired interfaces.
@@ -182,6 +183,11 @@ func (a DynamicAttribute) IsSensitive() bool {
182183
return a.Sensitive
183184
}
184185

186+
// IsWriteOnly returns false as write-only attributes are not supported in data source schemas.
187+
func (a DynamicAttribute) IsWriteOnly() bool {
188+
return false
189+
}
190+
185191
// DynamicValidators returns the Validators field value.
186192
func (a DynamicAttribute) DynamicValidators() []validator.Dynamic {
187193
return a.Validators

datasource/schema/dynamic_attribute_test.go

+30-1
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ import (
99
"testing"
1010

1111
"github.com/google/go-cmp/cmp"
12+
"github.com/hashicorp/terraform-plugin-go/tftypes"
13+
1214
"github.com/hashicorp/terraform-plugin-framework/attr"
1315
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
1416
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
1517
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema"
1618
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes"
1719
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1820
"github.com/hashicorp/terraform-plugin-framework/types"
19-
"github.com/hashicorp/terraform-plugin-go/tftypes"
2021
)
2122

2223
func TestDynamicAttributeApplyTerraform5AttributePathStep(t *testing.T) {
@@ -390,6 +391,34 @@ func TestDynamicAttributeIsSensitive(t *testing.T) {
390391
}
391392
}
392393

394+
func TestDynamicAttributeIsWriteOnly(t *testing.T) {
395+
t.Parallel()
396+
397+
testCases := map[string]struct {
398+
attribute schema.DynamicAttribute
399+
expected bool
400+
}{
401+
"not-writeOnly": {
402+
attribute: schema.DynamicAttribute{},
403+
expected: false,
404+
},
405+
}
406+
407+
for name, testCase := range testCases {
408+
name, testCase := name, testCase
409+
410+
t.Run(name, func(t *testing.T) {
411+
t.Parallel()
412+
413+
got := testCase.attribute.IsWriteOnly()
414+
415+
if diff := cmp.Diff(got, testCase.expected); diff != "" {
416+
t.Errorf("unexpected difference: %s", diff)
417+
}
418+
})
419+
}
420+
}
421+
393422
func TestDynamicAttributeDynamicValidators(t *testing.T) {
394423
t.Parallel()
395424

datasource/schema/float32_attribute.go

+5
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,8 @@ func (a Float32Attribute) IsRequired() bool {
189189
func (a Float32Attribute) IsSensitive() bool {
190190
return a.Sensitive
191191
}
192+
193+
// IsWriteOnly returns false as write-only attributes are not supported in data source schemas.
194+
func (a Float32Attribute) IsWriteOnly() bool {
195+
return false
196+
}

datasource/schema/float32_attribute_test.go

+28
Original file line numberDiff line numberDiff line change
@@ -424,3 +424,31 @@ func TestFloat32AttributeIsSensitive(t *testing.T) {
424424
})
425425
}
426426
}
427+
428+
func TestFloat32AttributeIsWriteOnly(t *testing.T) {
429+
t.Parallel()
430+
431+
testCases := map[string]struct {
432+
attribute schema.Float32Attribute
433+
expected bool
434+
}{
435+
"not-writeOnly": {
436+
attribute: schema.Float32Attribute{},
437+
expected: false,
438+
},
439+
}
440+
441+
for name, testCase := range testCases {
442+
name, testCase := name, testCase
443+
444+
t.Run(name, func(t *testing.T) {
445+
t.Parallel()
446+
447+
got := testCase.attribute.IsWriteOnly()
448+
449+
if diff := cmp.Diff(got, testCase.expected); diff != "" {
450+
t.Errorf("unexpected difference: %s", diff)
451+
}
452+
})
453+
}
454+
}

datasource/schema/float64_attribute.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
package schema
55

66
import (
7+
"github.com/hashicorp/terraform-plugin-go/tftypes"
8+
79
"github.com/hashicorp/terraform-plugin-framework/attr"
810
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
911
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema"
1012
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1113
"github.com/hashicorp/terraform-plugin-framework/types"
1214
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
13-
"github.com/hashicorp/terraform-plugin-go/tftypes"
1415
)
1516

1617
// Ensure the implementation satisifies the desired interfaces.
@@ -188,3 +189,8 @@ func (a Float64Attribute) IsRequired() bool {
188189
func (a Float64Attribute) IsSensitive() bool {
189190
return a.Sensitive
190191
}
192+
193+
// IsWriteOnly returns false as write-only attributes are not supported in data source schemas.
194+
func (a Float64Attribute) IsWriteOnly() bool {
195+
return false
196+
}

datasource/schema/float64_attribute_test.go

+30-1
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ import (
99
"testing"
1010

1111
"github.com/google/go-cmp/cmp"
12+
"github.com/hashicorp/terraform-plugin-go/tftypes"
13+
1214
"github.com/hashicorp/terraform-plugin-framework/attr"
1315
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
1416
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
1517
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema"
1618
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes"
1719
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1820
"github.com/hashicorp/terraform-plugin-framework/types"
19-
"github.com/hashicorp/terraform-plugin-go/tftypes"
2021
)
2122

2223
func TestFloat64AttributeApplyTerraform5AttributePathStep(t *testing.T) {
@@ -423,3 +424,31 @@ func TestFloat64AttributeIsSensitive(t *testing.T) {
423424
})
424425
}
425426
}
427+
428+
func TestFloat64AttributeIsWriteOnly(t *testing.T) {
429+
t.Parallel()
430+
431+
testCases := map[string]struct {
432+
attribute schema.Float64Attribute
433+
expected bool
434+
}{
435+
"not-writeOnly": {
436+
attribute: schema.Float64Attribute{},
437+
expected: false,
438+
},
439+
}
440+
441+
for name, testCase := range testCases {
442+
name, testCase := name, testCase
443+
444+
t.Run(name, func(t *testing.T) {
445+
t.Parallel()
446+
447+
got := testCase.attribute.IsWriteOnly()
448+
449+
if diff := cmp.Diff(got, testCase.expected); diff != "" {
450+
t.Errorf("unexpected difference: %s", diff)
451+
}
452+
})
453+
}
454+
}

datasource/schema/int32_attribute.go

+5
Original file line numberDiff line numberDiff line change
@@ -189,3 +189,8 @@ func (a Int32Attribute) IsRequired() bool {
189189
func (a Int32Attribute) IsSensitive() bool {
190190
return a.Sensitive
191191
}
192+
193+
// IsWriteOnly returns false as write-only attributes are not supported in data source schemas.
194+
func (a Int32Attribute) IsWriteOnly() bool {
195+
return false
196+
}

datasource/schema/int32_attribute_test.go

+28
Original file line numberDiff line numberDiff line change
@@ -424,3 +424,31 @@ func TestInt32AttributeIsSensitive(t *testing.T) {
424424
})
425425
}
426426
}
427+
428+
func TestInt32AttributeIsWriteOnly(t *testing.T) {
429+
t.Parallel()
430+
431+
testCases := map[string]struct {
432+
attribute schema.Int32Attribute
433+
expected bool
434+
}{
435+
"not-writeOnly": {
436+
attribute: schema.Int32Attribute{},
437+
expected: false,
438+
},
439+
}
440+
441+
for name, testCase := range testCases {
442+
name, testCase := name, testCase
443+
444+
t.Run(name, func(t *testing.T) {
445+
t.Parallel()
446+
447+
got := testCase.attribute.IsWriteOnly()
448+
449+
if diff := cmp.Diff(got, testCase.expected); diff != "" {
450+
t.Errorf("unexpected difference: %s", diff)
451+
}
452+
})
453+
}
454+
}

datasource/schema/int64_attribute.go

+7-1
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
package schema
55

66
import (
7+
"github.com/hashicorp/terraform-plugin-go/tftypes"
8+
79
"github.com/hashicorp/terraform-plugin-framework/attr"
810
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
911
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema/fwxschema"
1012
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1113
"github.com/hashicorp/terraform-plugin-framework/types"
1214
"github.com/hashicorp/terraform-plugin-framework/types/basetypes"
13-
"github.com/hashicorp/terraform-plugin-go/tftypes"
1415
)
1516

1617
// Ensure the implementation satisifies the desired interfaces.
@@ -188,3 +189,8 @@ func (a Int64Attribute) IsRequired() bool {
188189
func (a Int64Attribute) IsSensitive() bool {
189190
return a.Sensitive
190191
}
192+
193+
// IsWriteOnly returns false as write-only attributes are not supported in data source schemas.
194+
func (a Int64Attribute) IsWriteOnly() bool {
195+
return false
196+
}

datasource/schema/int64_attribute_test.go

+30-1
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,15 @@ import (
99
"testing"
1010

1111
"github.com/google/go-cmp/cmp"
12+
"github.com/hashicorp/terraform-plugin-go/tftypes"
13+
1214
"github.com/hashicorp/terraform-plugin-framework/attr"
1315
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
1416
"github.com/hashicorp/terraform-plugin-framework/internal/fwschema"
1517
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testschema"
1618
"github.com/hashicorp/terraform-plugin-framework/internal/testing/testtypes"
1719
"github.com/hashicorp/terraform-plugin-framework/schema/validator"
1820
"github.com/hashicorp/terraform-plugin-framework/types"
19-
"github.com/hashicorp/terraform-plugin-go/tftypes"
2021
)
2122

2223
func TestInt64AttributeApplyTerraform5AttributePathStep(t *testing.T) {
@@ -423,3 +424,31 @@ func TestInt64AttributeIsSensitive(t *testing.T) {
423424
})
424425
}
425426
}
427+
428+
func TestInt64AttributeIsWriteOnly(t *testing.T) {
429+
t.Parallel()
430+
431+
testCases := map[string]struct {
432+
attribute schema.Int64Attribute
433+
expected bool
434+
}{
435+
"not-writeOnly": {
436+
attribute: schema.Int64Attribute{},
437+
expected: false,
438+
},
439+
}
440+
441+
for name, testCase := range testCases {
442+
name, testCase := name, testCase
443+
444+
t.Run(name, func(t *testing.T) {
445+
t.Parallel()
446+
447+
got := testCase.attribute.IsWriteOnly()
448+
449+
if diff := cmp.Diff(got, testCase.expected); diff != "" {
450+
t.Errorf("unexpected difference: %s", diff)
451+
}
452+
})
453+
}
454+
}

0 commit comments

Comments
 (0)