github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/gate/flag.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package gate
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"launchpad.net/tomb"
     9  
    10  	"github.com/juju/juju/cmd/jujud/agent/util"
    11  	"github.com/juju/juju/worker"
    12  	"github.com/juju/juju/worker/dependency"
    13  )
    14  
    15  // FlagManifoldConfig holds the dependencies required to run a Flag
    16  // in a dependency.Engine.
    17  type FlagManifoldConfig struct {
    18  	GateName  string
    19  	NewWorker func(gate Waiter) (worker.Worker, error)
    20  }
    21  
    22  // start is a dependency.StartFunc that uses config.
    23  func (config FlagManifoldConfig) start(context dependency.Context) (worker.Worker, error) {
    24  	var gate Waiter
    25  	if err := context.Get(config.GateName, &gate); err != nil {
    26  		return nil, errors.Trace(err)
    27  	}
    28  	worker, err := config.NewWorker(gate)
    29  	if err != nil {
    30  		return nil, errors.Trace(err)
    31  	}
    32  	return worker, nil
    33  
    34  }
    35  
    36  // FlagManifold runs a worker that implements util.Flag such that
    37  // it's only considered set when the referenced gate is unlocked.
    38  func FlagManifold(config FlagManifoldConfig) dependency.Manifold {
    39  	return dependency.Manifold{
    40  		Inputs: []string{config.GateName},
    41  		Start:  config.start,
    42  		Output: util.FlagOutput,
    43  		Filter: bounceUnlocked,
    44  	}
    45  }
    46  
    47  // NewFlag returns a worker that implements util.Flag,
    48  // backed by the supplied gate's unlockedness.
    49  func NewFlag(gate Waiter) (*Flag, error) {
    50  	w := &Flag{
    51  		gate:     gate,
    52  		unlocked: gate.IsUnlocked(),
    53  	}
    54  	go func() {
    55  		defer w.tomb.Done()
    56  		w.tomb.Kill(w.run())
    57  	}()
    58  	return w, nil
    59  }
    60  
    61  // Flag uses a gate to implement util.Flag.
    62  type Flag struct {
    63  	tomb     tomb.Tomb
    64  	gate     Waiter
    65  	unlocked bool
    66  }
    67  
    68  // Kill is part of the worker.Worker interface.
    69  func (w *Flag) Kill() {
    70  	w.tomb.Kill(nil)
    71  }
    72  
    73  // Wait is part of the worker.Worker interface.
    74  func (w *Flag) Wait() error {
    75  	return w.tomb.Wait()
    76  }
    77  
    78  // Check is part of the util.Flag interface.
    79  func (w *Flag) Check() bool {
    80  	return w.unlocked
    81  }
    82  
    83  func (w *Flag) run() error {
    84  	var bounce <-chan struct{}
    85  	if !w.unlocked {
    86  		bounce = w.gate.Unlocked()
    87  	}
    88  	select {
    89  	case <-w.tomb.Dying():
    90  		return tomb.ErrDying
    91  	case <-bounce:
    92  		return ErrUnlocked
    93  	}
    94  }
    95  
    96  // ErrUnlocked indicates that a Flag's gate has been unlocked and
    97  // it should be restarted to reflect the new value.
    98  var ErrUnlocked = errors.New("gate unlocked")
    99  
   100  // bounceUnlocked returns dependency.ErrBounce if passed an error caused
   101  // by ErrUnlocked; and otherwise returns the original error.
   102  func bounceUnlocked(err error) error {
   103  	if errors.Cause(err) == ErrUnlocked {
   104  		return dependency.ErrBounce
   105  	}
   106  	return err
   107  }