github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/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/utils/set"
    11  
    12  	"github.com/juju/juju/state"
    13  	"github.com/juju/juju/worker"
    14  	"github.com/juju/juju/worker/catacomb"
    15  )
    16  
    17  // Backend defines the State functionality used by the manager worker.
    18  type Backend interface {
    19  	WatchModels() state.StringsWatcher
    20  }
    21  
    22  // NewWorkerFunc should return a worker responsible for running
    23  // all a model's required workers; and for returning nil when
    24  // there's no more model to manage.
    25  type NewWorkerFunc func(modelUUID string) (worker.Worker, error)
    26  
    27  // Config holds the dependencies and configuration necessary to run
    28  // a model worker manager.
    29  type Config struct {
    30  	Backend    Backend
    31  	NewWorker  NewWorkerFunc
    32  	ErrorDelay time.Duration
    33  }
    34  
    35  // Validate returns an error if config cannot be expected to drive
    36  // a functional model worker manager.
    37  func (config Config) Validate() error {
    38  	if config.Backend == nil {
    39  		return errors.NotValidf("nil Backend")
    40  	}
    41  	if config.NewWorker == nil {
    42  		return errors.NotValidf("nil NewWorker")
    43  	}
    44  	if config.ErrorDelay <= 0 {
    45  		return errors.NotValidf("non-positive ErrorDelay")
    46  	}
    47  	return nil
    48  }
    49  
    50  func New(config Config) (worker.Worker, error) {
    51  	if err := config.Validate(); err != nil {
    52  		return nil, errors.Trace(err)
    53  	}
    54  	m := &modelWorkerManager{
    55  		config:  config,
    56  		started: set.NewStrings(),
    57  	}
    58  
    59  	err := catacomb.Invoke(catacomb.Plan{
    60  		Site: &m.catacomb,
    61  		Work: m.loop,
    62  	})
    63  	if err != nil {
    64  		return nil, errors.Trace(err)
    65  	}
    66  	return m, nil
    67  }
    68  
    69  type modelWorkerManager struct {
    70  	catacomb catacomb.Catacomb
    71  	config   Config
    72  	runner   worker.Runner
    73  	started  set.Strings
    74  }
    75  
    76  // Kill satisfies the Worker interface.
    77  func (m *modelWorkerManager) Kill() {
    78  	m.catacomb.Kill(nil)
    79  }
    80  
    81  // Wait satisfies the Worker interface.
    82  func (m *modelWorkerManager) Wait() error {
    83  	return m.catacomb.Wait()
    84  }
    85  
    86  func (m *modelWorkerManager) loop() error {
    87  	m.runner = worker.NewRunner(
    88  		neverFatal, neverImportant, m.config.ErrorDelay,
    89  	)
    90  	if err := m.catacomb.Add(m.runner); err != nil {
    91  		return errors.Trace(err)
    92  	}
    93  	watcher := m.config.Backend.WatchModels()
    94  	if err := m.catacomb.Add(watcher); err != nil {
    95  		return errors.Trace(err)
    96  	}
    97  
    98  	for {
    99  		select {
   100  		case <-m.catacomb.Dying():
   101  			return m.catacomb.ErrDying()
   102  		case uuids, ok := <-watcher.Changes():
   103  			if !ok {
   104  				return errors.New("changes stopped")
   105  			}
   106  			for _, uuid := range uuids {
   107  				if err := m.ensure(uuid); err != nil {
   108  					return errors.Trace(err)
   109  				}
   110  			}
   111  		}
   112  	}
   113  }
   114  
   115  func (m *modelWorkerManager) ensure(uuid string) error {
   116  	if m.started.Contains(uuid) {
   117  		// A second StartWorker for a given ID is mostly
   118  		// harmless -- it will probably be ignored, but
   119  		// might work if the previous worker has already
   120  		// stopped without error. Neither situation will
   121  		// hurt us directly, but we prefer to eliminate
   122  		// the messy uncertainty.
   123  		return nil
   124  	}
   125  	starter := m.starter(uuid)
   126  	if err := m.runner.StartWorker(uuid, starter); err != nil {
   127  		return errors.Trace(err)
   128  	}
   129  	m.started.Add(uuid)
   130  	return nil
   131  }
   132  
   133  func (m *modelWorkerManager) starter(uuid string) func() (worker.Worker, error) {
   134  	return func() (worker.Worker, error) {
   135  		worker, err := m.config.NewWorker(uuid)
   136  		if err != nil {
   137  			return nil, errors.Annotatef(err, "cannot manage model %q", uuid)
   138  		}
   139  		return worker, nil
   140  	}
   141  }
   142  
   143  func neverFatal(error) bool {
   144  	return false
   145  }
   146  
   147  func neverImportant(error, error) bool {
   148  	return false
   149  }