From 78322d5843daf76b72d6023a39094642f8d69886 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Thu, 1 Oct 2020 13:49:27 -0400 Subject: [PATCH] data depends_on with indexed references If a data source refers to a indexed managed resource, we need to re-target that reference to the containing resource for planning. Since data sources use the same mechanism as depends_on for managed resource references, they can only refer to resources as a whole. --- terraform/context_plan_test.go | 17 +++++++-- terraform/transform_reference.go | 64 +++++++++++++++++++------------- 2 files changed, 52 insertions(+), 29 deletions(-) diff --git a/terraform/context_plan_test.go b/terraform/context_plan_test.go index adf93cc7c..ef3ddcf91 100644 --- a/terraform/context_plan_test.go +++ b/terraform/context_plan_test.go @@ -6346,17 +6346,28 @@ func TestContext2Plan_dataReferencesResource(t *testing.T) { m := testModuleInline(t, map[string]string{ "main.tf": ` locals { - x = "value" + x = "value" } resource "test_resource" "a" { - value = local.x + value = local.x } // test_resource.a.value can be resolved during plan, but the reference implies // that the data source should wait until the resource is created. data "test_data_source" "d" { - foo = test_resource.a.value + foo = test_resource.a.value +} + +// ensure referencing an indexed instance that has not yet created will also +// delay reading the data source +resource "test_resource" "b" { + count = 2 + value = local.x +} + +data "test_data_source" "e" { + foo = test_resource.b[0].value } `}) diff --git a/terraform/transform_reference.go b/terraform/transform_reference.go index 44aae0fc8..168929a0d 100644 --- a/terraform/transform_reference.go +++ b/terraform/transform_reference.go @@ -328,30 +328,8 @@ func (m ReferenceMap) dependsOn(g *Graph, depender graphNodeDependsOn) ([]dag.Ve refs := depender.DependsOn() - // For data sources we implicitly treat references as depends_on entries. - // If a data source references a resource, even if that reference is - // resolvable, it stands to reason that the user intends for the data - // source to require that resource in some way. - if n, ok := depender.(GraphNodeConfigResource); ok && - n.ResourceAddr().Resource.Mode == addrs.DataResourceMode { - for _, r := range depender.References() { - - // We don't need to wait on referenced data sources. They have no - // side effects, so our configuration reference should suffice for - // proper ordering. - var resAddr addrs.Resource - switch s := r.Subject.(type) { - case addrs.Resource: - resAddr = s - case addrs.ResourceInstance: - resAddr = s.Resource - } - - if resAddr.Mode == addrs.ManagedResourceMode { - refs = append(refs, r) - } - } - } + // get any implied dependencies for data sources + refs = append(refs, m.dataDependsOn(depender)...) // This is where we record that a module has depends_on configured. if _, ok := depender.(*nodeExpandModule); ok && len(refs) > 0 { @@ -391,10 +369,44 @@ func (m ReferenceMap) dependsOn(g *Graph, depender graphNodeDependsOn) ([]dag.Ve return res, fromModule || fromParentModule } +// Return extra depends_on references if this is a data source. +// For data sources we implicitly treat references to managed resources as +// depends_on entries. If a data source references a managed resource, even if +// that reference is resolvable, it stands to reason that the user intends for +// the data source to require that resource in some way. +func (m ReferenceMap) dataDependsOn(depender graphNodeDependsOn) []*addrs.Reference { + var refs []*addrs.Reference + if n, ok := depender.(GraphNodeConfigResource); ok && + n.ResourceAddr().Resource.Mode == addrs.DataResourceMode { + for _, r := range depender.References() { + + var resAddr addrs.Resource + switch s := r.Subject.(type) { + case addrs.Resource: + resAddr = s + case addrs.ResourceInstance: + resAddr = s.Resource + r.Subject = resAddr + } + + if resAddr.Mode != addrs.ManagedResourceMode { + // We only want to wait on directly referenced managed resources. + // Data sources have no external side effects, so normal + // references to them in the config will suffice for proper + // ordering. + continue + } + + refs = append(refs, r) + } + } + return refs +} + // parentModuleDependsOn returns the set of vertices that a data sources parent // module references through the module call's depends_on. The bool return // value indicates if depends_on was found in a parent module configuration. -func (n ReferenceMap) parentModuleDependsOn(g *Graph, depender graphNodeDependsOn) ([]dag.Vertex, bool) { +func (m ReferenceMap) parentModuleDependsOn(g *Graph, depender graphNodeDependsOn) ([]dag.Vertex, bool) { var res []dag.Vertex fromModule := false @@ -408,7 +420,7 @@ func (n ReferenceMap) parentModuleDependsOn(g *Graph, depender graphNodeDependsO continue } - deps, fromParentModule := n.dependsOn(g, mod) + deps, fromParentModule := m.dependsOn(g, mod) for _, dep := range deps { // add the dependency res = append(res, dep)