github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/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 a worker which 21 // needs to run on a per environment basis. It takes a function which will 22 // be called to start a worker for a new environment. This worker 23 // will be killed when an environment goes away. 24 func NewEnvWorkerManager( 25 st InitialState, 26 startEnvWorker func(InitialState, *state.State) (worker.Worker, error), 27 ) worker.Worker { 28 m := &envWorkerManager{ 29 st: st, 30 startEnvWorker: startEnvWorker, 31 } 32 m.runner = worker.NewRunner(cmdutil.IsFatal, cmdutil.MoreImportant) 33 go func() { 34 defer m.tomb.Done() 35 m.tomb.Kill(m.loop()) 36 }() 37 return m 38 } 39 40 // InitialState defines the State functionality used by 41 // envWorkerManager and/or could be useful to startEnvWorker 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 startEnvWorker func(InitialState, *state.State) (worker.Worker, 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 go func() { 71 // When the runner stops, make sure we stop the envWorker as well 72 m.tomb.Kill(m.runner.Wait()) 73 }() 74 defer func() { 75 // When we return, make sure that we kill 76 // the runner and wait for it. 77 m.runner.Kill() 78 m.tomb.Kill(m.runner.Wait()) 79 }() 80 w := m.st.WatchEnvironments() 81 defer w.Stop() 82 for { 83 select { 84 case uuids := <-w.Changes(): 85 // One or more environments have changed. 86 for _, uuid := range uuids { 87 if err := m.envHasChanged(uuid); err != nil { 88 return errors.Trace(err) 89 } 90 } 91 case <-m.tomb.Dying(): 92 return tomb.ErrDying 93 } 94 } 95 } 96 97 func (m *envWorkerManager) envHasChanged(uuid string) error { 98 envTag := names.NewEnvironTag(uuid) 99 envAlive, err := m.isEnvAlive(envTag) 100 if err != nil { 101 return errors.Trace(err) 102 } 103 if envAlive { 104 err = m.envIsAlive(envTag) 105 } else { 106 err = m.envIsDead(envTag) 107 } 108 return errors.Trace(err) 109 } 110 111 func (m *envWorkerManager) envIsAlive(envTag names.EnvironTag) error { 112 return m.runner.StartWorker(envTag.Id(), func() (worker.Worker, error) { 113 st, err := m.st.ForEnviron(envTag) 114 if err != nil { 115 return nil, errors.Annotatef(err, "failed to open state for environment %s", envTag.Id()) 116 } 117 closeState := func() { 118 err := st.Close() 119 if err != nil { 120 logger.Errorf("error closing state for env %s: %v", envTag.Id(), err) 121 } 122 } 123 124 envRunner, err := m.startEnvWorker(m.st, st) 125 if err != nil { 126 closeState() 127 return nil, errors.Trace(err) 128 } 129 130 // Close State when the runner for the environment is done. 131 go func() { 132 envRunner.Wait() 133 closeState() 134 }() 135 136 return envRunner, nil 137 }) 138 } 139 140 func (m *envWorkerManager) envIsDead(envTag names.EnvironTag) error { 141 err := m.runner.StopWorker(envTag.Id()) 142 return errors.Trace(err) 143 } 144 145 func (m *envWorkerManager) isEnvAlive(tag names.EnvironTag) (bool, error) { 146 env, err := m.st.GetEnvironment(tag) 147 if errors.IsNotFound(err) { 148 return false, nil 149 } else if err != nil { 150 return false, errors.Annotatef(err, "error loading environment %s", tag.Id()) 151 } 152 return env.Life() == state.Alive, nil 153 }