github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/worker/dependency/interface.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 // Engine is a mechanism for persistently running named workers and managing 13 // dependencies between them. 14 type Engine interface { 15 16 // Engine is itself a Worker. 17 worker.Worker 18 19 // Engine exposes human-comprehensible status data to its clients. 20 Reporter 21 22 // Install causes the Engine to accept responsibility for maintaining a 23 // worker corresponding to the supplied manifold, restarting it when it 24 // fails and when its inputs' workers change, until the Engine shuts down. 25 Install(name string, manifold Manifold) error 26 } 27 28 // Manifold defines the behaviour of a node in an Engine's dependency graph. It's 29 // named for the "device that connects multiple inputs or outputs" sense of the 30 // word. 31 type Manifold struct { 32 33 // Inputs lists the names of the manifolds which this manifold might use. 34 // An engine will attempt to start a worker independent of the availability 35 // of these inputs, and will restart the worker as the available inputs 36 // change. If a worker has no dependencies, it should declare empty inputs. 37 Inputs []string 38 39 // Start is used to create a worker for the manifold. It must not be nil. 40 // The supplied GetResourceFunc will return ErrMissing for any dependency 41 // not named in Inputs, and will cease to function immediately after the 42 // StartFunc returns: do not store references to it. 43 // 44 // Note that, while Start must exist, it doesn't *have* to *start* a worker 45 // (although it must return either a worker or an error). That is to say: in 46 // *some* circumstances, it's ok to wrap a worker under the management of a 47 // separate component (e.g. the `worker/agent` Manifold itself) but this 48 // approach should only be used: 49 // 50 // * as a last resort; and 51 // * with clear justification. 52 // 53 // ...because it's a deliberate, and surprising, subversion of the dependency 54 // model; and is thus much harder to reason about and implement correctly. In 55 // particular, if you write a surprising start func, you can't safely declare 56 // any inputs at all. 57 Start StartFunc 58 59 // Output is used to implement a GetResourceFunc for manifolds that declare 60 // a dependency on this one; it can be nil if your manifold is a leaf node, 61 // or if it exposes no services to its dependents. 62 // 63 // If you implement an Output func, be especially careful to expose sensible 64 // types. Your `out` param should almost always be a pointer to an interface; 65 // and, furthermore, to an interface that does *not* satisfy `worker.Worker`. 66 // 67 // (Consider the interface segregation principle: the *Engine* is reponsible 68 // for the lifetimes of the backing workers, and for handling their errors. 69 // Exposing those levers to your dependents as well can only encourage them 70 // to use them, and vastly complicate the possible interactions.) 71 // 72 // And if your various possible clients might use different sets of features, 73 // please keep those interfaces segregated as well: prefer to accept [a *Foo 74 // or a *Bar] rather than just [a *FooBar] -- unless all your clients really 75 // do want a FooBar resource. 76 // 77 // Even if the Engine itself didn't bother to track the types exposed per 78 // dependency, it's still a useful prophylactic against complexity -- so 79 // that when reading manifold code, it should be immediately clear both what 80 // your dependencies *are* (by reading the names in the manifold config) 81 // and what they *do* for you (by reading the start func and observing the 82 // types in play). 83 Output OutputFunc 84 } 85 86 // Manifolds conveniently represents several Manifolds. 87 type Manifolds map[string]Manifold 88 89 // StartFunc returns a worker or an error. All the worker's dependencies should 90 // be taken from the supplied GetResourceFunc; if no worker can be started due 91 // to unmet dependencies, it should return ErrMissing, in which case it will 92 // not be called again until its declared inputs change. 93 type StartFunc func(getResource GetResourceFunc) (worker.Worker, error) 94 95 // GetResourceFunc 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 // * if the named resource exists, and out is nil, it returns nil 100 // * if the named resource exists, and out is non-nil, it returns the error 101 // from the named resource manifold's output func (hopefully nil) 102 // 103 // Appropriate types for the out pointer depend upon the resource in question. 104 type GetResourceFunc func(name string, out interface{}) error 105 106 // ErrMissing can be returned by a StartFunc or a worker to indicate to 107 // the engine that it can't be usefully restarted until at least one of its 108 // dependencies changes. There's no way to specify *which* dependency you need, 109 // because that's a lot of implementation hassle for little practical gain. 110 var ErrMissing = errors.New("dependency not available") 111 112 // OutputFunc is a type coercion function for a worker generated by a StartFunc. 113 // When passed an out pointer to a type it recognises, it will assign a suitable 114 // value and return no error. 115 type OutputFunc func(in worker.Worker, out interface{}) error 116 117 // IsFatalFunc is used to configure an Engine such that, if any worker returns 118 // an error that satisfies the engine's IsFatalFunc, the engine will stop all 119 // its workers, shut itself down, and return the original fatal error via Wait(). 120 type IsFatalFunc func(err error) bool 121 122 // WorstErrorFunc is used to rank fatal errors, to allow an Engine to return the 123 // single most important error it's encountered. 124 type WorstErrorFunc func(err0, err1 error) error 125 126 // Reporter defines an interface for extracting human-relevant information 127 // from a worker. 128 type Reporter interface { 129 130 // Report returns a map describing the state of the receiver. It is expected 131 // to be goroutine-safe. 132 // 133 // It is polite and helpful to use the Key* constants and conventions defined 134 // and described in this package, where appropriate, but that's for the 135 // convenience of the humans that read the reports; we don't and shouldn't 136 // have any code that depends on particular Report formats. 137 Report() map[string]interface{} 138 } 139 140 // The Key constants describe the constant features of an Engine's Report. 141 const ( 142 143 // KeyState applies to a worker; possible values are "starting", "started", 144 // "stopping", or "stopped". Or it might be something else, in distant 145 // Reporter implementations; don't make assumptions. 146 KeyState = "state" 147 148 // KeyError holds some relevant error. In the case of an Engine, this will be: 149 // * any internal error indicating incorrect operation; or 150 // * the most important fatal error encountered by any worker; or 151 // * nil, if none of the above apply; 152 // ...and the value should not be presumed to be stable until the engine 153 // state is "stopped". 154 // 155 // In the case of a manifold, it will always hold the most recent error 156 // returned by the associated worker (or its start func); and will be 157 // rewritten whenever a worker state is set to "started" or "stopped". 158 // 159 // In the case of a resource access, it holds any error encountered when 160 // trying to find or convert the resource. 161 KeyError = "error" 162 163 // KeyManifolds holds a map of manifold name to further data (including 164 // dependency inputs; current worker state; and any relevant report/error 165 // for the associated current/recent worker.) 166 KeyManifolds = "manifolds" 167 168 // KeyReport holds an arbitrary map of information returned by a manifold 169 // Worker that is also a Reporter. 170 KeyReport = "report" 171 172 // KeyInputs holds the names of the manifolds on which this one depends. 173 KeyInputs = "inputs" 174 175 // KeyResourceLog holds a slice representing the calls the current worker 176 // made to its getResource func; the type of the output param; and any 177 // error encountered. 178 KeyResourceLog = "resource-log" 179 180 // KeyName holds the name of some resource. 181 KeyName = "name" 182 183 // KeyType holds a string representation of the type by which a resource 184 // was accessed. 185 KeyType = "type" 186 )