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  )