Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

decode unknown dynamic blocks to ensure they are valid #476

Merged
merged 1 commit into from
Jul 21, 2021
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