github.com/vic3lord/terraform@v0.8.0-rc1.0.20170626102919-16c6dd2cb372/terraform/graph.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 "runtime/debug" 7 "strings" 8 9 "github.com/hashicorp/terraform/dag" 10 ) 11 12 // RootModuleName is the name given to the root module implicitly. 13 const RootModuleName = "root" 14 15 // RootModulePath is the path for the root module. 16 var RootModulePath = []string{RootModuleName} 17 18 // Graph represents the graph that Terraform uses to represent resources 19 // and their dependencies. 20 type Graph struct { 21 // Graph is the actual DAG. This is embedded so you can call the DAG 22 // methods directly. 23 dag.AcyclicGraph 24 25 // Path is the path in the module tree that this Graph represents. 26 // The root is represented by a single element list containing 27 // RootModuleName 28 Path []string 29 30 // debugName is a name for reference in the debug output. This is usually 31 // to indicate what topmost builder was, and if this graph is a shadow or 32 // not. 33 debugName string 34 } 35 36 func (g *Graph) DirectedGraph() dag.Grapher { 37 return &g.AcyclicGraph 38 } 39 40 // Walk walks the graph with the given walker for callbacks. The graph 41 // will be walked with full parallelism, so the walker should expect 42 // to be called in concurrently. 43 func (g *Graph) Walk(walker GraphWalker) error { 44 return g.walk(walker) 45 } 46 47 func (g *Graph) walk(walker GraphWalker) error { 48 // The callbacks for enter/exiting a graph 49 ctx := walker.EnterPath(g.Path) 50 defer walker.ExitPath(g.Path) 51 52 // Get the path for logs 53 path := strings.Join(ctx.Path(), ".") 54 55 // Determine if our walker is a panic wrapper 56 panicwrap, ok := walker.(GraphWalkerPanicwrapper) 57 if !ok { 58 panicwrap = nil // just to be sure 59 } 60 61 debugName := "walk-graph.json" 62 if g.debugName != "" { 63 debugName = g.debugName + "-" + debugName 64 } 65 66 debugBuf := dbug.NewFileWriter(debugName) 67 g.SetDebugWriter(debugBuf) 68 defer debugBuf.Close() 69 70 // Walk the graph. 71 var walkFn dag.WalkFunc 72 walkFn = func(v dag.Vertex) (rerr error) { 73 log.Printf("[DEBUG] vertex '%s.%s': walking", path, dag.VertexName(v)) 74 g.DebugVisitInfo(v, g.debugName) 75 76 // If we have a panic wrap GraphWalker and a panic occurs, recover 77 // and call that. We ensure the return value is an error, however, 78 // so that future nodes are not called. 79 defer func() { 80 // If no panicwrap, do nothing 81 if panicwrap == nil { 82 return 83 } 84 85 // If no panic, do nothing 86 err := recover() 87 if err == nil { 88 return 89 } 90 91 // Modify the return value to show the error 92 rerr = fmt.Errorf("vertex %q captured panic: %s\n\n%s", 93 dag.VertexName(v), err, debug.Stack()) 94 95 // Call the panic wrapper 96 panicwrap.Panic(v, err) 97 }() 98 99 walker.EnterVertex(v) 100 defer walker.ExitVertex(v, rerr) 101 102 // vertexCtx is the context that we use when evaluating. This 103 // is normally the context of our graph but can be overridden 104 // with a GraphNodeSubPath impl. 105 vertexCtx := ctx 106 if pn, ok := v.(GraphNodeSubPath); ok && len(pn.Path()) > 0 { 107 vertexCtx = walker.EnterPath(normalizeModulePath(pn.Path())) 108 defer walker.ExitPath(pn.Path()) 109 } 110 111 // If the node is eval-able, then evaluate it. 112 if ev, ok := v.(GraphNodeEvalable); ok { 113 tree := ev.EvalTree() 114 if tree == nil { 115 panic(fmt.Sprintf( 116 "%s.%s (%T): nil eval tree", path, dag.VertexName(v), v)) 117 } 118 119 // Allow the walker to change our tree if needed. Eval, 120 // then callback with the output. 121 log.Printf("[DEBUG] vertex '%s.%s': evaluating", path, dag.VertexName(v)) 122 123 g.DebugVertexInfo(v, fmt.Sprintf("evaluating %T(%s)", v, path)) 124 125 tree = walker.EnterEvalTree(v, tree) 126 output, err := Eval(tree, vertexCtx) 127 if rerr = walker.ExitEvalTree(v, output, err); rerr != nil { 128 return 129 } 130 } 131 132 // If the node is dynamically expanded, then expand it 133 if ev, ok := v.(GraphNodeDynamicExpandable); ok { 134 log.Printf( 135 "[DEBUG] vertex '%s.%s': expanding/walking dynamic subgraph", 136 path, 137 dag.VertexName(v)) 138 139 g.DebugVertexInfo(v, fmt.Sprintf("expanding %T(%s)", v, path)) 140 141 g, err := ev.DynamicExpand(vertexCtx) 142 if err != nil { 143 rerr = err 144 return 145 } 146 if g != nil { 147 // Walk the subgraph 148 if rerr = g.walk(walker); rerr != nil { 149 return 150 } 151 } 152 } 153 154 // If the node has a subgraph, then walk the subgraph 155 if sn, ok := v.(GraphNodeSubgraph); ok { 156 log.Printf( 157 "[DEBUG] vertex '%s.%s': walking subgraph", 158 path, 159 dag.VertexName(v)) 160 161 g.DebugVertexInfo(v, fmt.Sprintf("subgraph: %T(%s)", v, path)) 162 163 if rerr = sn.Subgraph().(*Graph).walk(walker); rerr != nil { 164 return 165 } 166 } 167 168 return nil 169 } 170 171 return g.AcyclicGraph.Walk(walkFn) 172 }