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