From 09646ef51d674fefc74cbececf0c08d503a183b6 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Mon, 17 Feb 2025 13:08:33 -0500 Subject: [PATCH] add validation for nested write-only within sets We can't use write-only within sets, because there is no way to assign an ephemeral value to an individual attribute within a set, without making the entire set ephemeral. This will be enforced by the provider framework as well to help prevent unusable schemas for reaching Terraform. --- .../configs/configschema/internal_validate.go | 14 ++++++- .../configschema/internal_validate_test.go | 39 +++++++++++++++++++ 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/internal/configs/configschema/internal_validate.go b/internal/configs/configschema/internal_validate.go index 4754da77d3..55681cac9a 100644 --- a/internal/configs/configschema/internal_validate.go +++ b/internal/configs/configschema/internal_validate.go @@ -77,6 +77,12 @@ func (b *Block) internalValidate(prefix string) error { // properly hash them, and so can't support mixed types. multiErr = errors.Join(multiErr, fmt.Errorf("%s%s: NestingSet blocks may not contain attributes of cty.DynamicPseudoType", prefix, name)) } + if blockS.Block.ContainsWriteOnly() { + // This is not permitted because any marks within sets will + // be hoisted up the outer set value, so only the set itself + // can be WriteOnly. + multiErr = errors.Join(multiErr, fmt.Errorf("%s%s: NestingSet blocks may not contain WriteOnly attributes", prefix, name)) + } } case NestingMap: if blockS.MinItems != 0 || blockS.MaxItems != 0 { @@ -142,7 +148,13 @@ func (a *Attribute) internalValidate(name, prefix string) error { // This is not permitted because the HCL (cty) set implementation // needs to know the exact type of set elements in order to // properly hash them, and so can't support mixed types. - err = errors.Join(err, fmt.Errorf("%s%s: NestingSet blocks may not contain attributes of cty.DynamicPseudoType", prefix, name)) + err = errors.Join(err, fmt.Errorf("%s%s: NestingSet attributes may not contain attributes of cty.DynamicPseudoType", prefix, name)) + } + if a.NestedType.ContainsWriteOnly() { + // This is not permitted because any marks within sets will + // be hoisted up the outer set value, so only the set itself + // can be WriteOnly. + err = errors.Join(err, fmt.Errorf("%s%s: NestingSet attributes may not contain WriteOnly attributes", prefix, name)) } } default: diff --git a/internal/configs/configschema/internal_validate_test.go b/internal/configs/configschema/internal_validate_test.go index 0c8e77f0fb..0f2e4e141b 100644 --- a/internal/configs/configschema/internal_validate_test.go +++ b/internal/configs/configschema/internal_validate_test.go @@ -265,6 +265,45 @@ func TestBlockInternalValidate(t *testing.T) { }, []string{"bad: block schema is nil"}, }, + "nested set block with write-only attribute": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "bad": { + Nesting: NestingSet, + Block: Block{ + Attributes: map[string]*Attribute{ + "wo": { + Type: cty.String, + Optional: true, + WriteOnly: true, + }, + }, + }, + }, + }, + }, + []string{"bad: NestingSet blocks may not contain WriteOnly attributes"}, + }, + "nested set attributes with write-only attribute": { + &Block{ + Attributes: map[string]*Attribute{ + "bad": { + NestedType: &Object{ + Attributes: map[string]*Attribute{ + "wo": { + Type: cty.String, + Optional: true, + WriteOnly: true, + }, + }, + Nesting: NestingSet, + }, + Optional: true, + }, + }, + }, + []string{"bad: NestingSet attributes may not contain WriteOnly attributes"}, + }, } for name, test := range tests {