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 }