summaryrefslogtreecommitdiff
path: root/ext/dynblock
diff options
context:
space:
mode:
Diffstat (limited to 'ext/dynblock')
-rw-r--r--ext/dynblock/expand_body.go10
-rw-r--r--ext/dynblock/expand_body_test.go53
-rw-r--r--ext/dynblock/unknown_body.go84
3 files changed, 145 insertions, 2 deletions
diff --git a/ext/dynblock/expand_body.go b/ext/dynblock/expand_body.go
index 97d1e4d..dd30822 100644
--- a/ext/dynblock/expand_body.go
+++ b/ext/dynblock/expand_body.go
@@ -213,6 +213,16 @@ func (b *expandBody) expandBlocks(schema *hcl.BodySchema, rawBlocks hcl.Blocks,
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}
+
blocks = append(blocks, block)
}
}
diff --git a/ext/dynblock/expand_body_test.go b/ext/dynblock/expand_body_test.go
index b8d10e2..350dbe3 100644
--- a/ext/dynblock/expand_body_test.go
+++ b/ext/dynblock/expand_body_test.go
@@ -3,9 +3,8 @@ package dynblock
import (
"testing"
- "github.com/hashicorp/hcl2/hcldec"
-
"github.com/hashicorp/hcl2/hcl"
+ "github.com/hashicorp/hcl2/hcldec"
"github.com/hashicorp/hcl2/hcltest"
"github.com/zclconf/go-cty/cty"
)
@@ -192,6 +191,47 @@ func TestExpand(t *testing.T) {
}),
},
{
+ Type: "dynamic",
+ Labels: []string{"b"},
+ 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))),
+ "iterator": hcltest.MockExprVariable("dyn_b"),
+ }),
+ Blocks: hcl.Blocks{
+ {
+ Type: "content",
+ Body: hcltest.MockBody(&hcl.BodyContent{
+ Blocks: hcl.Blocks{
+ {
+ Type: "dynamic",
+ Labels: []string{"c"},
+ LabelRanges: []hcl.Range{hcl.Range{}},
+ Body: hcltest.MockBody(&hcl.BodyContent{
+ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
+ "for_each": hcltest.MockExprTraversalSrc("dyn_b.value"),
+ }),
+ Blocks: hcl.Blocks{
+ {
+ Type: "content",
+ Body: hcltest.MockBody(&hcl.BodyContent{
+ Attributes: hcltest.MockAttrs(map[string]hcl.Expression{
+ "val0": hcltest.MockExprTraversalSrc("c.value"),
+ "val1": hcltest.MockExprTraversalSrc("dyn_b.key"),
+ }),
+ }),
+ },
+ },
+ }),
+ },
+ },
+ }),
+ },
+ },
+ }),
+ },
+ {
Type: "a",
Labels: []string{"static1"},
LabelRanges: []hcl.Range{hcl.Range{}},
@@ -324,6 +364,15 @@ func TestExpand(t *testing.T) {
"val1": cty.StringVal("foo"),
}),
}),
+ cty.ListVal([]cty.Value{
+ // This one comes from a dynamic block with an unknown for_each
+ // value, so we produce a single block object with all of the
+ // leaf attribute values set to unknown values.
+ cty.ObjectVal(map[string]cty.Value{
+ "val0": cty.UnknownVal(cty.String),
+ "val1": cty.UnknownVal(cty.String),
+ }),
+ }),
})
if !got.RawEquals(want) {
diff --git a/ext/dynblock/unknown_body.go b/ext/dynblock/unknown_body.go
new file mode 100644
index 0000000..932f6a3
--- /dev/null
+++ b/ext/dynblock/unknown_body.go
@@ -0,0 +1,84 @@
+package dynblock
+
+import (
+ "github.com/hashicorp/hcl2/hcl"
+ "github.com/zclconf/go-cty/cty"
+)
+
+// unknownBody is a funny body that just reports everything inside it as
+// unknown. It uses a given other body as a sort of template for what attributes
+// and blocks are inside -- including source location information -- but
+// subsitutes unknown values of unknown type for all attributes.
+//
+// This rather odd process is used to handle expansion of dynamic blocks whose
+// for_each expression is unknown. Since a block cannot itself be unknown,
+// we instead arrange for everything _inside_ the block to be unknown instead,
+// to give the best possible approximation.
+type unknownBody struct {
+ template hcl.Body
+}
+
+var _ hcl.Body = unknownBody{}
+
+func (b unknownBody) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
+ content, diags := b.template.Content(schema)
+ content = b.fixupContent(content)
+
+ // We're intentionally preserving the diagnostics reported from the
+ // inner body so that we can still report where the template body doesn't
+ // match the requested schema.
+ return content, diags
+}
+
+func (b unknownBody) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
+ content, remain, diags := b.template.PartialContent(schema)
+ content = b.fixupContent(content)
+ remain = unknownBody{remain} // remaining content must also be wrapped
+
+ // We're intentionally preserving the diagnostics reported from the
+ // inner body so that we can still report where the template body doesn't
+ // match the requested schema.
+ return content, remain, diags
+}
+
+func (b unknownBody) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
+ attrs, diags := b.template.JustAttributes()
+ attrs = b.fixupAttrs(attrs)
+
+ // We're intentionally preserving the diagnostics reported from the
+ // inner body so that we can still report where the template body doesn't
+ // match the requested schema.
+ return attrs, diags
+}
+
+func (b unknownBody) MissingItemRange() hcl.Range {
+ return b.template.MissingItemRange()
+}
+
+func (b unknownBody) fixupContent(got *hcl.BodyContent) *hcl.BodyContent {
+ ret := &hcl.BodyContent{}
+ ret.Attributes = b.fixupAttrs(got.Attributes)
+ if len(got.Blocks) > 0 {
+ ret.Blocks = make(hcl.Blocks, 0, len(got.Blocks))
+ for _, gotBlock := range got.Blocks {
+ new := *gotBlock // shallow copy
+ new.Body = unknownBody{gotBlock.Body} // nested content must also be marked unknown
+ ret.Blocks = append(ret.Blocks, &new)
+ }
+ }
+
+ return ret
+}
+
+func (b unknownBody) fixupAttrs(got hcl.Attributes) hcl.Attributes {
+ if len(got) == 0 {
+ return nil
+ }
+ ret := make(hcl.Attributes, len(got))
+ for name, gotAttr := range got {
+ new := *gotAttr // shallow copy
+ new.Expr = hcl.StaticExpr(cty.DynamicVal, gotAttr.Expr.Range())
+ ret[name] = &new
+ }
+ return ret
+}