github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/cmd/jujud/agent/util/housing.go (about)

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