lang: Consider "dynamic" blocks when resolving references
The hcldec package has no awareness of the dynamic block extension, so the hcldec.Variables function misses any variables declared inside dynamic blocks. dynblock.VariablesHCLDec is a drop-in replacement for hcldec.Variables that _is_ aware of dynamic blocks, returning all of the same variables that hcldec would find naturally plus also any variables used inside the dynamic block "for_each" and "labels" arguments and inside the nested "content" block.
This commit is contained in:
parent
838a42d218
commit
50a101afbd
|
@ -1,8 +1,8 @@
|
||||||
package lang
|
package lang
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/hashicorp/hcl2/ext/dynblock"
|
||||||
"github.com/hashicorp/hcl2/hcl"
|
"github.com/hashicorp/hcl2/hcl"
|
||||||
"github.com/hashicorp/hcl2/hcldec"
|
|
||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
"github.com/hashicorp/terraform/configs/configschema"
|
"github.com/hashicorp/terraform/configs/configschema"
|
||||||
"github.com/hashicorp/terraform/tfdiags"
|
"github.com/hashicorp/terraform/tfdiags"
|
||||||
|
@ -52,7 +52,13 @@ func ReferencesInBlock(body hcl.Body, schema *configschema.Block) ([]*addrs.Refe
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
spec := schema.DecoderSpec()
|
spec := schema.DecoderSpec()
|
||||||
traversals := hcldec.Variables(body, spec)
|
|
||||||
|
// We use dynblock.VariablesHCLDec instead of hcldec.Variables here because
|
||||||
|
// when we evaluate a block we'll apply the HCL dynamic block extension
|
||||||
|
// expansion to it first, and so we need this specialized version in order
|
||||||
|
// to properly understand what the dependencies will be once expanded.
|
||||||
|
// Otherwise, we'd miss references that only occur inside dynamic blocks.
|
||||||
|
traversals := dynblock.VariablesHCLDec(body, spec)
|
||||||
return References(traversals)
|
return References(traversals)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"github.com/hashicorp/terraform/addrs"
|
"github.com/hashicorp/terraform/addrs"
|
||||||
"github.com/hashicorp/terraform/configs/configschema"
|
"github.com/hashicorp/terraform/configs/configschema"
|
||||||
"github.com/hashicorp/terraform/providers"
|
"github.com/hashicorp/terraform/providers"
|
||||||
|
"github.com/zclconf/go-cty/cty"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestPlanGraphBuilder_impl(t *testing.T) {
|
func TestPlanGraphBuilder_impl(t *testing.T) {
|
||||||
|
@ -67,6 +68,90 @@ func TestPlanGraphBuilder(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPlanGraphBuilder_dynamicBlock(t *testing.T) {
|
||||||
|
provider := &MockProvider{
|
||||||
|
GetSchemaReturn: &ProviderSchema{
|
||||||
|
ResourceTypes: map[string]*configschema.Block{
|
||||||
|
"test_thing": {
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"id": {Type: cty.String, Computed: true},
|
||||||
|
"list": {Type: cty.List(cty.String), Computed: true},
|
||||||
|
},
|
||||||
|
BlockTypes: map[string]*configschema.NestedBlock{
|
||||||
|
"nested": {
|
||||||
|
Nesting: configschema.NestingList,
|
||||||
|
Block: configschema.Block{
|
||||||
|
Attributes: map[string]*configschema.Attribute{
|
||||||
|
"foo": {Type: cty.String, Optional: true},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
components := &basicComponentFactory{
|
||||||
|
providers: map[string]providers.Factory{
|
||||||
|
"test": providers.FactoryFixed(provider),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
b := &PlanGraphBuilder{
|
||||||
|
Config: testModule(t, "graph-builder-plan-dynblock"),
|
||||||
|
Components: components,
|
||||||
|
Schemas: &Schemas{
|
||||||
|
Providers: map[string]*ProviderSchema{
|
||||||
|
"test": provider.GetSchemaReturn,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DisableReduce: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
g, err := b.Build(addrs.RootModuleInstance)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("err: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if g.Path.String() != addrs.RootModuleInstance.String() {
|
||||||
|
t.Fatalf("wrong module path %q", g.Path)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This test is here to make sure we properly detect references inside
|
||||||
|
// the special "dynamic" block construct. The most important thing here
|
||||||
|
// is that at the end test_thing.c depends on both test_thing.a and
|
||||||
|
// test_thing.b. Other details might shift over time as other logic in
|
||||||
|
// the graph builders changes.
|
||||||
|
actual := strings.TrimSpace(g.String())
|
||||||
|
expected := strings.TrimSpace(`
|
||||||
|
meta.count-boundary (EachMode fixup)
|
||||||
|
provider.test
|
||||||
|
test_thing.a
|
||||||
|
test_thing.b
|
||||||
|
test_thing.c
|
||||||
|
provider.test
|
||||||
|
provider.test (close)
|
||||||
|
provider.test
|
||||||
|
test_thing.a
|
||||||
|
test_thing.b
|
||||||
|
test_thing.c
|
||||||
|
root
|
||||||
|
meta.count-boundary (EachMode fixup)
|
||||||
|
provider.test (close)
|
||||||
|
test_thing.a
|
||||||
|
provider.test
|
||||||
|
test_thing.b
|
||||||
|
provider.test
|
||||||
|
test_thing.c
|
||||||
|
provider.test
|
||||||
|
test_thing.a
|
||||||
|
test_thing.b
|
||||||
|
`)
|
||||||
|
if actual != expected {
|
||||||
|
t.Fatalf("expected:\n%s\n\ngot:\n%s", expected, actual)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestPlanGraphBuilder_targetModule(t *testing.T) {
|
func TestPlanGraphBuilder_targetModule(t *testing.T) {
|
||||||
b := &PlanGraphBuilder{
|
b := &PlanGraphBuilder{
|
||||||
Config: testModule(t, "graph-builder-plan-target-module-provider"),
|
Config: testModule(t, "graph-builder-plan-target-module-provider"),
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
resource "test_thing" "a" {
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "test_thing" "b" {
|
||||||
|
}
|
||||||
|
|
||||||
|
resource "test_thing" "c" {
|
||||||
|
dynamic "nested" {
|
||||||
|
for_each = test_thing.a.list
|
||||||
|
content {
|
||||||
|
foo = test_thing.b.id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue