github.com/jaredpalmer/terraform@v1.1.0-alpha20210908.0.20210911170307-88705c943a03/internal/terraform/graph.go (about) 1 package terraform 2 3 import ( 4 "log" 5 "strings" 6 7 "github.com/hashicorp/terraform/internal/tfdiags" 8 9 "github.com/hashicorp/terraform/internal/addrs" 10 11 "github.com/hashicorp/terraform/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 25 func (g *Graph) DirectedGraph() dag.Grapher { 26 return &g.AcyclicGraph 27 } 28 29 // Walk walks the graph with the given walker for callbacks. The graph 30 // will be walked with full parallelism, so the walker should expect 31 // to be called in concurrently. 32 func (g *Graph) Walk(walker GraphWalker) tfdiags.Diagnostics { 33 return g.walk(walker) 34 } 35 36 func (g *Graph) walk(walker GraphWalker) tfdiags.Diagnostics { 37 // The callbacks for enter/exiting a graph 38 ctx := walker.EvalContext() 39 40 // Walk the graph. 41 walkFn := func(v dag.Vertex) (diags tfdiags.Diagnostics) { 42 log.Printf("[TRACE] vertex %q: starting visit (%T)", dag.VertexName(v), v) 43 44 defer func() { 45 if diags.HasErrors() { 46 for _, diag := range diags { 47 if diag.Severity() == tfdiags.Error { 48 desc := diag.Description() 49 log.Printf("[ERROR] vertex %q error: %s", dag.VertexName(v), desc.Summary) 50 } 51 } 52 log.Printf("[TRACE] vertex %q: visit complete, with errors", dag.VertexName(v)) 53 } else { 54 log.Printf("[TRACE] vertex %q: visit complete", dag.VertexName(v)) 55 } 56 }() 57 58 // vertexCtx is the context that we use when evaluating. This 59 // is normally the context of our graph but can be overridden 60 // with a GraphNodeModuleInstance impl. 61 vertexCtx := ctx 62 if pn, ok := v.(GraphNodeModuleInstance); ok { 63 vertexCtx = walker.EnterPath(pn.Path()) 64 defer walker.ExitPath(pn.Path()) 65 } 66 67 // If the node is exec-able, then execute it. 68 if ev, ok := v.(GraphNodeExecutable); ok { 69 diags = diags.Append(walker.Execute(vertexCtx, ev)) 70 if diags.HasErrors() { 71 return 72 } 73 } 74 75 // If the node is dynamically expanded, then expand it 76 if ev, ok := v.(GraphNodeDynamicExpandable); ok { 77 log.Printf("[TRACE] vertex %q: expanding dynamic subgraph", dag.VertexName(v)) 78 79 g, err := ev.DynamicExpand(vertexCtx) 80 if err != nil { 81 diags = diags.Append(err) 82 return 83 } 84 if g != nil { 85 // Walk the subgraph 86 log.Printf("[TRACE] vertex %q: entering dynamic subgraph", dag.VertexName(v)) 87 subDiags := g.walk(walker) 88 diags = diags.Append(subDiags) 89 if subDiags.HasErrors() { 90 var errs []string 91 for _, d := range subDiags { 92 errs = append(errs, d.Description().Summary) 93 } 94 log.Printf("[TRACE] vertex %q: dynamic subgraph encountered errors: %s", dag.VertexName(v), strings.Join(errs, ",")) 95 return 96 } 97 log.Printf("[TRACE] vertex %q: dynamic subgraph completed successfully", dag.VertexName(v)) 98 } else { 99 log.Printf("[TRACE] vertex %q: produced no dynamic subgraph", dag.VertexName(v)) 100 } 101 } 102 return 103 } 104 105 return g.AcyclicGraph.Walk(walkFn) 106 }