diff --git a/internal/configs/configschema/internal_validate.go b/internal/configs/configschema/internal_validate.go index 083494c668..96ec675afb 100644 --- a/internal/configs/configschema/internal_validate.go +++ b/internal/configs/configschema/internal_validate.go @@ -39,6 +39,11 @@ func (b *Block) internalValidate(prefix string) error { continue } multiErr = errors.Join(multiErr, attrS.internalValidate(name, prefix)) + + // all attributes within a computed block must also be computed + if b.Computed && !attrS.Computed { + multiErr = errors.Join(multiErr, fmt.Errorf("%s%s: all attributes within computed blocks must also be computed", prefix, name)) + } } for name, blockS := range b.BlockTypes { @@ -60,6 +65,11 @@ func (b *Block) internalValidate(prefix string) error { multiErr = errors.Join(multiErr, fmt.Errorf("%s%s: MinItems and MaxItems must both be greater than zero", prefix, name)) } + // any nested blocks within a computed block must also be computed + if b.Computed && !blockS.Computed { + multiErr = errors.Join(multiErr, fmt.Errorf("%s%s: all nested blocks within computed blocks must also be computed", prefix, name)) + } + switch blockS.Nesting { case NestingSingle: switch { diff --git a/internal/configs/configschema/internal_validate_test.go b/internal/configs/configschema/internal_validate_test.go index 80c88e828a..78427d7ec5 100644 --- a/internal/configs/configschema/internal_validate_test.go +++ b/internal/configs/configschema/internal_validate_test.go @@ -335,6 +335,38 @@ func TestBlockInternalValidate(t *testing.T) { }, []string{"bad: NestingSet attributes may not contain WriteOnly attributes"}, }, + + "nested computed block with non-computed attr": { + &Block{ + BlockTypes: map[string]*NestedBlock{ + "partial": &NestedBlock{ + Block: Block{ + Computed: true, + Attributes: map[string]*Attribute{ + "required": {Type: cty.String, Required: true}, + "computed": {Type: cty.String, Computed: true}, + }, + + BlockTypes: map[string]*NestedBlock{ + "nested": &NestedBlock{ + Block: Block{ + Attributes: map[string]*Attribute{ + "optional": {Type: cty.String, Optional: true}, + }, + }, + Nesting: NestingList, + }, + }, + }, + Nesting: NestingList, + }, + }, + }, + []string{ + `partial.required: all attributes within computed blocks must also be computed`, + `partial.nested: all nested blocks within computed blocks must also be computed`, + }, + }, } for name, test := range tests { @@ -460,10 +492,15 @@ func joinedErrors(err error) []error { Unwrap() []error } + var res []error + switch terr := err.(type) { case Unwrapper: - return terr.Unwrap() + for _, err := range terr.Unwrap() { + res = append(res, joinedErrors(err)...) + } default: return []error{err} } + return res }