github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/dependency/interface.go (about) 1 // Copyright 2015-2016 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 // Manifold defines the behaviour of a node in an Engine's dependency graph. It's 13 // named for the "device that connects multiple inputs or outputs" sense of the 14 // word. 15 type Manifold struct { 16 17 // Inputs lists the names of the manifolds which this manifold might use. 18 // An engine will attempt to start a worker independent of the availability 19 // of these inputs, and will restart the worker as the available inputs 20 // change. If a worker has no dependencies, it should declare empty inputs. 21 Inputs []string 22 23 // Start is used to create a worker for the manifold. It must not be nil. 24 // The supplied GetResourceFunc will return ErrMissing for any dependency 25 // not named in Inputs, and will cease to function immediately after the 26 // StartFunc returns: do not store references to it. 27 // 28 // Note that, while Start must exist, it doesn't *have* to *start* a worker 29 // (although it must return either a worker or an error). That is to say: in 30 // *some* circumstances, it's ok to wrap a worker under the management of a 31 // separate component (e.g. the `worker/agent` Manifold itself) but this 32 // approach should only be used: 33 // 34 // * as a last resort; and 35 // * with clear justification. 36 // 37 // ...because it's a deliberate, and surprising, subversion of the dependency 38 // model; and is thus much harder to reason about and implement correctly. In 39 // particular, if you write a surprising start func, you can't safely declare 40 // any inputs at all. 41 Start StartFunc 42 43 // Filter is used to convert errors returned from workers or Start funcs. It 44 // can be nil, in which case no filtering or conversion will be done. 45 // 46 // It's intended to convert domain-specific errors into dependency-specific 47 // errors (such as ErrBounce and ErrUninstall), so that workers managed by 48 // an Engine don't have to depend on this package directly. 49 // 50 // It *could* also be used to cause side effects, but remember to be careful; 51 // from your perspective, it'll be called from an arbitrary goroutine. 52 Filter FilterFunc 53 54 // Output is used to implement a GetResourceFunc for manifolds that declare 55 // a dependency on this one; it can be nil if your manifold is a leaf node, 56 // or if it exposes no services to its dependents. 57 // 58 // If you implement an Output func, be especially careful to expose sensible 59 // types. Your `out` param should almost always be a pointer to an interface; 60 // and, furthermore, to an interface that does *not* satisfy `worker.Worker`. 61 // 62 // (Consider the interface segregation principle: the *Engine* is reponsible 63 // for the lifetimes of the backing workers, and for handling their errors. 64 // Exposing those levers to your dependents as well can only encourage them 65 // to use them, and vastly complicate the possible interactions.) 66 // 67 // And if your various possible clients might use different sets of features, 68 // please keep those interfaces segregated as well: prefer to accept [a *Foo 69 // or a *Bar] rather than just [a *FooBar] -- unless all your clients really 70 // do want a FooBar resource. 71 // 72 // Even if the Engine itself didn't bother to track the types exposed per 73 // dependency, it's still a useful prophylactic against complexity -- so 74 // that when reading manifold code, it should be immediately clear both what 75 // your dependencies *are* (by reading the names in the manifold config) 76 // and what they *do* for you (by reading the start func and observing the 77 // types in play). 78 Output OutputFunc 79 } 80 81 // Manifolds conveniently represents several Manifolds. 82 type Manifolds map[string]Manifold 83 84 // Context represents the situation in which a StartFunc is running. A Context should 85 // not be used outside its StartFunc; attempts to do so will have undefined results. 86 type Context interface { 87 88 // Abort will be closed if the containing engine no longer wants to 89 // start the manifold's worker. You can ignore Abort if your worker 90 // will start quickly -- it'll just be shut down immediately, nbd -- 91 // but if you need to mess with channels or long-running operations 92 // in your StartFunc, Abort lets you do so safely. 93 Abort() <-chan struct{} 94 95 // Get returns an indication of whether a named dependency can be 96 // satisfied. In particular: 97 // 98 // * if the named resource does not exist, it returns ErrMissing; 99 // * else if out is nil, it returns nil; 100 // * else if the named resource has no OutputFunc, it returns ErrMissing; 101 // * else it passes out into the OutputFunc and returns whatever error 102 // transpires (hopefully nil). 103 // 104 // Appropriate types for the out pointer depend upon the resource in question. 105 Get(name string, out interface{}) error 106 } 107 108 // StartFunc returns a worker or an error. All the worker's dependencies should 109 // be taken from the supplied GetResourceFunc; if no worker can be started due 110 // to unmet dependencies, it should return ErrMissing, in which case it will 111 // not be called again until its declared inputs change. 112 type StartFunc func(context Context) (worker.Worker, error) 113 114 // ErrMissing can be returned by a StartFunc or a worker to indicate to 115 // the engine that it can't be usefully restarted until at least one of its 116 // dependencies changes. There's no way to specify *which* dependency you need, 117 // because that's a lot of implementation hassle for little practical gain. 118 var ErrMissing = errors.New("dependency not available") 119 120 // ErrBounce can be returned by a StartFunc or a worker to indicate to 121 // the engine that it should be restarted immediately, instead of 122 // waiting for ErrorDelay. This is useful for workers which restart 123 // themselves to alert dependents that an output has changed. 124 var ErrBounce = errors.New("restart immediately") 125 126 // ErrUninstall can be returned by a StartFunc or a worker to indicate to the 127 // engine that it can/should never run again, and that the originating manifold 128 // should be completely removed. 129 var ErrUninstall = errors.New("resource permanently unavailable") 130 131 // FilterFunc is an error conversion function for errors returned from workers 132 // or StartFuncs. 133 type FilterFunc func(error) error 134 135 // OutputFunc is a type coercion function for a worker generated by a StartFunc. 136 // When passed an out pointer to a type it recognises, it will assign a suitable 137 // value and return no error. 138 type OutputFunc func(in worker.Worker, out interface{}) error 139 140 // IsFatalFunc is used to configure an Engine such that, if any worker returns 141 // an error that satisfies the engine's IsFatalFunc, the engine will stop all 142 // its workers, shut itself down, and return the original fatal error via Wait(). 143 type IsFatalFunc func(err error) bool 144 145 // WorstErrorFunc is used to rank fatal errors, to allow an Engine to return the 146 // single most important error it's encountered. 147 type WorstErrorFunc func(err0, err1 error) error