github.com/ezbercih/terraform@v0.1.1-0.20140729011846-3c33865e0839/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 buf.WriteString(fmt.Sprintf("root: %s\n", g.Root.Name)) 167 for _, k := range keys { 168 n := mapping[k] 169 buf.WriteString(fmt.Sprintf("%s\n", n.Name)) 170 171 // Alphabetize the dependency names 172 depKeys := make([]string, 0, len(n.Deps)) 173 depMapping := make(map[string]*Dependency) 174 for _, d := range n.Deps { 175 depMapping[d.Target.Name] = d 176 depKeys = append(depKeys, d.Target.Name) 177 } 178 sort.Strings(depKeys) 179 180 for _, k := range depKeys { 181 dep := depMapping[k] 182 buf.WriteString(fmt.Sprintf( 183 " %s -> %s\n", 184 dep.Source, 185 dep.Target)) 186 } 187 } 188 189 return buf.String() 190 } 191 192 // Validate is used to ensure that a few properties of the graph are not violated: 193 // 1) There must be a single "root", or source on which nothing depends. 194 // 2) All nouns in the graph must be reachable from the root 195 // 3) The graph must be cycle free, meaning there are no cicular dependencies 196 func (g *Graph) Validate() error { 197 // Convert to node list 198 nodes := make([]digraph.Node, len(g.Nouns)) 199 for i, n := range g.Nouns { 200 nodes[i] = n 201 } 202 203 // Create a validate erro 204 vErr := &ValidateError{} 205 206 // Search for all the sources, if we have only 1, it must be the root 207 if sources := digraph.Sources(nodes); len(sources) != 1 { 208 vErr.MissingRoot = true 209 goto CHECK_CYCLES 210 } else { 211 g.Root = sources[0].(*Noun) 212 } 213 214 // Check reachability 215 if unreached := digraph.Unreachable(g.Root, nodes); len(unreached) > 0 { 216 vErr.Unreachable = make([]*Noun, len(unreached)) 217 for i, u := range unreached { 218 vErr.Unreachable[i] = u.(*Noun) 219 } 220 } 221 222 CHECK_CYCLES: 223 // Check for cycles 224 if cycles := digraph.StronglyConnectedComponents(nodes, true); len(cycles) > 0 { 225 vErr.Cycles = make([][]*Noun, len(cycles)) 226 for i, cycle := range cycles { 227 group := make([]*Noun, len(cycle)) 228 for j, n := range cycle { 229 group[j] = n.(*Noun) 230 } 231 vErr.Cycles[i] = group 232 } 233 } 234 235 // Check for loops to yourself 236 for _, n := range g.Nouns { 237 for _, d := range n.Deps { 238 if d.Source == d.Target { 239 vErr.Cycles = append(vErr.Cycles, []*Noun{n}) 240 } 241 } 242 } 243 244 // Return the detailed error 245 if vErr.MissingRoot || vErr.Unreachable != nil || vErr.Cycles != nil { 246 return vErr 247 } 248 return nil 249 } 250 251 // Walk will walk the tree depth-first (dependency first) and call 252 // the callback. 253 // 254 // The callbacks will be called in parallel, so if you need non-parallelism, 255 // then introduce a lock in your callback. 256 func (g *Graph) Walk(fn WalkFunc) error { 257 // Set so we don't callback for a single noun multiple times 258 var seenMapL sync.RWMutex 259 seenMap := make(map[*Noun]chan struct{}) 260 seenMap[g.Root] = make(chan struct{}) 261 262 // Keep track of what nodes errored. 263 var errMapL sync.RWMutex 264 errMap := make(map[*Noun]struct{}) 265 266 // Build the list of things to visit 267 tovisit := make([]*Noun, 1, len(g.Nouns)) 268 tovisit[0] = g.Root 269 270 // Spawn off all our goroutines to walk the tree 271 errCh := make(chan error) 272 quitCh := make(chan struct{}) 273 for len(tovisit) > 0 { 274 // Grab the current thing to use 275 n := len(tovisit) 276 current := tovisit[n-1] 277 tovisit = tovisit[:n-1] 278 279 // Go through each dependency and run that first 280 for _, dep := range current.Deps { 281 if _, ok := seenMap[dep.Target]; !ok { 282 seenMapL.Lock() 283 seenMap[dep.Target] = make(chan struct{}) 284 seenMapL.Unlock() 285 tovisit = append(tovisit, dep.Target) 286 } 287 } 288 289 // Spawn off a goroutine to execute our callback once 290 // all our dependencies are satisified. 291 go func(current *Noun) { 292 seenMapL.RLock() 293 closeCh := seenMap[current] 294 seenMapL.RUnlock() 295 296 defer close(closeCh) 297 298 // Wait for all our dependencies 299 for _, dep := range current.Deps { 300 seenMapL.RLock() 301 ch := seenMap[dep.Target] 302 seenMapL.RUnlock() 303 304 select { 305 case <-ch: 306 case <-quitCh: 307 return 308 } 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 // Close the quit channel so all our goroutines will end now 347 close(quitCh) 348 349 // Drain the error channel 350 go func() { 351 for _ = range errCh { 352 // Nothing 353 } 354 }() 355 356 // Wait for the goroutines to end 357 <-doneCh 358 close(errCh) 359 360 return err 361 } 362 }