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 }