github.com/graywolf-at-work-2/terraform-vendor@v1.4.5/internal/terraform/graph.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 "strings" 7 8 "github.com/hashicorp/terraform/internal/logging" 9 "github.com/hashicorp/terraform/internal/tfdiags" 10 11 "github.com/hashicorp/terraform/internal/addrs" 12 13 "github.com/hashicorp/terraform/internal/dag" 14 ) 15 16 // Graph represents the graph that Terraform uses to represent resources 17 // and their dependencies. 18 type Graph struct { 19 // Graph is the actual DAG. This is embedded so you can call the DAG 20 // methods directly. 21 dag.AcyclicGraph 22 23 // Path is the path in the module tree that this Graph represents. 24 Path addrs.ModuleInstance 25 } 26 27 func (g *Graph) DirectedGraph() dag.Grapher { 28 return &g.AcyclicGraph 29 } 30 31 // Walk walks the graph with the given walker for callbacks. The graph 32 // will be walked with full parallelism, so the walker should expect 33 // to be called in concurrently. 34 func (g *Graph) Walk(walker GraphWalker) tfdiags.Diagnostics { 35 return g.walk(walker) 36 } 37 38 func (g *Graph) walk(walker GraphWalker) tfdiags.Diagnostics { 39 // The callbacks for enter/exiting a graph 40 ctx := walker.EvalContext() 41 42 // Walk the graph. 43 walkFn := func(v dag.Vertex) (diags tfdiags.Diagnostics) { 44 // the walkFn is called asynchronously, and needs to be recovered 45 // separately in the case of a panic. 46 defer logging.PanicHandler() 47 48 log.Printf("[TRACE] vertex %q: starting visit (%T)", dag.VertexName(v), v) 49 50 defer func() { 51 if diags.HasErrors() { 52 for _, diag := range diags { 53 if diag.Severity() == tfdiags.Error { 54 desc := diag.Description() 55 log.Printf("[ERROR] vertex %q error: %s", dag.VertexName(v), desc.Summary) 56 } 57 } 58 log.Printf("[TRACE] vertex %q: visit complete, with errors", dag.VertexName(v)) 59 } else { 60 log.Printf("[TRACE] vertex %q: visit complete", dag.VertexName(v)) 61 } 62 }() 63 64 // vertexCtx is the context that we use when evaluating. This 65 // is normally the context of our graph but can be overridden 66 // with a GraphNodeModuleInstance impl. 67 vertexCtx := ctx 68 if pn, ok := v.(GraphNodeModuleInstance); ok { 69 vertexCtx = walker.EnterPath(pn.Path()) 70 defer walker.ExitPath(pn.Path()) 71 } 72 73 // If the node is exec-able, then execute it. 74 if ev, ok := v.(GraphNodeExecutable); ok { 75 diags = diags.Append(walker.Execute(vertexCtx, ev)) 76 if diags.HasErrors() { 77 return 78 } 79 } 80 81 // If the node is dynamically expanded, then expand it 82 if ev, ok := v.(GraphNodeDynamicExpandable); ok { 83 log.Printf("[TRACE] vertex %q: expanding dynamic subgraph", dag.VertexName(v)) 84 85 g, err := ev.DynamicExpand(vertexCtx) 86 diags = diags.Append(err) 87 if diags.HasErrors() { 88 log.Printf("[TRACE] vertex %q: failed expanding dynamic subgraph: %s", dag.VertexName(v), err) 89 return 90 } 91 if g != nil { 92 // The subgraph should always be valid, per our normal acyclic 93 // graph validation rules. 94 if err := g.Validate(); err != nil { 95 diags = diags.Append(tfdiags.Sourceless( 96 tfdiags.Error, 97 "Graph node has invalid dynamic subgraph", 98 fmt.Sprintf("The internal logic for %q generated an invalid dynamic subgraph: %s.\n\nThis is a bug in Terraform. Please report it!", dag.VertexName(v), err), 99 )) 100 return 101 } 102 // If we passed validation then there is exactly one root node. 103 // That root node should always be "rootNode", the singleton 104 // root node value. 105 if n, err := g.Root(); err != nil || n != dag.Vertex(rootNode) { 106 diags = diags.Append(tfdiags.Sourceless( 107 tfdiags.Error, 108 "Graph node has invalid dynamic subgraph", 109 fmt.Sprintf("The internal logic for %q generated an invalid dynamic subgraph: the root node is %T, which is not a suitable root node type.\n\nThis is a bug in Terraform. Please report it!", dag.VertexName(v), n), 110 )) 111 return 112 } 113 114 // Walk the subgraph 115 log.Printf("[TRACE] vertex %q: entering dynamic subgraph", dag.VertexName(v)) 116 subDiags := g.walk(walker) 117 diags = diags.Append(subDiags) 118 if subDiags.HasErrors() { 119 var errs []string 120 for _, d := range subDiags { 121 errs = append(errs, d.Description().Summary) 122 } 123 log.Printf("[TRACE] vertex %q: dynamic subgraph encountered errors: %s", dag.VertexName(v), strings.Join(errs, ",")) 124 return 125 } 126 log.Printf("[TRACE] vertex %q: dynamic subgraph completed successfully", dag.VertexName(v)) 127 } else { 128 log.Printf("[TRACE] vertex %q: produced no dynamic subgraph", dag.VertexName(v)) 129 } 130 } 131 return 132 } 133 134 return g.AcyclicGraph.Walk(walkFn) 135 }