github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/lifeflag/worker.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package lifeflag
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"gopkg.in/juju/names.v2"
     9  	"gopkg.in/juju/worker.v1/catacomb"
    10  
    11  	"github.com/juju/juju/api/lifeflag"
    12  	"github.com/juju/juju/core/life"
    13  	"github.com/juju/juju/core/watcher"
    14  )
    15  
    16  // Facade exposes capabilities required by the worker.
    17  type Facade interface {
    18  	Watch(names.Tag) (watcher.NotifyWatcher, error)
    19  	Life(names.Tag) (life.Value, error)
    20  }
    21  
    22  // Config holds the configuration and dependencies for a worker.
    23  type Config struct {
    24  	Facade Facade
    25  	Entity names.Tag
    26  	Result life.Predicate
    27  }
    28  
    29  // Validate returns an error if the config cannot be expected
    30  // to drive a functional worker.
    31  func (config Config) Validate() error {
    32  	if config.Facade == nil {
    33  		return errors.NotValidf("nil Facade")
    34  	}
    35  	if config.Entity == nil {
    36  		return errors.NotValidf("nil Entity")
    37  	}
    38  	if config.Result == nil {
    39  		return errors.NotValidf("nil Result")
    40  	}
    41  	return nil
    42  }
    43  
    44  var (
    45  	// ErrNotFound indicates that the worker cannot run because
    46  	// the configured entity does not exist.
    47  	ErrNotFound = errors.New("entity not found")
    48  
    49  	// ErrValueChanged indicates that the result of Check is
    50  	// outdated, and the worker should be restarted.
    51  	ErrValueChanged = errors.New("flag value changed")
    52  )
    53  
    54  // filter is used to wrap errors that might have come from the api,
    55  // so that we can return an error appropriate to our level. Was
    56  // tempted to make it a manifold-level thing, but that'd be even
    57  // worse (because the worker should not be emitting api errors for
    58  // conditions it knows about, full stop).
    59  func filter(err error) error {
    60  	if cause := errors.Cause(err); cause == lifeflag.ErrNotFound {
    61  		return ErrNotFound
    62  	}
    63  	return err
    64  }
    65  
    66  // New returns a worker that exposes the result of the configured
    67  // predicate when applied to the configured entity's life value,
    68  // and fails with ErrValueChanged when the result changes.
    69  func New(config Config) (*Worker, error) {
    70  	if err := config.Validate(); err != nil {
    71  		return nil, errors.Trace(err)
    72  	}
    73  
    74  	// Read it before the worker starts, so that we have a value
    75  	// guaranteed before we return the worker. Because we read this
    76  	// before we start the internal watcher, we'll need an additional
    77  	// read triggered by the first change event; this will *probably*
    78  	// be the same value, but we can't assume it.
    79  	life, err := config.Facade.Life(config.Entity)
    80  	if err != nil {
    81  		return nil, filter(errors.Trace(err))
    82  	}
    83  
    84  	w := &Worker{
    85  		config: config,
    86  		life:   life,
    87  	}
    88  	err = catacomb.Invoke(catacomb.Plan{
    89  		Site: &w.catacomb,
    90  		Work: w.loop,
    91  	})
    92  	if err != nil {
    93  		return nil, errors.Trace(err)
    94  	}
    95  	return w, nil
    96  }
    97  
    98  // Worker holds the result of some predicate regarding an entity's life,
    99  // and fails with ErrValueChanged when the result of the predicate changes.
   100  type Worker struct {
   101  	catacomb catacomb.Catacomb
   102  	config   Config
   103  	life     life.Value
   104  }
   105  
   106  // Kill is part of the worker.Worker interface.
   107  func (w *Worker) Kill() {
   108  	w.catacomb.Kill(nil)
   109  }
   110  
   111  // Wait is part of the worker.Worker interface.
   112  func (w *Worker) Wait() error {
   113  	return filter(w.catacomb.Wait())
   114  }
   115  
   116  // Check is part of the util.Flag interface.
   117  func (w *Worker) Check() bool {
   118  	return w.config.Result(w.life)
   119  }
   120  
   121  func (w *Worker) loop() error {
   122  	watcher, err := w.config.Facade.Watch(w.config.Entity)
   123  	if err != nil {
   124  		return errors.Trace(err)
   125  	}
   126  	if err := w.catacomb.Add(watcher); err != nil {
   127  		return errors.Trace(err)
   128  	}
   129  	for {
   130  		select {
   131  		case <-w.catacomb.Dying():
   132  			return w.catacomb.ErrDying()
   133  		case <-watcher.Changes():
   134  			life, err := w.config.Facade.Life(w.config.Entity)
   135  			if err != nil {
   136  				return errors.Trace(err)
   137  			}
   138  			if w.config.Result(life) != w.Check() {
   139  				return ErrValueChanged
   140  			}
   141  		}
   142  	}
   143  }