don't check MinItems with unknowns in blocks
If a block was defined via "dynamic", there will be only one block value until the expansion is known. Since we can't detect dynamic blocks at this point, don't verify MinItems while there are unknown values in the config. The decoder spec can also only check for existence of a block, so limit the check to 0 or 1.
This commit is contained in:
parent
682286e184
commit
67dbd6d345
|
@ -113,7 +113,10 @@ func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) {
|
|||
return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("must be a list")
|
||||
}
|
||||
l := coll.LengthInt()
|
||||
if l < blockS.MinItems {
|
||||
|
||||
// Assume that if there are unknowns this could have come from
|
||||
// a dynamic block, and we can't validate MinItems yet.
|
||||
if l < blockS.MinItems && coll.IsWhollyKnown() {
|
||||
return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("insufficient items for attribute %q; must have at least %d", typeName, blockS.MinItems)
|
||||
}
|
||||
if l > blockS.MaxItems && blockS.MaxItems > 0 {
|
||||
|
@ -161,7 +164,10 @@ func (b *Block) coerceValue(in cty.Value, path cty.Path) (cty.Value, error) {
|
|||
return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("must be a set")
|
||||
}
|
||||
l := coll.LengthInt()
|
||||
if l < blockS.MinItems {
|
||||
|
||||
// Assume that if there are unknowns this could have come from
|
||||
// a dynamic block, and we can't validate MinItems yet.
|
||||
if l < blockS.MinItems && coll.IsWhollyKnown() {
|
||||
return cty.UnknownVal(b.ImpliedType()), path.NewErrorf("insufficient items for attribute %q; must have at least %d", typeName, blockS.MinItems)
|
||||
}
|
||||
if l > blockS.MaxItems && blockS.MaxItems > 0 {
|
||||
|
|
|
@ -331,7 +331,7 @@ func TestCoerceValue(t *testing.T) {
|
|||
"foo": {
|
||||
Block: Block{},
|
||||
Nesting: NestingList,
|
||||
MinItems: 1,
|
||||
MinItems: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -345,6 +345,39 @@ func TestCoerceValue(t *testing.T) {
|
|||
}),
|
||||
"",
|
||||
},
|
||||
"unknowns in nested list": {
|
||||
&Block{
|
||||
BlockTypes: map[string]*NestedBlock{
|
||||
"foo": {
|
||||
Block: Block{
|
||||
Attributes: map[string]*Attribute{
|
||||
"attr": {
|
||||
Type: cty.String,
|
||||
Required: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
Nesting: NestingList,
|
||||
MinItems: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"foo": cty.ListVal([]cty.Value{
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"attr": cty.UnknownVal(cty.String),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"foo": cty.ListVal([]cty.Value{
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"attr": cty.UnknownVal(cty.String),
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
"",
|
||||
},
|
||||
"unknown nested set": {
|
||||
&Block{
|
||||
Attributes: map[string]*Attribute{
|
||||
|
|
|
@ -33,6 +33,14 @@ func (b *Block) DecoderSpec() hcldec.Spec {
|
|||
|
||||
childSpec := blockS.Block.DecoderSpec()
|
||||
|
||||
// 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.
|
||||
minItems := 0
|
||||
if blockS.MinItems > 1 {
|
||||
minItems = 1
|
||||
}
|
||||
|
||||
switch blockS.Nesting {
|
||||
case NestingSingle, NestingGroup:
|
||||
ret[name] = &hcldec.BlockSpec{
|
||||
|
@ -57,14 +65,14 @@ func (b *Block) DecoderSpec() hcldec.Spec {
|
|||
ret[name] = &hcldec.BlockTupleSpec{
|
||||
TypeName: name,
|
||||
Nested: childSpec,
|
||||
MinItems: blockS.MinItems,
|
||||
MinItems: minItems,
|
||||
MaxItems: blockS.MaxItems,
|
||||
}
|
||||
} else {
|
||||
ret[name] = &hcldec.BlockListSpec{
|
||||
TypeName: name,
|
||||
Nested: childSpec,
|
||||
MinItems: blockS.MinItems,
|
||||
MinItems: minItems,
|
||||
MaxItems: blockS.MaxItems,
|
||||
}
|
||||
}
|
||||
|
@ -77,7 +85,7 @@ func (b *Block) DecoderSpec() hcldec.Spec {
|
|||
ret[name] = &hcldec.BlockSetSpec{
|
||||
TypeName: name,
|
||||
Nested: childSpec,
|
||||
MinItems: blockS.MinItems,
|
||||
MinItems: minItems,
|
||||
MaxItems: blockS.MaxItems,
|
||||
}
|
||||
case NestingMap:
|
||||
|
|
|
@ -356,6 +356,33 @@ func TestBlockDecoderSpec(t *testing.T) {
|
|||
}),
|
||||
1, // too many "foo" blocks
|
||||
},
|
||||
// dynamic blocks may fulfill MinItems, but there is only one block to
|
||||
// decode.
|
||||
"required MinItems": {
|
||||
&Block{
|
||||
BlockTypes: map[string]*NestedBlock{
|
||||
"foo": {
|
||||
Nesting: NestingList,
|
||||
Block: Block{},
|
||||
MinItems: 2,
|
||||
},
|
||||
},
|
||||
},
|
||||
hcltest.MockBody(&hcl.BodyContent{
|
||||
Blocks: hcl.Blocks{
|
||||
&hcl.Block{
|
||||
Type: "foo",
|
||||
Body: hcl.EmptyBody(),
|
||||
},
|
||||
},
|
||||
}),
|
||||
cty.ObjectVal(map[string]cty.Value{
|
||||
"foo": cty.ListVal([]cty.Value{
|
||||
cty.EmptyObjectVal,
|
||||
}),
|
||||
}),
|
||||
0,
|
||||
},
|
||||
"extraneous attribute": {
|
||||
&Block{},
|
||||
hcltest.MockBody(&hcl.BodyContent{
|
||||
|
|
Loading…
Reference in New Issue