diff --git a/terraform/graph.go b/terraform/graph.go index cf5439097..a86e0ca1e 100644 --- a/terraform/graph.go +++ b/terraform/graph.go @@ -144,8 +144,8 @@ func (g *Graph) init() { func (g *Graph) walk(walker GraphWalker) error { // The callbacks for enter/exiting a graph - ctx := walker.EnterGraph(g) - defer walker.ExitGraph(g) + ctx := walker.EnterPath(g.Path) + defer walker.ExitPath(g.Path) // Get the path for logs path := strings.Join(ctx.Path(), ".") @@ -158,6 +158,15 @@ func (g *Graph) walk(walker GraphWalker) error { walker.EnterVertex(v) defer func() { walker.ExitVertex(v, rerr) }() + // vertexCtx is the context that we use when evaluating. This + // is normally the context of our graph but can be overridden + // with a GraphNodeSubPath impl. + vertexCtx := ctx + if pn, ok := v.(GraphNodeSubPath); ok { + vertexCtx = walker.EnterPath(pn.Path()) + defer walker.ExitPath(pn.Path()) + } + // If the node is eval-able, then evaluate it. if ev, ok := v.(GraphNodeEvalable); ok { tree := ev.EvalTree() @@ -170,7 +179,7 @@ func (g *Graph) walk(walker GraphWalker) error { // then callback with the output. log.Printf("[DEBUG] vertex %s.%s: evaluating", path, dag.VertexName(v)) tree = walker.EnterEvalTree(v, tree) - output, err := Eval(tree, ctx) + output, err := Eval(tree, vertexCtx) if rerr = walker.ExitEvalTree(v, output, err); rerr != nil { return } @@ -182,7 +191,7 @@ func (g *Graph) walk(walker GraphWalker) error { "[DEBUG] vertex %s.%s: expanding/walking dynamic subgraph", path, dag.VertexName(v)) - g, err := ev.DynamicExpand(ctx) + g, err := ev.DynamicExpand(vertexCtx) if err != nil { rerr = err return diff --git a/terraform/graph_config_node_module.go b/terraform/graph_config_node_module.go index 15fa29ace..28cca7749 100644 --- a/terraform/graph_config_node_module.go +++ b/terraform/graph_config_node_module.go @@ -227,7 +227,7 @@ func (n *graphNodeModuleExpanded) FlattenGraph() *Graph { graph.Replace(v, &graphNodeModuleFlatWrap{ graphNodeModuleWrappable: wn, - Path: graph.Path, + PathValue: graph.Path, NamePrefix: pathStr, DependentOnPrefix: depPrefix, }) @@ -259,7 +259,7 @@ type graphNodeModuleWrappable interface { type graphNodeModuleFlatWrap struct { graphNodeModuleWrappable - Path []string + PathValue []string NamePrefix string DependentOnPrefix string } @@ -268,6 +268,11 @@ func (n *graphNodeModuleFlatWrap) Name() string { return fmt.Sprintf("%s.%s", n.NamePrefix, n.graphNodeModuleWrappable.Name()) } +// GraphNodeSubPath impl. +func (n *graphNodeModuleFlatWrap) Path() []string { + return n.PathValue +} + func (n *graphNodeModuleFlatWrap) DependableName() []string { result := n.graphNodeModuleWrappable.DependableName() n.prefixList(result, n.NamePrefix) diff --git a/terraform/graph_interface_subgraph.go b/terraform/graph_interface_subgraph.go new file mode 100644 index 000000000..2897eb546 --- /dev/null +++ b/terraform/graph_interface_subgraph.go @@ -0,0 +1,7 @@ +package terraform + +// GraphNodeSubPath says that a node is part of a graph with a +// different path, and the context should be adjusted accordingly. +type GraphNodeSubPath interface { + Path() []string +} diff --git a/terraform/graph_walk.go b/terraform/graph_walk.go index f5da6c093..ef3a4f6f5 100644 --- a/terraform/graph_walk.go +++ b/terraform/graph_walk.go @@ -7,8 +7,8 @@ import ( // GraphWalker is an interface that can be implemented that when used // with Graph.Walk will invoke the given callbacks under certain events. type GraphWalker interface { - EnterGraph(*Graph) EvalContext - ExitGraph(*Graph) + EnterPath([]string) EvalContext + ExitPath([]string) EnterVertex(dag.Vertex) ExitVertex(dag.Vertex, error) EnterEvalTree(dag.Vertex, EvalNode) EvalNode @@ -20,8 +20,8 @@ type GraphWalker interface { // implementing all the required functions. type NullGraphWalker struct{} -func (NullGraphWalker) EnterGraph(*Graph) EvalContext { return nil } -func (NullGraphWalker) ExitGraph(*Graph) {} +func (NullGraphWalker) EnterPath([]string) EvalContext { return nil } +func (NullGraphWalker) ExitPath([]string) {} func (NullGraphWalker) EnterVertex(dag.Vertex) {} func (NullGraphWalker) ExitVertex(dag.Vertex, error) {} func (NullGraphWalker) EnterEvalTree(v dag.Vertex, n EvalNode) EvalNode { return n } diff --git a/terraform/graph_walk_context.go b/terraform/graph_walk_context.go index 64a9f3d02..508a8edc3 100644 --- a/terraform/graph_walk_context.go +++ b/terraform/graph_walk_context.go @@ -33,14 +33,14 @@ type ContextGraphWalker struct { provisionerLock sync.Mutex } -func (w *ContextGraphWalker) EnterGraph(g *Graph) EvalContext { +func (w *ContextGraphWalker) EnterPath(path []string) EvalContext { w.once.Do(w.init) w.contextLock.Lock() defer w.contextLock.Unlock() // If we already have a context for this path cached, use that - key := PathCacheKey(g.Path) + key := PathCacheKey(path) if ctx, ok := w.contexts[key]; ok { return ctx } @@ -49,13 +49,13 @@ func (w *ContextGraphWalker) EnterGraph(g *Graph) EvalContext { // to the root module. As we enter subgraphs, we don't want to set // variables, which is set by the SetVariables EvalContext function. variables := w.Context.variables - if len(g.Path) > 1 { + if len(path) > 1 { // We're in a submodule, the variables should be empty variables = make(map[string]string) } ctx := &BuiltinEvalContext{ - PathValue: g.Path, + PathValue: path, Hooks: w.Context.hooks, InputValue: w.Context.uiInput, Providers: w.Context.providers,