github.com/r3labs/terraform@v0.8.4/terraform/graph.go (about) 1 package terraform 2 3 import ( 4 "fmt" 5 "log" 6 "runtime/debug" 7 "strings" 8 "sync" 9 10 "github.com/hashicorp/terraform/dag" 11 ) 12 13 // RootModuleName is the name given to the root module implicitly. 14 const RootModuleName = "root" 15 16 // RootModulePath is the path for the root module. 17 var RootModulePath = []string{RootModuleName} 18 19 // Graph represents the graph that Terraform uses to represent resources 20 // and their dependencies. Each graph represents only one module, but it 21 // can contain further modules, which themselves have their own graph. 22 type Graph struct { 23 // Graph is the actual DAG. This is embedded so you can call the DAG 24 // methods directly. 25 dag.AcyclicGraph 26 27 // Path is the path in the module tree that this Graph represents. 28 // The root is represented by a single element list containing 29 // RootModuleName 30 Path []string 31 32 // annotations are the annotations that are added to vertices. Annotations 33 // are arbitrary metadata taht is used for various logic. Annotations 34 // should have unique keys that are referenced via constants. 35 annotations map[dag.Vertex]map[string]interface{} 36 37 // dependableMap is a lookaside table for fast lookups for connecting 38 // dependencies by their GraphNodeDependable value to avoid O(n^3)-like 39 // situations and turn them into O(1) with respect to the number of new 40 // edges. 41 dependableMap map[string]dag.Vertex 42 43 // debugName is a name for reference in the debug output. This is usually 44 // to indicate what topmost builder was, and if this graph is a shadow or 45 // not. 46 debugName string 47 48 once sync.Once 49 } 50 51 func (g *Graph) DirectedGraph() dag.Grapher { 52 return &g.AcyclicGraph 53 } 54 55 // Annotations returns the annotations that are configured for the 56 // given vertex. The map is guaranteed to be non-nil but may be empty. 57 // 58 // The returned map may be modified to modify the annotations of the 59 // vertex. 60 func (g *Graph) Annotations(v dag.Vertex) map[string]interface{} { 61 g.once.Do(g.init) 62 63 // If this vertex isn't in the graph, then just return an empty map 64 if !g.HasVertex(v) { 65 return map[string]interface{}{} 66 } 67 68 // Get the map, if it doesn't exist yet then initialize it 69 m, ok := g.annotations[v] 70 if !ok { 71 m = make(map[string]interface{}) 72 g.annotations[v] = m 73 } 74 75 return m 76 } 77 78 // Add is the same as dag.Graph.Add. 79 func (g *Graph) Add(v dag.Vertex) dag.Vertex { 80 g.once.Do(g.init) 81 82 // Call upwards to add it to the actual graph 83 g.Graph.Add(v) 84 85 // If this is a depend-able node, then store the lookaside info 86 if dv, ok := v.(GraphNodeDependable); ok { 87 for _, n := range dv.DependableName() { 88 g.dependableMap[n] = v 89 } 90 } 91 92 // If this initializes annotations, then do that 93 if av, ok := v.(GraphNodeAnnotationInit); ok { 94 as := g.Annotations(v) 95 for k, v := range av.AnnotationInit() { 96 as[k] = v 97 } 98 } 99 100 return v 101 } 102 103 // Remove is the same as dag.Graph.Remove 104 func (g *Graph) Remove(v dag.Vertex) dag.Vertex { 105 g.once.Do(g.init) 106 107 // If this is a depend-able node, then remove the lookaside info 108 if dv, ok := v.(GraphNodeDependable); ok { 109 for _, n := range dv.DependableName() { 110 delete(g.dependableMap, n) 111 } 112 } 113 114 // Remove the annotations 115 delete(g.annotations, v) 116 117 // Call upwards to remove it from the actual graph 118 return g.Graph.Remove(v) 119 } 120 121 // Replace is the same as dag.Graph.Replace 122 func (g *Graph) Replace(o, n dag.Vertex) bool { 123 g.once.Do(g.init) 124 125 // Go through and update our lookaside to point to the new vertex 126 for k, v := range g.dependableMap { 127 if v == o { 128 if _, ok := n.(GraphNodeDependable); ok { 129 g.dependableMap[k] = n 130 } else { 131 delete(g.dependableMap, k) 132 } 133 } 134 } 135 136 // Move the annotation if it exists 137 if m, ok := g.annotations[o]; ok { 138 g.annotations[n] = m 139 delete(g.annotations, o) 140 } 141 142 return g.Graph.Replace(o, n) 143 } 144 145 // ConnectDependent connects a GraphNodeDependent to all of its 146 // GraphNodeDependables. It returns the list of dependents it was 147 // unable to connect to. 148 func (g *Graph) ConnectDependent(raw dag.Vertex) []string { 149 v, ok := raw.(GraphNodeDependent) 150 if !ok { 151 return nil 152 } 153 154 return g.ConnectTo(v, v.DependentOn()) 155 } 156 157 // ConnectDependents goes through the graph, connecting all the 158 // GraphNodeDependents to GraphNodeDependables. This is safe to call 159 // multiple times. 160 // 161 // To get details on whether dependencies could be found/made, the more 162 // specific ConnectDependent should be used. 163 func (g *Graph) ConnectDependents() { 164 for _, v := range g.Vertices() { 165 if dv, ok := v.(GraphNodeDependent); ok { 166 g.ConnectDependent(dv) 167 } 168 } 169 } 170 171 // ConnectFrom creates an edge by finding the source from a DependableName 172 // and connecting it to the specific vertex. 173 func (g *Graph) ConnectFrom(source string, target dag.Vertex) { 174 g.once.Do(g.init) 175 176 if source := g.dependableMap[source]; source != nil { 177 g.Connect(dag.BasicEdge(source, target)) 178 } 179 } 180 181 // ConnectTo connects a vertex to a raw string of targets that are the 182 // result of DependableName, and returns the list of targets that are missing. 183 func (g *Graph) ConnectTo(v dag.Vertex, targets []string) []string { 184 g.once.Do(g.init) 185 186 var missing []string 187 for _, t := range targets { 188 if dest := g.dependableMap[t]; dest != nil { 189 g.Connect(dag.BasicEdge(v, dest)) 190 } else { 191 missing = append(missing, t) 192 } 193 } 194 195 return missing 196 } 197 198 // Dependable finds the vertices in the graph that have the given dependable 199 // names and returns them. 200 func (g *Graph) Dependable(n string) dag.Vertex { 201 // TODO: do we need this? 202 return nil 203 } 204 205 // Walk walks the graph with the given walker for callbacks. The graph 206 // will be walked with full parallelism, so the walker should expect 207 // to be called in concurrently. 208 func (g *Graph) Walk(walker GraphWalker) error { 209 return g.walk(walker) 210 } 211 212 func (g *Graph) init() { 213 if g.annotations == nil { 214 g.annotations = make(map[dag.Vertex]map[string]interface{}) 215 } 216 217 if g.dependableMap == nil { 218 g.dependableMap = make(map[string]dag.Vertex) 219 } 220 } 221 222 func (g *Graph) walk(walker GraphWalker) error { 223 // The callbacks for enter/exiting a graph 224 ctx := walker.EnterPath(g.Path) 225 defer walker.ExitPath(g.Path) 226 227 // Get the path for logs 228 path := strings.Join(ctx.Path(), ".") 229 230 // Determine if our walker is a panic wrapper 231 panicwrap, ok := walker.(GraphWalkerPanicwrapper) 232 if !ok { 233 panicwrap = nil // just to be sure 234 } 235 236 debugName := "walk-graph.json" 237 if g.debugName != "" { 238 debugName = g.debugName + "-" + debugName 239 } 240 241 debugBuf := dbug.NewFileWriter(debugName) 242 g.SetDebugWriter(debugBuf) 243 defer debugBuf.Close() 244 245 // Walk the graph. 246 var walkFn dag.WalkFunc 247 walkFn = func(v dag.Vertex) (rerr error) { 248 log.Printf("[DEBUG] vertex '%s.%s': walking", path, dag.VertexName(v)) 249 g.DebugVisitInfo(v, g.debugName) 250 251 // If we have a panic wrap GraphWalker and a panic occurs, recover 252 // and call that. We ensure the return value is an error, however, 253 // so that future nodes are not called. 254 defer func() { 255 // If no panicwrap, do nothing 256 if panicwrap == nil { 257 return 258 } 259 260 // If no panic, do nothing 261 err := recover() 262 if err == nil { 263 return 264 } 265 266 // Modify the return value to show the error 267 rerr = fmt.Errorf("vertex %q captured panic: %s\n\n%s", 268 dag.VertexName(v), err, debug.Stack()) 269 270 // Call the panic wrapper 271 panicwrap.Panic(v, err) 272 }() 273 274 walker.EnterVertex(v) 275 defer walker.ExitVertex(v, rerr) 276 277 // vertexCtx is the context that we use when evaluating. This 278 // is normally the context of our graph but can be overridden 279 // with a GraphNodeSubPath impl. 280 vertexCtx := ctx 281 if pn, ok := v.(GraphNodeSubPath); ok && len(pn.Path()) > 0 { 282 vertexCtx = walker.EnterPath(normalizeModulePath(pn.Path())) 283 defer walker.ExitPath(pn.Path()) 284 } 285 286 // If the node is eval-able, then evaluate it. 287 if ev, ok := v.(GraphNodeEvalable); ok { 288 tree := ev.EvalTree() 289 if tree == nil { 290 panic(fmt.Sprintf( 291 "%s.%s (%T): nil eval tree", path, dag.VertexName(v), v)) 292 } 293 294 // Allow the walker to change our tree if needed. Eval, 295 // then callback with the output. 296 log.Printf("[DEBUG] vertex '%s.%s': evaluating", path, dag.VertexName(v)) 297 298 g.DebugVertexInfo(v, fmt.Sprintf("evaluating %T(%s)", v, path)) 299 300 tree = walker.EnterEvalTree(v, tree) 301 output, err := Eval(tree, vertexCtx) 302 if rerr = walker.ExitEvalTree(v, output, err); rerr != nil { 303 return 304 } 305 } 306 307 // If the node is dynamically expanded, then expand it 308 if ev, ok := v.(GraphNodeDynamicExpandable); ok { 309 log.Printf( 310 "[DEBUG] vertex '%s.%s': expanding/walking dynamic subgraph", 311 path, 312 dag.VertexName(v)) 313 314 g.DebugVertexInfo(v, fmt.Sprintf("expanding %T(%s)", v, path)) 315 316 g, err := ev.DynamicExpand(vertexCtx) 317 if err != nil { 318 rerr = err 319 return 320 } 321 if g != nil { 322 // Walk the subgraph 323 if rerr = g.walk(walker); rerr != nil { 324 return 325 } 326 } 327 } 328 329 // If the node has a subgraph, then walk the subgraph 330 if sn, ok := v.(GraphNodeSubgraph); ok { 331 log.Printf( 332 "[DEBUG] vertex '%s.%s': walking subgraph", 333 path, 334 dag.VertexName(v)) 335 336 g.DebugVertexInfo(v, fmt.Sprintf("subgraph: %T(%s)", v, path)) 337 338 if rerr = sn.Subgraph().(*Graph).walk(walker); rerr != nil { 339 return 340 } 341 } 342 343 return nil 344 } 345 346 return g.AcyclicGraph.Walk(walkFn) 347 } 348 349 // GraphNodeAnnotationInit is an interface that allows a node to 350 // initialize it's annotations. 351 // 352 // AnnotationInit will be called _once_ when the node is added to a 353 // graph for the first time and is expected to return it's initial 354 // annotations. 355 type GraphNodeAnnotationInit interface { 356 AnnotationInit() map[string]interface{} 357 } 358 359 // GraphNodeDependable is an interface which says that a node can be 360 // depended on (an edge can be placed between this node and another) according 361 // to the well-known name returned by DependableName. 362 // 363 // DependableName can return multiple names it is known by. 364 type GraphNodeDependable interface { 365 DependableName() []string 366 } 367 368 // GraphNodeDependent is an interface which says that a node depends 369 // on another GraphNodeDependable by some name. By implementing this 370 // interface, Graph.ConnectDependents() can be called multiple times 371 // safely and efficiently. 372 type GraphNodeDependent interface { 373 DependentOn() []string 374 }