github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/worker/dependency/util.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package dependency 5 6 import ( 7 "github.com/juju/errors" 8 9 "github.com/juju/juju/worker" 10 ) 11 12 // Installer exposes an Engine's Install method. 13 type Installer interface { 14 Install(name string, manifold Manifold) error 15 } 16 17 // Install is a convenience function for installing multiple manifolds into an 18 // Installer at once. It returns the first error it encounters (and installs no 19 // more manifolds). 20 func Install(installer Installer, manifolds Manifolds) error { 21 for name, manifold := range manifolds { 22 if err := installer.Install(name, manifold); err != nil { 23 return errors.Trace(err) 24 } 25 } 26 return nil 27 } 28 29 // Validate will return an error if the dependency graph defined by the supplied 30 // manifolds contains any cycles. 31 func Validate(manifolds Manifolds) error { 32 inputs := make(map[string][]string) 33 for name, manifold := range manifolds { 34 inputs[name] = manifold.Inputs 35 } 36 return validator{ 37 inputs: inputs, 38 doing: make(map[string]bool), 39 done: make(map[string]bool), 40 }.run() 41 } 42 43 // validator implements a topological sort of the nodes defined in inputs; it 44 // doesn't actually produce sorted nodes, but rather exists to return an error 45 // if it determines that the nodes cannot be sorted (and hence a cycle exists). 46 type validator struct { 47 inputs map[string][]string 48 doing map[string]bool 49 done map[string]bool 50 } 51 52 func (v validator) run() error { 53 for node := range v.inputs { 54 if err := v.visit(node); err != nil { 55 return errors.Trace(err) 56 } 57 } 58 return nil 59 } 60 61 func (v validator) visit(node string) error { 62 if v.doing[node] { 63 return errors.Errorf("cycle detected at %q (considering: %v)", node, v.doing) 64 } 65 if !v.done[node] { 66 v.doing[node] = true 67 for _, input := range v.inputs[node] { 68 if err := v.visit(input); err != nil { 69 // Tracing this error will not help anyone. 70 return err 71 } 72 } 73 v.done[node] = true 74 v.doing[node] = false 75 } 76 return nil 77 } 78 79 // SelfManifold returns a manifold exposing a running dependency engine's 80 // Installer and Reporter services. The returned manifold is intended for 81 // installation into the engine it wraps; installing it into other engines 82 // may have surprising effects. 83 func SelfManifold(engine *Engine) Manifold { 84 return Manifold{ 85 Start: func(_ Context) (worker.Worker, error) { 86 return engine, nil 87 }, 88 Output: func(in worker.Worker, out interface{}) error { 89 if in != engine { 90 return errors.New("unexpected input worker") 91 } 92 switch outPtr := out.(type) { 93 case *Installer: 94 *outPtr = engine 95 case *Reporter: 96 *outPtr = engine 97 default: 98 return errors.Errorf("out should be a *Installer or a *Reporter; is %#v", out) 99 } 100 return nil 101 }, 102 } 103 }