github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/worker/envworkermanager/envworkermanager.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package envworkermanager 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "github.com/juju/names" 10 "gopkg.in/mgo.v2" 11 "launchpad.net/tomb" 12 13 cmdutil "github.com/juju/juju/cmd/jujud/util" 14 "github.com/juju/juju/state" 15 "github.com/juju/juju/worker" 16 ) 17 18 var logger = loggo.GetLogger("juju.worker.envworkermanager") 19 20 // NewEnvWorkerManager returns a Worker which manages the workers which 21 // need to run on a per environment basis. It takes a function which will 22 // be called to start workers for a new environment. These workers 23 // will be killed when an environment goes away. 24 func NewEnvWorkerManager( 25 st InitialState, 26 startEnvWorkers func(InitialState, *state.State) (worker.Runner, error), 27 ) worker.Worker { 28 m := &envWorkerManager{ 29 st: st, 30 startEnvWorkers: startEnvWorkers, 31 } 32 m.runner = worker.NewRunner(cmdutil.IsFatal, cmdutil.MoreImportant) 33 go func() { 34 m.tomb.Kill(m.loop()) 35 m.tomb.Done() 36 }() 37 return m 38 } 39 40 // InitialState defines the State functionality used by 41 // envWorkerManager and/or could be useful to startEnvWorkers 42 // funcs. It mainly exists to support testing. 43 type InitialState interface { 44 WatchEnvironments() state.StringsWatcher 45 ForEnviron(names.EnvironTag) (*state.State, error) 46 GetEnvironment(names.EnvironTag) (*state.Environment, error) 47 EnvironUUID() string 48 Machine(string) (*state.Machine, error) 49 MongoSession() *mgo.Session 50 } 51 52 type envWorkerManager struct { 53 runner worker.Runner 54 tomb tomb.Tomb 55 st InitialState 56 startEnvWorkers func(InitialState, *state.State) (worker.Runner, error) 57 } 58 59 // Kill satisfies the Worker interface. 60 func (m *envWorkerManager) Kill() { 61 m.tomb.Kill(nil) 62 } 63 64 // Wait satisfies the Worker interface. 65 func (m *envWorkerManager) Wait() error { 66 return m.tomb.Wait() 67 } 68 69 func (m *envWorkerManager) loop() error { 70 w := m.st.WatchEnvironments() 71 defer w.Stop() 72 for { 73 select { 74 case uuids := <-w.Changes(): 75 // One or more environments have changed. 76 for _, uuid := range uuids { 77 if err := m.envHasChanged(uuid); err != nil { 78 return errors.Trace(err) 79 } 80 } 81 case <-m.runner.Dying(): 82 // The master runner is dying: wait for it to finish dying 83 // and report its error (if any). 84 return m.runner.Wait() 85 case <-m.tomb.Dying(): 86 // The envWorkerManager has been asked to die: kill the 87 // master runner and stop. 88 m.runner.Kill() 89 m.runner.Wait() 90 return tomb.ErrDying 91 } 92 } 93 } 94 95 func (m *envWorkerManager) envHasChanged(uuid string) error { 96 envTag := names.NewEnvironTag(uuid) 97 envAlive, err := m.isEnvAlive(envTag) 98 if err != nil { 99 return errors.Trace(err) 100 } 101 if envAlive { 102 err = m.envIsAlive(envTag) 103 } else { 104 err = m.envIsDead(envTag) 105 } 106 return errors.Trace(err) 107 } 108 109 func (m *envWorkerManager) envIsAlive(envTag names.EnvironTag) error { 110 return m.runner.StartWorker(envTag.Id(), func() (worker.Worker, error) { 111 st, err := m.st.ForEnviron(envTag) 112 if err != nil { 113 return nil, errors.Annotatef(err, "failed to open state for environment %s", envTag.Id()) 114 } 115 closeState := func() { 116 err := st.Close() 117 if err != nil { 118 logger.Errorf("error closing state for env %s: %v", envTag.Id(), err) 119 } 120 } 121 122 envRunner, err := m.startEnvWorkers(m.st, st) 123 if err != nil { 124 closeState() 125 return nil, errors.Trace(err) 126 } 127 128 // Close State when the runner for the environment is done. 129 go func() { 130 envRunner.Wait() 131 closeState() 132 }() 133 134 return envRunner, nil 135 }) 136 } 137 138 func (m *envWorkerManager) envIsDead(envTag names.EnvironTag) error { 139 err := m.runner.StopWorker(envTag.Id()) 140 return errors.Trace(err) 141 } 142 143 func (m *envWorkerManager) isEnvAlive(tag names.EnvironTag) (bool, error) { 144 env, err := m.st.GetEnvironment(tag) 145 if errors.IsNotFound(err) { 146 return false, nil 147 } else if err != nil { 148 return false, errors.Annotatef(err, "error loading environment %s", tag.Id()) 149 } 150 return env.Life() == state.Alive, nil 151 }