launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/worker/provisioner/provisioner.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package provisioner 5 6 import ( 7 "sync" 8 9 "github.com/loggo/loggo" 10 "launchpad.net/errgo/errors" 11 "launchpad.net/tomb" 12 13 "launchpad.net/juju-core/agent" 14 "launchpad.net/juju-core/environs" 15 "launchpad.net/juju-core/environs/config" 16 "launchpad.net/juju-core/instance" 17 apiprovisioner "launchpad.net/juju-core/state/api/provisioner" 18 "launchpad.net/juju-core/state/watcher" 19 "launchpad.net/juju-core/worker" 20 ) 21 22 var logger = loggo.GetLogger("juju.provisioner") 23 24 var mask = errors.Mask 25 26 // Ensure our structs implement the required Provisioner interface. 27 var _ Provisioner = (*environProvisioner)(nil) 28 var _ Provisioner = (*containerProvisioner)(nil) 29 30 // Provisioner represents a running provisioner worker. 31 type Provisioner interface { 32 worker.Worker 33 Stop() error 34 getWatcher() (Watcher, error) 35 } 36 37 // environProvisioner represents a running provisioning worker for machine nodes 38 // belonging to an environment. 39 type environProvisioner struct { 40 provisioner 41 environ environs.Environ 42 configObserver 43 } 44 45 // containerProvisioner represents a running provisioning worker for containers 46 // hosted on a machine. 47 type containerProvisioner struct { 48 provisioner 49 containerType instance.ContainerType 50 machine *apiprovisioner.Machine 51 } 52 53 // provisioner providers common behaviour for a running provisioning worker. 54 type provisioner struct { 55 Provisioner 56 st *apiprovisioner.State 57 agentConfig agent.Config 58 broker environs.InstanceBroker 59 tomb tomb.Tomb 60 } 61 62 // configObserver is implemented so that tests can see 63 // when the environment configuration changes. 64 type configObserver struct { 65 sync.Mutex 66 observer chan<- *config.Config 67 } 68 69 // notify notifies the observer of a configuration change. 70 func (o *configObserver) notify(cfg *config.Config) { 71 o.Lock() 72 if o.observer != nil { 73 o.observer <- cfg 74 } 75 o.Unlock() 76 } 77 78 // Err returns the reason why the provisioner has stopped or tomb.ErrStillAlive 79 // when it is still alive. 80 func (p *provisioner) Err() (reason error) { 81 return p.tomb.Err() 82 } 83 84 // Kill implements worker.Worker.Kill. 85 func (p *provisioner) Kill() { 86 p.tomb.Kill(nil) 87 } 88 89 // Wait implements worker.Worker.Wait. 90 func (p *provisioner) Wait() error { 91 return p.tomb.Wait() 92 } 93 94 // Stop stops the provisioner and returns any error encountered while 95 // provisioning. 96 func (p *provisioner) Stop() error { 97 p.tomb.Kill(nil) 98 return p.tomb.Wait() 99 } 100 101 // getStartTask creates a new worker for the provisioner, 102 func (p *provisioner) getStartTask(safeMode bool) (ProvisionerTask, error) { 103 auth, err := environs.NewAPIAuthenticator(p.st) 104 if err != nil { 105 return nil, mask(err) 106 } 107 108 // Start responding to changes in machines, and to any further updates 109 // to the environment config. 110 machineWatcher, err := p.getWatcher() 111 if err != nil { 112 return nil, mask(err) 113 } 114 task := NewProvisionerTask( 115 p.agentConfig.Tag(), 116 safeMode, 117 p.st, 118 machineWatcher, 119 p.broker, 120 auth) 121 return task, nil 122 } 123 124 // NewEnvironProvisioner returns a new Provisioner for an environment. 125 // When new machines are added to the state, it allocates instances 126 // from the environment and allocates them to the new machines. 127 func NewEnvironProvisioner(st *apiprovisioner.State, agentConfig agent.Config) Provisioner { 128 p := &environProvisioner{ 129 provisioner: provisioner{ 130 st: st, 131 agentConfig: agentConfig, 132 }, 133 } 134 p.Provisioner = p 135 logger.Tracef("Starting environ provisioner for %q", p.agentConfig.Tag()) 136 go func() { 137 defer p.tomb.Done() 138 killTomb(&p.tomb, p.loop()) 139 }() 140 return p 141 } 142 143 func killTomb(t *tomb.Tomb, err error) { 144 if errors.Cause(err) == tomb.ErrDying { 145 return 146 } 147 t.Kill(err) 148 } 149 150 func (p *environProvisioner) loop() error { 151 var environConfigChanges <-chan struct{} 152 environWatcher, err := p.st.WatchForEnvironConfigChanges() 153 if err != nil { 154 return mask(err) 155 } 156 environConfigChanges = environWatcher.Changes() 157 defer watcher.Stop(environWatcher, &p.tomb) 158 159 p.environ, err = worker.WaitForEnviron(environWatcher, p.st, p.tomb.Dying()) 160 if err != nil { 161 return mask(err, errors.Is(tomb.ErrDying)) 162 } 163 p.broker = p.environ 164 165 safeMode := p.environ.Config().ProvisionerSafeMode() 166 task, err := p.getStartTask(safeMode) 167 if err != nil { 168 return mask(err) 169 } 170 defer watcher.Stop(task, &p.tomb) 171 172 for { 173 select { 174 case <-p.tomb.Dying(): 175 return tomb.ErrDying 176 case <-task.Dying(): 177 err := task.Err() 178 logger.Errorf("environ provisioner died: %v", err) 179 return err 180 case _, ok := <-environConfigChanges: 181 if !ok { 182 return watcher.MustErr(environWatcher) 183 } 184 environConfig, err := p.st.EnvironConfig() 185 if err != nil { 186 logger.Errorf("cannot load environment configuration: %v", err) 187 return err 188 } 189 if err := p.setConfig(environConfig); err != nil { 190 logger.Errorf("loaded invalid environment configuration: %v", err) 191 } 192 task.SetSafeMode(environConfig.ProvisionerSafeMode()) 193 } 194 } 195 } 196 197 func (p *environProvisioner) getWatcher() (Watcher, error) { 198 return p.st.WatchEnvironMachines() 199 } 200 201 // setConfig updates the environment configuration and notifies 202 // the config observer. 203 func (p *environProvisioner) setConfig(environConfig *config.Config) error { 204 if err := p.environ.SetConfig(environConfig); err != nil { 205 return mask(err) 206 } 207 p.configObserver.notify(environConfig) 208 return nil 209 } 210 211 // NewContainerProvisioner returns a new Provisioner. When new machines 212 // are added to the state, it allocates instances from the environment 213 // and allocates them to the new machines. 214 func NewContainerProvisioner(containerType instance.ContainerType, st *apiprovisioner.State, 215 agentConfig agent.Config, broker environs.InstanceBroker) Provisioner { 216 217 p := &containerProvisioner{ 218 provisioner: provisioner{ 219 st: st, 220 agentConfig: agentConfig, 221 broker: broker, 222 }, 223 containerType: containerType, 224 } 225 p.Provisioner = p 226 logger.Tracef("Starting %s provisioner for %q", p.containerType, p.agentConfig.Tag()) 227 go func() { 228 defer p.tomb.Done() 229 killTomb(&p.tomb, p.loop()) 230 }() 231 return p 232 } 233 234 func (p *containerProvisioner) loop() error { 235 task, err := p.getStartTask(false) 236 if err != nil { 237 return mask(err) 238 } 239 defer watcher.Stop(task, &p.tomb) 240 241 for { 242 select { 243 case <-p.tomb.Dying(): 244 return tomb.ErrDying 245 case <-task.Dying(): 246 err := task.Err() 247 logger.Errorf("%s provisioner died: %v", p.containerType, err) 248 return err 249 } 250 } 251 } 252 253 func (p *containerProvisioner) getMachine() (*apiprovisioner.Machine, error) { 254 if p.machine == nil { 255 var err error 256 if p.machine, err = p.st.Machine(p.agentConfig.Tag()); err != nil { 257 logger.Errorf("%s is not in state", p.agentConfig.Tag()) 258 return nil, err 259 } 260 } 261 return p.machine, nil 262 } 263 264 func (p *containerProvisioner) getWatcher() (Watcher, error) { 265 machine, err := p.getMachine() 266 if err != nil { 267 return nil, mask(err) 268 } 269 return machine.WatchContainers(p.containerType) 270 }