diff --git a/configs/configschema/coerce_value.go b/configs/configschema/coerce_value.go index 23a48e1be..41a533745 100644 --- a/configs/configschema/coerce_value.go +++ b/configs/configschema/coerce_value.go @@ -8,9 +8,7 @@ import ( ) // CoerceValue attempts to force the given value to conform to the type -// implied by the receiever, while also applying the same validation and -// transformation rules that would be applied by the decoder specification -// returned by method DecoderSpec. +// implied by the receiever. // // This is useful in situations where a configuration must be derived from // an already-decoded value. It is always better to decode directly from @@ -83,16 +81,8 @@ func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) { if err != nil { return cty.UnknownVal(b.ImpliedType()), err } - case blockS.MinItems != 1 && blockS.MaxItems != 1: - if blockS.Nesting == NestingGroup { - attrs[typeName] = blockS.EmptyValue() - } else { - attrs[typeName] = cty.NullVal(blockS.ImpliedType()) - } default: - // We use the word "attribute" here because we're talking about - // the cty sense of that word rather than the HCL sense. - return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("attribute %q is required", typeName) + attrs[typeName] = blockS.EmptyValue() } case NestingList: diff --git a/configs/configschema/coerce_value_test.go b/configs/configschema/coerce_value_test.go index 084107878..974dfdd35 100644 --- a/configs/configschema/coerce_value_test.go +++ b/configs/configschema/coerce_value_test.go @@ -288,8 +288,10 @@ func TestCoerceValue(t *testing.T) { }, }, cty.EmptyObjectVal, - cty.DynamicVal, - `attribute "foo" is required`, + cty.ObjectVal(map[string]cty.Value{ + "foo": cty.NullVal(cty.EmptyObject), + }), + ``, }, "unknown nested list": { &Block{ diff --git a/configs/configschema/decoder_spec.go b/configs/configschema/decoder_spec.go index e748dd20d..c4bc3be03 100644 --- a/configs/configschema/decoder_spec.go +++ b/configs/configschema/decoder_spec.go @@ -35,7 +35,8 @@ func (b *Block) DecoderSpec() hcldec.Spec { // We can only validate 0 or 1 for MinItems, because a dynamic block // may satisfy any number of min items while only having a single - // block in the config. + // block in the config. We cannot validate MaxItems because a + // configuration may have any number of dynamic blocks minItems := 0 if blockS.MinItems > 1 { minItems = 1 @@ -46,7 +47,7 @@ func (b *Block) DecoderSpec() hcldec.Spec { ret[name] = &hcldec.BlockSpec{ TypeName: name, Nested: childSpec, - Required: blockS.MinItems == 1 && blockS.MaxItems >= 1, + Required: blockS.MinItems == 1, } if blockS.Nesting == NestingGroup { ret[name] = &hcldec.DefaultSpec{ @@ -66,14 +67,12 @@ func (b *Block) DecoderSpec() hcldec.Spec { TypeName: name, Nested: childSpec, MinItems: minItems, - MaxItems: blockS.MaxItems, } } else { ret[name] = &hcldec.BlockListSpec{ TypeName: name, Nested: childSpec, MinItems: minItems, - MaxItems: blockS.MaxItems, } } case NestingSet: @@ -86,7 +85,6 @@ func (b *Block) DecoderSpec() hcldec.Spec { TypeName: name, Nested: childSpec, MinItems: minItems, - MaxItems: blockS.MaxItems, } case NestingMap: // We prefer to use a list where possible, since it makes our diff --git a/configs/configschema/decoder_spec_test.go b/configs/configschema/decoder_spec_test.go index d6f6a6a15..1e7b1d4c6 100644 --- a/configs/configschema/decoder_spec_test.go +++ b/configs/configschema/decoder_spec_test.go @@ -354,7 +354,7 @@ func TestBlockDecoderSpec(t *testing.T) { cty.EmptyObjectVal, }), }), - 1, // too many "foo" blocks + 0, // max items cannot be validated during decode }, // dynamic blocks may fulfill MinItems, but there is only one block to // decode.