From e74ebe178757b156bc0ca6eebb1a8209309be817 Mon Sep 17 00:00:00 2001 From: James Bardin Date: Wed, 3 Jun 2020 21:36:42 -0400 Subject: [PATCH] get more depends_on info for data source Since data sources may be read during refresh or early in plan, they need to know if there are any inherited dependencies from containing modules too. --- terraform/transform_reference.go | 85 ++++++++++++++++++++++---------- 1 file changed, 58 insertions(+), 27 deletions(-) diff --git a/terraform/transform_reference.go b/terraform/transform_reference.go index 5ea5bd8e04..ba5fc2b51f 100644 --- a/terraform/transform_reference.go +++ b/terraform/transform_reference.go @@ -43,6 +43,12 @@ type GraphNodeAttachDependencies interface { AttachDependencies([]addrs.ConfigResource) } +// graphNodeDependsOn is implemented by resources that need to expose any +// references set via DependsOn in their configuration. +type graphNodeDependsOn interface { + DependsOn() []*addrs.Reference +} + // graphNodeAttachResourceDependencies records all resources that are transitively // referenced through depends_on in the configuration. This is used by data // resources to determine if they can be read during the plan, or if they need @@ -55,8 +61,8 @@ type GraphNodeAttachDependencies interface { // "perpetual diff" type graphNodeAttachResourceDependencies interface { GraphNodeConfigResource + graphNodeDependsOn AttachResourceDependencies([]addrs.ConfigResource) - DependsOn() []*addrs.Reference } // GraphNodeReferenceOutside is an interface that can optionally be implemented. @@ -122,7 +128,7 @@ func (t *ReferenceTransformer) Transform(g *Graph) error { type depMap map[string]addrs.ConfigResource -// addDep adds the vertex if it represents a resource in the +// add stores the vertex if it represents a resource in the // graph. func (m depMap) add(v dag.Vertex) { // we're only concerned with resources which may have changes that @@ -155,34 +161,32 @@ func (t attachDataResourceDependenciesTransformer) Transform(g *Graph) error { if !ok { continue } - selfAddr := depender.ResourceAddr() // Only data need to attach depends_on, so they can determine if they // are eligible to be read during plan. - if selfAddr.Resource.Mode != addrs.DataResourceMode { + if depender.ResourceAddr().Resource.Mode != addrs.DataResourceMode { continue } - // depMap will only add resource references and dedupe - m := make(depMap) - - for _, dep := range refMap.DependsOn(v) { + // depMap will only add resource references then dedupe + deps := make(depMap) + for _, dep := range refMap.dependsOn(g, depender) { // any the dependency - m.add(dep) - // and check any ancestors + deps.add(dep) + // and check any ancestors for transitive dependencies ans, _ := g.Ancestors(dep) for _, v := range ans { - m.add(v) + deps.add(v) } } - deps := make([]addrs.ConfigResource, 0, len(m)) - for _, d := range m { - deps = append(deps, d) + res := make([]addrs.ConfigResource, 0, len(deps)) + for _, d := range deps { + res = append(res, d) } - log.Printf("[TRACE] AttachDependsOnTransformer: %s depends on %s", depender.ResourceAddr(), deps) - depender.AttachResourceDependencies(deps) + log.Printf("[TRACE] attachDataDependenciesTransformer: %s depends on %s", depender.ResourceAddr(), res) + depender.AttachResourceDependencies(res) } return nil @@ -304,37 +308,64 @@ func (m ReferenceMap) References(v dag.Vertex) []dag.Vertex { return matches } -// DependsOn returns the set of vertices that the given vertex refers to from +// dependsOn returns the set of vertices that the given vertex refers to from // the configured depends_on. -func (m ReferenceMap) DependsOn(v dag.Vertex) []dag.Vertex { - depender, ok := v.(graphNodeAttachResourceDependencies) - if !ok { - return nil - } - +func (m ReferenceMap) dependsOn(g *Graph, depender graphNodeDependsOn) []dag.Vertex { var matches []dag.Vertex + refs := depender.DependsOn() - for _, ref := range depender.DependsOn() { + for _, ref := range refs { subject := ref.Subject - key := m.referenceMapKey(v, subject) + key := m.referenceMapKey(depender, subject) vertices, ok := m[key] if !ok { - log.Printf("[WARN] DependOn: reference not found: %q", subject) + log.Printf("[WARN] DependsOn: reference not found: %q", subject) continue } for _, rv := range vertices { // don't include self-references - if rv == v { + if rv == depender { continue } matches = append(matches, rv) } } + matches = append(matches, m.parentModuleDependsOn(g, depender)...) + return matches } +// parentModuleDependsOn returns the set of vertices that a data sources parent +// module references through the module call's depends_on. +func (n ReferenceMap) parentModuleDependsOn(g *Graph, depender graphNodeDependsOn) []dag.Vertex { + var res []dag.Vertex + + // look for containing modules with DependsOn + for _, v := range g.DownEdges(depender) { + mod, ok := v.(*nodeExpandModule) + if !ok { + continue + } + + for _, dep := range n.dependsOn(g, mod) { + // add the dependency + res = append(res, dep) + // and check any ancestors + ans, _ := g.Ancestors(dep) + for _, v := range ans { + res = append(res, v) + } + } + + // recurse up through parent modules + res = append(res, n.parentModuleDependsOn(g, mod)...) + } + + return res +} + func (m *ReferenceMap) mapKey(path addrs.Module, addr addrs.Referenceable) string { return fmt.Sprintf("%s|%s", path.String(), addr.String()) }