github.com/hashicorp/terraform-plugin-sdk@v1.17.2/terraform/graph.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 7 "github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags" 8 9 "github.com/hashicorp/terraform-plugin-sdk/internal/addrs" 10 11 "github.com/hashicorp/terraform-plugin-sdk/internal/dag" 12 ) 13 14 // Graph represents the graph that Terraform uses to represent resources 15 // and their dependencies. 16 type Graph struct { 17 // Graph is the actual DAG. This is embedded so you can call the DAG 18 // methods directly. 19 dag.AcyclicGraph 20 21 // Path is the path in the module tree that this Graph represents. 22 Path addrs.ModuleInstance 23 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 26 // not. 27 debugName string 28 } 29 30 func (g *Graph) DirectedGraph() dag.Grapher { 31 return &g.AcyclicGraph 32 } 33 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 { 38 return g.walk(walker) 39 } 40 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) 45 46 // Get the path for logs 47 path := ctx.Path().String() 48 49 debugName := "walk-graph.json" 50 if g.debugName != "" { 51 debugName = g.debugName + "-" + debugName 52 } 53 54 // Walk the graph. 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) 59 60 defer func() { 61 log.Printf("[TRACE] vertex %q: visit complete", dag.VertexName(v)) 62 }() 63 64 walker.EnterVertex(v) 65 defer walker.ExitVertex(v, diags) 66 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. 70 vertexCtx := ctx 71 if pn, ok := v.(GraphNodeSubPath); ok && len(pn.Path()) > 0 { 72 vertexCtx = walker.EnterPath(pn.Path()) 73 defer walker.ExitPath(pn.Path()) 74 } 75 76 // If the node is eval-able, then evaluate it. 77 if ev, ok := v.(GraphNodeEvalable); ok { 78 tree := ev.EvalTree() 79 if tree == nil { 80 panic(fmt.Sprintf("%q (%T): nil eval tree", dag.VertexName(v), v)) 81 } 82 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)) 86 87 g.DebugVertexInfo(v, fmt.Sprintf("evaluating %T(%s)", v, path)) 88 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() { 93 return 94 } 95 } 96 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)) 100 101 g.DebugVertexInfo(v, fmt.Sprintf("expanding %T(%s)", v, path)) 102 103 g, err := ev.DynamicExpand(vertexCtx) 104 if err != nil { 105 diags = diags.Append(err) 106 return 107 } 108 if g != nil { 109 // Walk the subgraph 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)) 115 return 116 } 117 log.Printf("[TRACE] vertex %q: dynamic subgraph completed successfully", dag.VertexName(v)) 118 } else { 119 log.Printf("[TRACE] vertex %q: produced no dynamic subgraph", dag.VertexName(v)) 120 } 121 } 122 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)) 126 127 g.DebugVertexInfo(v, fmt.Sprintf("subgraph: %T(%s)", v, path)) 128 129 subDiags := sn.Subgraph().(*Graph).walk(walker) 130 if subDiags.HasErrors() { 131 log.Printf("[TRACE] vertex %q: static subgraph encountered errors", dag.VertexName(v)) 132 return 133 } 134 log.Printf("[TRACE] vertex %q: static subgraph completed successfully", dag.VertexName(v)) 135 } 136 137 return 138 } 139 140 return g.AcyclicGraph.Walk(walkFn) 141 }