Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 4 additions & 18 deletions ext/dynblock/expand_body.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,28 +201,14 @@ func (b *expandBody) expandBlocks(schema *hcl.BodySchema, rawBlocks hcl.Blocks,
}
}
} else {
// If our top-level iteration value isn't known then we're forced
// to compromise since HCL doesn't have any concept of an
// "unknown block". In this case then, we'll produce a single
// dynamic block with the iterator values set to DynamicVal,
// which at least makes the potential for a block visible
// in our result, even though it's not represented in a fully-accurate
// way.
// If our top-level iteration value isn't known then we
// substitute an unknownBody, which will cause the entire block
// to evaluate to an unknown value.
i := b.iteration.MakeChild(spec.iteratorName, cty.DynamicVal, cty.DynamicVal)
block, blockDiags := spec.newBlock(i, b.forEachCtx)
diags = append(diags, blockDiags...)
if block != nil {
block.Body = b.expandChild(block.Body, i)

// We additionally force all of the leaf attribute values
// in the result to be unknown so the calling application
// can, if necessary, use that as a heuristic to detect
// when a single nested block might be standing in for
// multiple blocks yet to be expanded. This retains the
// structure of the generated body but forces all of its
// leaf attribute values to be unknown.
block.Body = unknownBody{block.Body}

block.Body = unknownBody{b.expandChild(block.Body, i)}
blocks = append(blocks, block)
}
}
Expand Down
46 changes: 46 additions & 0 deletions ext/dynblock/expand_body_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package dynblock

import (
"strings"
"testing"

"github.com/hashicorp/hcl/v2"
Expand Down Expand Up @@ -441,6 +442,28 @@ func TestExpandUnknownBodies(t *testing.T) {
},
}),
},
{
Type: "dynamic",
Labels: []string{"invalid_list"},
LabelRanges: []hcl.Range{hcl.Range{}},
Body: hcltest.MockBody(&hcl.BodyContent{
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
"for_each": hcltest.MockExprLiteral(cty.UnknownVal(cty.Map(cty.String))),
}),
Blocks: hcl.Blocks{
{
Type: "content",
Body: hcltest.MockBody(&hcl.BodyContent{
Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
"val": hcltest.MockExprTraversalSrc("each.value"),
// unexpected attributes should still produce an error
"invalid": hcltest.MockExprLiteral(cty.StringVal("static")),
}),
}),
},
},
}),
},
},
}

Expand Down Expand Up @@ -574,4 +597,27 @@ func TestExpandUnknownBodies(t *testing.T) {
}
})

t.Run("DecodeInvalidList", func(t *testing.T) {
decSpec := &hcldec.BlockListSpec{
TypeName: "invalid_list",
Nested: &hcldec.ObjectSpec{
"val": &hcldec.AttrSpec{
Name: "val",
Type: cty.String,
},
},
}

_, _, diags := hcldec.PartialDecode(dynBody, decSpec, nil)
if len(diags) != 1 {
t.Error("expected 1 extraneous argument")
}

want := `Mock body has extraneous argument "invalid"`

if !strings.Contains(diags.Error(), want) {
t.Errorf("unexpected diagnostics: %v", diags)
}
})

}
15 changes: 9 additions & 6 deletions hcldec/spec.go
Original file line number Diff line number Diff line change
Expand Up @@ -468,6 +468,9 @@ func (s *BlockListSpec) decode(content *hcl.BodyContent, blockLabels []blockLabe
continue
}

val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false)
diags = append(diags, childDiags...)

if u, ok := childBlock.Body.(UnknownBody); ok {
if u.Unknown() {
// If any block Body is unknown, then the entire block value
Expand All @@ -476,8 +479,6 @@ func (s *BlockListSpec) decode(content *hcl.BodyContent, blockLabels []blockLabe
}
}

val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false)
diags = append(diags, childDiags...)
elems = append(elems, val)
sourceRanges = append(sourceRanges, sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested))
}
Expand Down Expand Up @@ -629,6 +630,9 @@ func (s *BlockTupleSpec) decode(content *hcl.BodyContent, blockLabels []blockLab
continue
}

val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false)
diags = append(diags, childDiags...)

if u, ok := childBlock.Body.(UnknownBody); ok {
if u.Unknown() {
// If any block Body is unknown, then the entire block value
Expand All @@ -637,8 +641,6 @@ func (s *BlockTupleSpec) decode(content *hcl.BodyContent, blockLabels []blockLab
}
}

val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false)
diags = append(diags, childDiags...)
elems = append(elems, val)
sourceRanges = append(sourceRanges, sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested))
}
Expand Down Expand Up @@ -751,6 +753,9 @@ func (s *BlockSetSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel
continue
}

val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false)
diags = append(diags, childDiags...)

if u, ok := childBlock.Body.(UnknownBody); ok {
if u.Unknown() {
// If any block Body is unknown, then the entire block value
Expand All @@ -759,8 +764,6 @@ func (s *BlockSetSpec) decode(content *hcl.BodyContent, blockLabels []blockLabel
}
}

val, _, childDiags := decode(childBlock.Body, labelsForBlock(childBlock), ctx, s.Nested, false)
diags = append(diags, childDiags...)
elems = append(elems, val)
sourceRanges = append(sourceRanges, sourceRange(childBlock.Body, labelsForBlock(childBlock), s.Nested))
}
Expand Down