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