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

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package modelworkermanager
     5  
     6  import (
     7  	"time"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/loggo"
    11  	"gopkg.in/juju/worker.v1"
    12  	"gopkg.in/juju/worker.v1/catacomb"
    13  
    14  	"github.com/juju/juju/state"
    15  )
    16  
    17  var logger = loggo.GetLogger("juju.workers.modelworkermanager")
    18  
    19  // ModelWatcher provides an interface for watching the additiona and
    20  // removal of models.
    21  type ModelWatcher interface {
    22  	WatchModels() state.StringsWatcher
    23  }
    24  
    25  // ModelGetter provides an interface for getting models by UUID.
    26  // Once a model is no longer required, the returned function must
    27  // be called to dispose of the model.
    28  type ModelGetter interface {
    29  	Model(modelUUID string) (Model, func(), error)
    30  }
    31  
    32  // Model represents a model.
    33  type Model interface {
    34  	MigrationMode() state.MigrationMode
    35  	Type() state.ModelType
    36  }
    37  
    38  // NewModelWorkerFunc should return a worker responsible for running
    39  // all a model's required workers; and for returning nil when there's
    40  // no more model to manage.
    41  type NewModelWorkerFunc func(modelUUID string, modelType state.ModelType) (worker.Worker, error)
    42  
    43  // Config holds the dependencies and configuration necessary to run
    44  // a model worker manager.
    45  type Config struct {
    46  	ModelWatcher   ModelWatcher
    47  	ModelGetter    ModelGetter
    48  	NewModelWorker NewModelWorkerFunc
    49  	ErrorDelay     time.Duration
    50  }
    51  
    52  // Validate returns an error if config cannot be expected to drive
    53  // a functional model worker manager.
    54  func (config Config) Validate() error {
    55  	if config.ModelWatcher == nil {
    56  		return errors.NotValidf("nil ModelWatcher")
    57  	}
    58  	if config.ModelGetter == nil {
    59  		return errors.NotValidf("nil ModelGetter")
    60  	}
    61  	if config.NewModelWorker == nil {
    62  		return errors.NotValidf("nil NewModelWorker")
    63  	}
    64  	if config.ErrorDelay <= 0 {
    65  		return errors.NotValidf("non-positive ErrorDelay")
    66  	}
    67  	return nil
    68  }
    69  
    70  func New(config Config) (worker.Worker, error) {
    71  	if err := config.Validate(); err != nil {
    72  		return nil, errors.Trace(err)
    73  	}
    74  	m := &modelWorkerManager{
    75  		config: config,
    76  	}
    77  
    78  	err := catacomb.Invoke(catacomb.Plan{
    79  		Site: &m.catacomb,
    80  		Work: m.loop,
    81  	})
    82  	if err != nil {
    83  		return nil, errors.Trace(err)
    84  	}
    85  	return m, nil
    86  }
    87  
    88  type modelWorkerManager struct {
    89  	catacomb catacomb.Catacomb
    90  	config   Config
    91  	runner   *worker.Runner
    92  }
    93  
    94  // Kill satisfies the Worker interface.
    95  func (m *modelWorkerManager) Kill() {
    96  	m.catacomb.Kill(nil)
    97  }
    98  
    99  // Wait satisfies the Worker interface.
   100  func (m *modelWorkerManager) Wait() error {
   101  	return m.catacomb.Wait()
   102  }
   103  
   104  func (m *modelWorkerManager) loop() error {
   105  	m.runner = worker.NewRunner(worker.RunnerParams{
   106  		IsFatal:       neverFatal,
   107  		MoreImportant: neverImportant,
   108  		RestartDelay:  m.config.ErrorDelay,
   109  	})
   110  	if err := m.catacomb.Add(m.runner); err != nil {
   111  		return errors.Trace(err)
   112  	}
   113  	watcher := m.config.ModelWatcher.WatchModels()
   114  	if err := m.catacomb.Add(watcher); err != nil {
   115  		return errors.Trace(err)
   116  	}
   117  
   118  	modelChanged := func(modelUUID string) error {
   119  		model, release, err := m.config.ModelGetter.Model(modelUUID)
   120  		if errors.IsNotFound(err) {
   121  			// Model was removed, ignore it.
   122  			return nil
   123  		} else if err != nil {
   124  			return errors.Trace(err)
   125  		}
   126  		defer release()
   127  
   128  		if !isModelActive(model) {
   129  			// Ignore this model until it's activated - we
   130  			// never want to run workers for an importing
   131  			// model.
   132  			// https://bugs.launchpad.net/juju/+bug/1646310
   133  			return nil
   134  		}
   135  		return errors.Trace(m.ensure(modelUUID, model.Type()))
   136  	}
   137  
   138  	for {
   139  		select {
   140  		case <-m.catacomb.Dying():
   141  			return m.catacomb.ErrDying()
   142  		case uuids, ok := <-watcher.Changes():
   143  			if !ok {
   144  				return errors.New("changes stopped")
   145  			}
   146  			for _, modelUUID := range uuids {
   147  				if err := modelChanged(modelUUID); err != nil {
   148  					return errors.Trace(err)
   149  				}
   150  			}
   151  		}
   152  	}
   153  }
   154  
   155  func (m *modelWorkerManager) ensure(modelUUID string, modelType state.ModelType) error {
   156  	starter := m.starter(modelUUID, modelType)
   157  	if err := m.runner.StartWorker(modelUUID, starter); err != nil {
   158  		return errors.Trace(err)
   159  	}
   160  	return nil
   161  }
   162  
   163  func (m *modelWorkerManager) starter(modelUUID string, modelType state.ModelType) func() (worker.Worker, error) {
   164  	return func() (worker.Worker, error) {
   165  		logger.Debugf("starting workers for model %q", modelUUID)
   166  		worker, err := m.config.NewModelWorker(modelUUID, modelType)
   167  		if err != nil {
   168  			return nil, errors.Annotatef(err, "cannot manage model %q", modelUUID)
   169  		}
   170  		return worker, nil
   171  	}
   172  }
   173  
   174  func neverFatal(error) bool {
   175  	return false
   176  }
   177  
   178  func neverImportant(error, error) bool {
   179  	return false
   180  }
   181  
   182  func isModelActive(m Model) bool {
   183  	return m.MigrationMode() != state.MigrationModeImporting
   184  }
   185  
   186  // Report shows up in the dependency engine report.
   187  func (m *modelWorkerManager) Report() map[string]interface{} {
   188  	return m.runner.Report()
   189  }