7 "github.com/hashicorp/terraform/tfdiags"
9 "github.com/hashicorp/terraform/addrs"
11 "github.com/hashicorp/terraform/dag"
14 // Graph represents the graph that Terraform uses to represent resources
15 // and their dependencies.
17 // Graph is the actual DAG. This is embedded so you can call the DAG
21 // Path is the path in the module tree that this Graph represents.
22 Path addrs.ModuleInstance
24 // debugName is a name for reference in the debug output. This is usually
25 // to indicate what topmost builder was, and if this graph is a shadow or
30 func (g *Graph) DirectedGraph() dag.Grapher {
31 return &g.AcyclicGraph
34 // Walk walks the graph with the given walker for callbacks. The graph
35 // will be walked with full parallelism, so the walker should expect
36 // to be called in concurrently.
37 func (g *Graph) Walk(walker GraphWalker) tfdiags.Diagnostics {
41 func (g *Graph) walk(walker GraphWalker) tfdiags.Diagnostics {
42 // The callbacks for enter/exiting a graph
43 ctx := walker.EnterPath(g.Path)
44 defer walker.ExitPath(g.Path)
46 // Get the path for logs
47 path := ctx.Path().String()
49 debugName := "walk-graph.json"
50 if g.debugName != "" {
51 debugName = g.debugName + "-" + debugName
55 var walkFn dag.WalkFunc
56 walkFn = func(v dag.Vertex) (diags tfdiags.Diagnostics) {
57 log.Printf("[TRACE] vertex %q: starting visit (%T)", dag.VertexName(v), v)
58 g.DebugVisitInfo(v, g.debugName)
61 log.Printf("[TRACE] vertex %q: visit complete", dag.VertexName(v))
65 defer walker.ExitVertex(v, diags)
67 // vertexCtx is the context that we use when evaluating. This
68 // is normally the context of our graph but can be overridden
69 // with a GraphNodeSubPath impl.
71 if pn, ok := v.(GraphNodeSubPath); ok && len(pn.Path()) > 0 {
72 vertexCtx = walker.EnterPath(pn.Path())
73 defer walker.ExitPath(pn.Path())
76 // If the node is eval-able, then evaluate it.
77 if ev, ok := v.(GraphNodeEvalable); ok {
80 panic(fmt.Sprintf("%q (%T): nil eval tree", dag.VertexName(v), v))
83 // Allow the walker to change our tree if needed. Eval,
84 // then callback with the output.
85 log.Printf("[TRACE] vertex %q: evaluating", dag.VertexName(v))
87 g.DebugVertexInfo(v, fmt.Sprintf("evaluating %T(%s)", v, path))
89 tree = walker.EnterEvalTree(v, tree)
90 output, err := Eval(tree, vertexCtx)
91 diags = diags.Append(walker.ExitEvalTree(v, output, err))
92 if diags.HasErrors() {
97 // If the node is dynamically expanded, then expand it
98 if ev, ok := v.(GraphNodeDynamicExpandable); ok {
99 log.Printf("[TRACE] vertex %q: expanding dynamic subgraph", dag.VertexName(v))
101 g.DebugVertexInfo(v, fmt.Sprintf("expanding %T(%s)", v, path))
103 g, err := ev.DynamicExpand(vertexCtx)
105 diags = diags.Append(err)
110 log.Printf("[TRACE] vertex %q: entering dynamic subgraph", dag.VertexName(v))
111 subDiags := g.walk(walker)
112 diags = diags.Append(subDiags)
113 if subDiags.HasErrors() {
114 log.Printf("[TRACE] vertex %q: dynamic subgraph encountered errors", dag.VertexName(v))
117 log.Printf("[TRACE] vertex %q: dynamic subgraph completed successfully", dag.VertexName(v))
119 log.Printf("[TRACE] vertex %q: produced no dynamic subgraph", dag.VertexName(v))
123 // If the node has a subgraph, then walk the subgraph
124 if sn, ok := v.(GraphNodeSubgraph); ok {
125 log.Printf("[TRACE] vertex %q: entering static subgraph", dag.VertexName(v))
127 g.DebugVertexInfo(v, fmt.Sprintf("subgraph: %T(%s)", v, path))
129 subDiags := sn.Subgraph().(*Graph).walk(walker)
130 if subDiags.HasErrors() {
131 log.Printf("[TRACE] vertex %q: static subgraph encountered errors", dag.VertexName(v))
134 log.Printf("[TRACE] vertex %q: static subgraph completed successfully", dag.VertexName(v))
140 return g.AcyclicGraph.Walk(walkFn)