Skip to content

Commit

Permalink
Merge pull request hashicorp#476 from hashicorp/jbardin/decode-unknow…
Browse files Browse the repository at this point in the history
…n-dynblock

decode unknown dynamic blocks to ensure they are valid
  • Loading branch information
jbardin committed Jul 21, 2021
2 parents 19e7232 + abe9c89 commit 82cdaa7
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 24 deletions.
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

0 comments on commit 82cdaa7

Please sign in to comment.