github.com/turtlemonvh/terraform@v0.6.9-0.20151204001754-8e40b6b855e8/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 // dependableMap is a lookaside table for fast lookups for connecting 32 // dependencies by their GraphNodeDependable value to avoid O(n^3)-like 33 // situations and turn them into O(1) with respect to the number of new 34 // edges. 35 dependableMap map[string]dag.Vertex 36 37 once sync.Once 38 } 39 40 // Add is the same as dag.Graph.Add. 41 func (g *Graph) Add(v dag.Vertex) dag.Vertex { 42 g.once.Do(g.init) 43 44 // Call upwards to add it to the actual graph 45 g.Graph.Add(v) 46 47 // If this is a depend-able node, then store the lookaside info 48 if dv, ok := v.(GraphNodeDependable); ok { 49 for _, n := range dv.DependableName() { 50 g.dependableMap[n] = v 51 } 52 } 53 54 return v 55 } 56 57 // Remove is the same as dag.Graph.Remove 58 func (g *Graph) Remove(v dag.Vertex) dag.Vertex { 59 g.once.Do(g.init) 60 61 // If this is a depend-able node, then remove the lookaside info 62 if dv, ok := v.(GraphNodeDependable); ok { 63 for _, n := range dv.DependableName() { 64 delete(g.dependableMap, n) 65 } 66 } 67 68 // Call upwards to remove it from the actual graph 69 return g.Graph.Remove(v) 70 } 71 72 // Replace is the same as dag.Graph.Replace 73 func (g *Graph) Replace(o, n dag.Vertex) bool { 74 // Go through and update our lookaside to point to the new vertex 75 for k, v := range g.dependableMap { 76 if v == o { 77 if _, ok := n.(GraphNodeDependable); ok { 78 g.dependableMap[k] = n 79 } else { 80 delete(g.dependableMap, k) 81 } 82 } 83 } 84 85 return g.Graph.Replace(o, n) 86 } 87 88 // ConnectDependent connects a GraphNodeDependent to all of its 89 // GraphNodeDependables. It returns the list of dependents it was 90 // unable to connect to. 91 func (g *Graph) ConnectDependent(raw dag.Vertex) []string { 92 v, ok := raw.(GraphNodeDependent) 93 if !ok { 94 return nil 95 } 96 97 return g.ConnectTo(v, v.DependentOn()) 98 } 99 100 // ConnectDependents goes through the graph, connecting all the 101 // GraphNodeDependents to GraphNodeDependables. This is safe to call 102 // multiple times. 103 // 104 // To get details on whether dependencies could be found/made, the more 105 // specific ConnectDependent should be used. 106 func (g *Graph) ConnectDependents() { 107 for _, v := range g.Vertices() { 108 if dv, ok := v.(GraphNodeDependent); ok { 109 g.ConnectDependent(dv) 110 } 111 } 112 } 113 114 // ConnectFrom creates an edge by finding the source from a DependableName 115 // and connecting it to the specific vertex. 116 func (g *Graph) ConnectFrom(source string, target dag.Vertex) { 117 g.once.Do(g.init) 118 119 if source := g.dependableMap[source]; source != nil { 120 g.Connect(dag.BasicEdge(source, target)) 121 } 122 } 123 124 // ConnectTo connects a vertex to a raw string of targets that are the 125 // result of DependableName, and returns the list of targets that are missing. 126 func (g *Graph) ConnectTo(v dag.Vertex, targets []string) []string { 127 g.once.Do(g.init) 128 129 var missing []string 130 for _, t := range targets { 131 if dest := g.dependableMap[t]; dest != nil { 132 g.Connect(dag.BasicEdge(v, dest)) 133 } else { 134 missing = append(missing, t) 135 } 136 } 137 138 return missing 139 } 140 141 // Dependable finds the vertices in the graph that have the given dependable 142 // names and returns them. 143 func (g *Graph) Dependable(n string) dag.Vertex { 144 // TODO: do we need this? 145 return nil 146 } 147 148 // Walk walks the graph with the given walker for callbacks. The graph 149 // will be walked with full parallelism, so the walker should expect 150 // to be called in concurrently. 151 func (g *Graph) Walk(walker GraphWalker) error { 152 return g.walk(walker) 153 } 154 155 func (g *Graph) init() { 156 if g.dependableMap == nil { 157 g.dependableMap = make(map[string]dag.Vertex) 158 } 159 } 160 161 func (g *Graph) walk(walker GraphWalker) error { 162 // The callbacks for enter/exiting a graph 163 ctx := walker.EnterPath(g.Path) 164 defer walker.ExitPath(g.Path) 165 166 // Get the path for logs 167 path := strings.Join(ctx.Path(), ".") 168 169 // Walk the graph. 170 var walkFn dag.WalkFunc 171 walkFn = func(v dag.Vertex) (rerr error) { 172 log.Printf("[DEBUG] vertex %s.%s: walking", path, dag.VertexName(v)) 173 174 walker.EnterVertex(v) 175 defer func() { walker.ExitVertex(v, rerr) }() 176 177 // vertexCtx is the context that we use when evaluating. This 178 // is normally the context of our graph but can be overridden 179 // with a GraphNodeSubPath impl. 180 vertexCtx := ctx 181 if pn, ok := v.(GraphNodeSubPath); ok && len(pn.Path()) > 0 { 182 vertexCtx = walker.EnterPath(pn.Path()) 183 defer walker.ExitPath(pn.Path()) 184 } 185 186 // If the node is eval-able, then evaluate it. 187 if ev, ok := v.(GraphNodeEvalable); ok { 188 tree := ev.EvalTree() 189 if tree == nil { 190 panic(fmt.Sprintf( 191 "%s.%s (%T): nil eval tree", path, dag.VertexName(v), v)) 192 } 193 194 // Allow the walker to change our tree if needed. Eval, 195 // then callback with the output. 196 log.Printf("[DEBUG] vertex %s.%s: evaluating", path, dag.VertexName(v)) 197 tree = walker.EnterEvalTree(v, tree) 198 output, err := Eval(tree, vertexCtx) 199 if rerr = walker.ExitEvalTree(v, output, err); rerr != nil { 200 return 201 } 202 } 203 204 // If the node is dynamically expanded, then expand it 205 if ev, ok := v.(GraphNodeDynamicExpandable); ok { 206 log.Printf( 207 "[DEBUG] vertex %s.%s: expanding/walking dynamic subgraph", 208 path, 209 dag.VertexName(v)) 210 g, err := ev.DynamicExpand(vertexCtx) 211 if err != nil { 212 rerr = err 213 return 214 } 215 216 // Walk the subgraph 217 if rerr = g.walk(walker); rerr != nil { 218 return 219 } 220 } 221 222 // If the node has a subgraph, then walk the subgraph 223 if sn, ok := v.(GraphNodeSubgraph); ok { 224 log.Printf( 225 "[DEBUG] vertex %s.%s: walking subgraph", 226 path, 227 dag.VertexName(v)) 228 229 if rerr = sn.Subgraph().walk(walker); rerr != nil { 230 return 231 } 232 } 233 234 return nil 235 } 236 237 return g.AcyclicGraph.Walk(walkFn) 238 } 239 240 // GraphNodeDependable is an interface which says that a node can be 241 // depended on (an edge can be placed between this node and another) according 242 // to the well-known name returned by DependableName. 243 // 244 // DependableName can return multiple names it is known by. 245 type GraphNodeDependable interface { 246 DependableName() []string 247 } 248 249 // GraphNodeDependent is an interface which says that a node depends 250 // on another GraphNodeDependable by some name. By implementing this 251 // interface, Graph.ConnectDependents() can be called multiple times 252 // safely and efficiently. 253 type GraphNodeDependent interface { 254 DependentOn() []string 255 }