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

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package migrationflag
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/utils"
     9  	"gopkg.in/juju/worker.v1/catacomb"
    10  
    11  	"github.com/juju/juju/core/migration"
    12  	"github.com/juju/juju/core/watcher"
    13  )
    14  
    15  // ErrChanged indicates that a Worker has stopped because its
    16  // Check result is no longer valid.
    17  var ErrChanged = errors.New("migration flag value changed")
    18  
    19  // Facade exposes controller functionality required by a Worker.
    20  type Facade interface {
    21  	Watch(uuid string) (watcher.NotifyWatcher, error)
    22  	Phase(uuid string) (migration.Phase, error)
    23  }
    24  
    25  // Predicate defines a predicate.
    26  type Predicate func(migration.Phase) bool
    27  
    28  // IsTerminal returns true when the given phase means a migration has
    29  // finished (successfully or otherwise).
    30  func IsTerminal(phase migration.Phase) bool {
    31  	return phase.IsTerminal()
    32  }
    33  
    34  // Config holds the dependencies and configuration for a Worker.
    35  type Config struct {
    36  	Facade Facade
    37  	Model  string
    38  	Check  Predicate
    39  }
    40  
    41  // Validate returns an error if the config cannot be expected to
    42  // drive a functional Worker.
    43  func (config Config) Validate() error {
    44  	if config.Facade == nil {
    45  		return errors.NotValidf("nil Facade")
    46  	}
    47  	if !utils.IsValidUUIDString(config.Model) {
    48  		return errors.NotValidf("Model %q", config.Model)
    49  	}
    50  	if config.Check == nil {
    51  		return errors.NotValidf("nil Check")
    52  	}
    53  	return nil
    54  }
    55  
    56  // New returns a Worker that tracks the result of the configured
    57  // Check on the Model's migration phase, as exposed by the Facade.
    58  func New(config Config) (*Worker, error) {
    59  	if err := config.Validate(); err != nil {
    60  		return nil, errors.Trace(err)
    61  	}
    62  	phase, err := config.Facade.Phase(config.Model)
    63  	if err != nil {
    64  		return nil, errors.Trace(err)
    65  	}
    66  
    67  	w := &Worker{
    68  		config: config,
    69  		phase:  phase,
    70  	}
    71  	err = catacomb.Invoke(catacomb.Plan{
    72  		Site: &w.catacomb,
    73  		Work: w.loop,
    74  	})
    75  	if err != nil {
    76  		return nil, errors.Trace(err)
    77  	}
    78  	return w, nil
    79  }
    80  
    81  // Worker implements worker.Worker and util.Flag, and exits
    82  // with ErrChanged whenever the result of its configured Check of
    83  // the Model's migration phase changes.
    84  type Worker struct {
    85  	catacomb catacomb.Catacomb
    86  	config   Config
    87  	phase    migration.Phase
    88  }
    89  
    90  // Kill is part of the worker.Worker interface.
    91  func (w *Worker) Kill() {
    92  	w.catacomb.Kill(nil)
    93  }
    94  
    95  // Wait is part of the worker.Worker interface.
    96  func (w *Worker) Wait() error {
    97  	return w.catacomb.Wait()
    98  }
    99  
   100  // Check is part of the util.Flag interface.
   101  func (w *Worker) Check() bool {
   102  	return w.config.Check(w.phase)
   103  }
   104  
   105  func (w *Worker) loop() error {
   106  	model := w.config.Model
   107  	facade := w.config.Facade
   108  	watcher, err := facade.Watch(model)
   109  	if err != nil {
   110  		return errors.Trace(err)
   111  	}
   112  	if err := w.catacomb.Add(watcher); err != nil {
   113  		return errors.Trace(err)
   114  	}
   115  	for {
   116  		select {
   117  		case <-w.catacomb.Dying():
   118  			return w.catacomb.ErrDying()
   119  		case <-watcher.Changes():
   120  			phase, err := facade.Phase(model)
   121  			if err != nil {
   122  				return errors.Trace(err)
   123  			}
   124  			if w.Check() != w.config.Check(phase) {
   125  				return ErrChanged
   126  			}
   127  		}
   128  	}
   129  }