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