github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/cmd/jujud/agent/engine/housing.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package engine
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  
     9  	"github.com/juju/juju/worker"
    10  	"github.com/juju/juju/worker/dependency"
    11  	"github.com/juju/juju/worker/fortress"
    12  )
    13  
    14  // Decorator creates copies of dependency.Manifolds with additional
    15  // features.
    16  type Decorator interface {
    17  
    18  	// Decorate returns a new Manifold, based on the one supplied.
    19  	Decorate(dependency.Manifold) dependency.Manifold
    20  }
    21  
    22  // Housing is a Decorator that combines several common mechanisms
    23  // for coordinating manifolds independently of their core concerns.
    24  type Housing struct {
    25  
    26  	// Flags contains a list of names of Flag manifolds, such
    27  	// that a decorated manifold will not start until all flags
    28  	// are both present and valid (and will be stopped when that
    29  	// is no longer true).
    30  	Flags []string
    31  
    32  	// Occupy is ignored if empty; otherwise it contains the name
    33  	// of a fortress.Guest manifold, such that a decorated manifold
    34  	// will never be run outside a Visit to that fortress.
    35  	//
    36  	// NOTE: this acquires a lock, and holds it for your manifold's
    37  	// worker's whole lifetime. It's fine in isolation, but multiple
    38  	// Occupy~s are almost certainly a Bad Idea.
    39  	Occupy string
    40  
    41  	// Filter is ignored if nil; otherwise it's unconditionally set
    42  	// as the manifold's Filter. Similarly to Occupy, attempted use
    43  	// of multiple filters is unlikely to be a great idea; it most
    44  	// likely indicates that either your Engine's IsFatal is too
    45  	// enthusiastic, or responsibility for termination is spread too
    46  	// widely across your installed manifolds, or both.
    47  	Filter dependency.FilterFunc
    48  }
    49  
    50  // Decorate is part of the Decorator interface.
    51  func (housing Housing) Decorate(base dependency.Manifold) dependency.Manifold {
    52  	manifold := base
    53  	// Apply Occupy wrapping first, so that it will be the last
    54  	// wrapper to execute before calling the original Start func, so
    55  	// as to minimise the time we hold the fortress open.
    56  	if housing.Occupy != "" {
    57  		manifold.Inputs = maybeAdd(manifold.Inputs, housing.Occupy)
    58  		manifold.Start = occupyStart(manifold.Start, housing.Occupy)
    59  	}
    60  	for _, name := range housing.Flags {
    61  		manifold.Inputs = maybeAdd(manifold.Inputs, name)
    62  		manifold.Start = flagStart(manifold.Start, name)
    63  	}
    64  	if housing.Filter != nil {
    65  		manifold.Filter = housing.Filter
    66  	}
    67  	return manifold
    68  }
    69  
    70  func maybeAdd(original []string, add string) []string {
    71  	for _, name := range original {
    72  		if name == add {
    73  			return original
    74  		}
    75  	}
    76  	count := len(original)
    77  	result := make([]string, count, count+1)
    78  	copy(result, original)
    79  	return append(result, add)
    80  }
    81  
    82  func occupyStart(inner dependency.StartFunc, name string) dependency.StartFunc {
    83  	return func(context dependency.Context) (worker.Worker, error) {
    84  		var guest fortress.Guest
    85  		if err := context.Get(name, &guest); err != nil {
    86  			return nil, errors.Trace(err)
    87  		}
    88  		task := func() (worker.Worker, error) {
    89  			return inner(context)
    90  		}
    91  		worker, err := fortress.Occupy(guest, task, context.Abort())
    92  		if err != nil {
    93  			return nil, errors.Trace(err)
    94  		}
    95  		return worker, nil
    96  	}
    97  }
    98  
    99  func flagStart(inner dependency.StartFunc, name string) dependency.StartFunc {
   100  	return func(context dependency.Context) (worker.Worker, error) {
   101  		var flag Flag
   102  		if err := context.Get(name, &flag); err != nil {
   103  			return nil, errors.Trace(err)
   104  		}
   105  		if !flag.Check() {
   106  			return nil, errors.Annotatef(dependency.ErrMissing, "%q not set", name)
   107  		}
   108  		return inner(context)
   109  	}
   110  }