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