github.com/tkak/terraform@v0.5.4-0.20150712180941-7f738dc27225/depgraph/graph.go (about) 1 // The depgraph package is used to create and model a dependency graph 2 // of nouns. Each noun can represent a service, server, application, 3 // network switch, etc. Nouns can depend on other nouns, and provide 4 // versioning constraints. Nouns can also have various meta data that 5 // may be relevant to their construction or configuration. 6 package depgraph 7 8 import ( 9 "bytes" 10 "fmt" 11 "sort" 12 "strings" 13 "sync" 14 15 "github.com/hashicorp/terraform/digraph" 16 ) 17 18 // WalkFunc is the type used for the callback for Walk. 19 type WalkFunc func(*Noun) error 20 21 // Graph is used to represent a dependency graph. 22 type Graph struct { 23 Name string 24 Meta interface{} 25 Nouns []*Noun 26 Root *Noun 27 } 28 29 // ValidateError implements the Error interface but provides 30 // additional information on a validation error. 31 type ValidateError struct { 32 // If set, then the graph is missing a single root, on which 33 // there are no depdendencies 34 MissingRoot bool 35 36 // Unreachable are nodes that could not be reached from 37 // the root noun. 38 Unreachable []*Noun 39 40 // Cycles are groups of strongly connected nodes, which 41 // form a cycle. This is disallowed. 42 Cycles [][]*Noun 43 } 44 45 func (v *ValidateError) Error() string { 46 var msgs []string 47 48 if v.MissingRoot { 49 msgs = append(msgs, "The graph has no single root") 50 } 51 52 for _, n := range v.Unreachable { 53 msgs = append(msgs, fmt.Sprintf( 54 "Unreachable node: %s", n.Name)) 55 } 56 57 for _, c := range v.Cycles { 58 cycleNodes := make([]string, len(c)) 59 for i, n := range c { 60 cycleNodes[i] = n.Name 61 } 62 63 msgs = append(msgs, fmt.Sprintf( 64 "Cycle: %s", strings.Join(cycleNodes, " -> "))) 65 } 66 67 for i, m := range msgs { 68 msgs[i] = fmt.Sprintf("* %s", m) 69 } 70 71 return fmt.Sprintf( 72 "The dependency graph is not valid:\n\n%s", 73 strings.Join(msgs, "\n")) 74 } 75 76 // ConstraintError is used to return detailed violation 77 // information from CheckConstraints 78 type ConstraintError struct { 79 Violations []*Violation 80 } 81 82 func (c *ConstraintError) Error() string { 83 return fmt.Sprintf("%d constraint violations", len(c.Violations)) 84 } 85 86 // Violation is used to pass along information about 87 // a constraint violation 88 type Violation struct { 89 Source *Noun 90 Target *Noun 91 Dependency *Dependency 92 Constraint Constraint 93 Err error 94 } 95 96 func (v *Violation) Error() string { 97 return fmt.Sprintf("Constraint %v between %v and %v violated: %v", 98 v.Constraint, v.Source, v.Target, v.Err) 99 } 100 101 // CheckConstraints walks the graph and ensures that all 102 // user imposed constraints are satisfied. 103 func (g *Graph) CheckConstraints() error { 104 // Ensure we have a root 105 if g.Root == nil { 106 return fmt.Errorf("Graph must be validated before checking constraint violations") 107 } 108 109 // Create a constraint error 110 cErr := &ConstraintError{} 111 112 // Walk from the root 113 digraph.DepthFirstWalk(g.Root, func(n digraph.Node) bool { 114 noun := n.(*Noun) 115 for _, dep := range noun.Deps { 116 target := dep.Target 117 for _, constraint := range dep.Constraints { 118 ok, err := constraint.Satisfied(noun, target) 119 if ok { 120 continue 121 } 122 violation := &Violation{ 123 Source: noun, 124 Target: target, 125 Dependency: dep, 126 Constraint: constraint, 127 Err: err, 128 } 129 cErr.Violations = append(cErr.Violations, violation) 130 } 131 } 132 return true 133 }) 134 135 if cErr.Violations != nil { 136 return cErr 137 } 138 return nil 139 } 140 141 // Noun returns the noun with the given name, or nil if it cannot be found. 142 func (g *Graph) Noun(name string) *Noun { 143 for _, n := range g.Nouns { 144 if n.Name == name { 145 return n 146 } 147 } 148 149 return nil 150 } 151 152 // String generates a little ASCII string of the graph, useful in 153 // debugging output. 154 func (g *Graph) String() string { 155 var buf bytes.Buffer 156 157 // Alphabetize the output based on the noun name 158 keys := make([]string, 0, len(g.Nouns)) 159 mapping := make(map[string]*Noun) 160 for _, n := range g.Nouns { 161 mapping[n.Name] = n 162 keys = append(keys, n.Name) 163 } 164 sort.Strings(keys) 165 166 if g.Root != nil { 167 buf.WriteString(fmt.Sprintf("root: %s\n", g.Root.Name)) 168 } else { 169 buf.WriteString("root: <unknown>\n") 170 } 171 for _, k := range keys { 172 n := mapping[k] 173 buf.WriteString(fmt.Sprintf("%s\n", n.Name)) 174 175 // Alphabetize the dependency names 176 depKeys := make([]string, 0, len(n.Deps)) 177 depMapping := make(map[string]*Dependency) 178 for _, d := range n.Deps { 179 depMapping[d.Target.Name] = d 180 depKeys = append(depKeys, d.Target.Name) 181 } 182 sort.Strings(depKeys) 183 184 for _, k := range depKeys { 185 dep := depMapping[k] 186 buf.WriteString(fmt.Sprintf( 187 " %s -> %s\n", 188 dep.Source, 189 dep.Target)) 190 } 191 } 192 193 return buf.String() 194 } 195 196 // Validate is used to ensure that a few properties of the graph are not violated: 197 // 1) There must be a single "root", or source on which nothing depends. 198 // 2) All nouns in the graph must be reachable from the root 199 // 3) The graph must be cycle free, meaning there are no cicular dependencies 200 func (g *Graph) Validate() error { 201 // Convert to node list 202 nodes := make([]digraph.Node, len(g.Nouns)) 203 for i, n := range g.Nouns { 204 nodes[i] = n 205 } 206 207 // Create a validate erro 208 vErr := &ValidateError{} 209 210 // Search for all the sources, if we have only 1, it must be the root 211 if sources := digraph.Sources(nodes); len(sources) != 1 { 212 vErr.MissingRoot = true 213 goto CHECK_CYCLES 214 } else { 215 g.Root = sources[0].(*Noun) 216 } 217 218 // Check reachability 219 if unreached := digraph.Unreachable(g.Root, nodes); len(unreached) > 0 { 220 vErr.Unreachable = make([]*Noun, len(unreached)) 221 for i, u := range unreached { 222 vErr.Unreachable[i] = u.(*Noun) 223 } 224 } 225 226 CHECK_CYCLES: 227 // Check for cycles 228 if cycles := digraph.StronglyConnectedComponents(nodes, true); len(cycles) > 0 { 229 vErr.Cycles = make([][]*Noun, len(cycles)) 230 for i, cycle := range cycles { 231 group := make([]*Noun, len(cycle)) 232 for j, n := range cycle { 233 group[j] = n.(*Noun) 234 } 235 vErr.Cycles[i] = group 236 } 237 } 238 239 // Check for loops to yourself 240 for _, n := range g.Nouns { 241 for _, d := range n.Deps { 242 if d.Source == d.Target { 243 vErr.Cycles = append(vErr.Cycles, []*Noun{n}) 244 } 245 } 246 } 247 248 // Return the detailed error 249 if vErr.MissingRoot || vErr.Unreachable != nil || vErr.Cycles != nil { 250 return vErr 251 } 252 return nil 253 } 254 255 // Walk will walk the tree depth-first (dependency first) and call 256 // the callback. 257 // 258 // The callbacks will be called in parallel, so if you need non-parallelism, 259 // then introduce a lock in your callback. 260 func (g *Graph) Walk(fn WalkFunc) error { 261 // Set so we don't callback for a single noun multiple times 262 var seenMapL sync.RWMutex 263 seenMap := make(map[*Noun]chan struct{}) 264 seenMap[g.Root] = make(chan struct{}) 265 266 // Keep track of what nodes errored. 267 var errMapL sync.RWMutex 268 errMap := make(map[*Noun]struct{}) 269 270 // Build the list of things to visit 271 tovisit := make([]*Noun, 1, len(g.Nouns)) 272 tovisit[0] = g.Root 273 274 // Spawn off all our goroutines to walk the tree 275 errCh := make(chan error) 276 for len(tovisit) > 0 { 277 // Grab the current thing to use 278 n := len(tovisit) 279 current := tovisit[n-1] 280 tovisit = tovisit[:n-1] 281 282 // Go through each dependency and run that first 283 for _, dep := range current.Deps { 284 if _, ok := seenMap[dep.Target]; !ok { 285 seenMapL.Lock() 286 seenMap[dep.Target] = make(chan struct{}) 287 seenMapL.Unlock() 288 tovisit = append(tovisit, dep.Target) 289 } 290 } 291 292 // Spawn off a goroutine to execute our callback once 293 // all our dependencies are satisfied. 294 go func(current *Noun) { 295 seenMapL.RLock() 296 closeCh := seenMap[current] 297 seenMapL.RUnlock() 298 299 defer close(closeCh) 300 301 // Wait for all our dependencies 302 for _, dep := range current.Deps { 303 seenMapL.RLock() 304 ch := seenMap[dep.Target] 305 seenMapL.RUnlock() 306 307 // Wait for the dep to be run 308 <-ch 309 310 // Check if any dependencies errored. If so, 311 // then return right away, we won't walk it. 312 errMapL.RLock() 313 _, errOk := errMap[dep.Target] 314 errMapL.RUnlock() 315 if errOk { 316 return 317 } 318 } 319 320 // Call our callback! 321 if err := fn(current); err != nil { 322 errMapL.Lock() 323 errMap[current] = struct{}{} 324 errMapL.Unlock() 325 326 errCh <- err 327 } 328 }(current) 329 } 330 331 // Aggregate channel that is closed when all goroutines finish 332 doneCh := make(chan struct{}) 333 go func() { 334 defer close(doneCh) 335 336 for _, ch := range seenMap { 337 <-ch 338 } 339 }() 340 341 // Wait for finish OR an error 342 select { 343 case <-doneCh: 344 return nil 345 case err := <-errCh: 346 // Drain the error channel 347 go func() { 348 for _ = range errCh { 349 // Nothing 350 } 351 }() 352 353 // Wait for the goroutines to end 354 <-doneCh 355 close(errCh) 356 357 return err 358 } 359 } 360 361 // DependsOn returns the set of nouns that have a 362 // dependency on a given noun. This can be used to find 363 // the incoming edges to a noun. 364 func (g *Graph) DependsOn(n *Noun) []*Noun { 365 var incoming []*Noun 366 OUTER: 367 for _, other := range g.Nouns { 368 if other == n { 369 continue 370 } 371 for _, d := range other.Deps { 372 if d.Target == n { 373 incoming = append(incoming, other) 374 continue OUTER 375 } 376 } 377 } 378 return incoming 379 }