github.com/cloud-green/juju@v0.0.0-20151002100041-a00291338d3d/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/juju/errors" 10 "github.com/juju/loggo" 11 "github.com/juju/names" 12 "launchpad.net/tomb" 13 14 "github.com/juju/juju/agent" 15 apiprovisioner "github.com/juju/juju/api/provisioner" 16 apiwatcher "github.com/juju/juju/api/watcher" 17 "github.com/juju/juju/environmentserver/authentication" 18 "github.com/juju/juju/environs" 19 "github.com/juju/juju/environs/config" 20 "github.com/juju/juju/instance" 21 "github.com/juju/juju/state/watcher" 22 "github.com/juju/juju/utils" 23 "github.com/juju/juju/worker" 24 ) 25 26 var logger = loggo.GetLogger("juju.provisioner") 27 28 // Ensure our structs implement the required Provisioner interface. 29 var _ Provisioner = (*environProvisioner)(nil) 30 var _ Provisioner = (*containerProvisioner)(nil) 31 32 // Provisioner represents a running provisioner worker. 33 type Provisioner interface { 34 worker.Worker 35 Stop() error 36 getMachineWatcher() (apiwatcher.StringsWatcher, error) 37 getRetryWatcher() (apiwatcher.NotifyWatcher, error) 38 } 39 40 // environProvisioner represents a running provisioning worker for machine nodes 41 // belonging to an environment. 42 type environProvisioner struct { 43 provisioner 44 environ environs.Environ 45 configObserver 46 } 47 48 // containerProvisioner represents a running provisioning worker for containers 49 // hosted on a machine. 50 type containerProvisioner struct { 51 provisioner 52 containerType instance.ContainerType 53 machine *apiprovisioner.Machine 54 configObserver 55 } 56 57 // provisioner providers common behaviour for a running provisioning worker. 58 type provisioner struct { 59 Provisioner 60 st *apiprovisioner.State 61 agentConfig agent.Config 62 broker environs.InstanceBroker 63 toolsFinder ToolsFinder 64 tomb tomb.Tomb 65 } 66 67 // configObserver is implemented so that tests can see 68 // when the environment configuration changes. 69 type configObserver struct { 70 sync.Mutex 71 observer chan<- *config.Config 72 } 73 74 // notify notifies the observer of a configuration change. 75 func (o *configObserver) notify(cfg *config.Config) { 76 o.Lock() 77 if o.observer != nil { 78 o.observer <- cfg 79 } 80 o.Unlock() 81 } 82 83 // Err returns the reason why the provisioner has stopped or tomb.ErrStillAlive 84 // when it is still alive. 85 func (p *provisioner) Err() (reason error) { 86 return p.tomb.Err() 87 } 88 89 // Kill implements worker.Worker.Kill. 90 func (p *provisioner) Kill() { 91 p.tomb.Kill(nil) 92 } 93 94 // Wait implements worker.Worker.Wait. 95 func (p *provisioner) Wait() error { 96 return p.tomb.Wait() 97 } 98 99 // Stop stops the provisioner and returns any error encountered while 100 // provisioning. 101 func (p *provisioner) Stop() error { 102 p.tomb.Kill(nil) 103 return p.tomb.Wait() 104 } 105 106 // getToolsFinder returns a ToolsFinder for the provided State. 107 // This exists for mocking. 108 var getToolsFinder = func(st *apiprovisioner.State) ToolsFinder { 109 return st 110 } 111 112 // getStartTask creates a new worker for the provisioner, 113 func (p *provisioner) getStartTask(harvestMode config.HarvestMode) (ProvisionerTask, error) { 114 auth, err := authentication.NewAPIAuthenticator(p.st) 115 if err != nil { 116 return nil, err 117 } 118 // Start responding to changes in machines, and to any further updates 119 // to the environment config. 120 machineWatcher, err := p.getMachineWatcher() 121 if err != nil { 122 return nil, err 123 } 124 retryWatcher, err := p.getRetryWatcher() 125 if err != nil && !errors.IsNotImplemented(err) { 126 return nil, err 127 } 128 tag := p.agentConfig.Tag() 129 machineTag, ok := tag.(names.MachineTag) 130 if !ok { 131 errors.Errorf("expacted names.MachineTag, got %T", tag) 132 } 133 134 envCfg, err := p.st.EnvironConfig() 135 if err != nil { 136 return nil, errors.Annotate(err, "could not retrieve the environment config.") 137 } 138 139 secureServerConnection := false 140 if info, ok := p.agentConfig.StateServingInfo(); ok { 141 secureServerConnection = info.CAPrivateKey != "" 142 } 143 task := NewProvisionerTask( 144 machineTag, 145 harvestMode, 146 p.st, 147 p.toolsFinder, 148 machineWatcher, 149 retryWatcher, 150 p.broker, 151 auth, 152 envCfg.ImageStream(), 153 secureServerConnection, 154 ) 155 return task, nil 156 } 157 158 // NewEnvironProvisioner returns a new Provisioner for an environment. 159 // When new machines are added to the state, it allocates instances 160 // from the environment and allocates them to the new machines. 161 func NewEnvironProvisioner(st *apiprovisioner.State, agentConfig agent.Config) Provisioner { 162 p := &environProvisioner{ 163 provisioner: provisioner{ 164 st: st, 165 agentConfig: agentConfig, 166 toolsFinder: getToolsFinder(st), 167 }, 168 } 169 p.Provisioner = p 170 logger.Tracef("Starting environ provisioner for %q", p.agentConfig.Tag()) 171 go func() { 172 defer p.tomb.Done() 173 p.tomb.Kill(errors.Cause(p.loop())) 174 }() 175 return p 176 } 177 178 func (p *environProvisioner) loop() error { 179 var environConfigChanges <-chan struct{} 180 environWatcher, err := p.st.WatchForEnvironConfigChanges() 181 if err != nil { 182 return utils.LoggedErrorStack(errors.Trace(err)) 183 } 184 environConfigChanges = environWatcher.Changes() 185 defer watcher.Stop(environWatcher, &p.tomb) 186 187 p.environ, err = worker.WaitForEnviron(environWatcher, p.st, p.tomb.Dying()) 188 if err != nil { 189 return utils.LoggedErrorStack(errors.Trace(err)) 190 } 191 p.broker = p.environ 192 193 harvestMode := p.environ.Config().ProvisionerHarvestMode() 194 task, err := p.getStartTask(harvestMode) 195 if err != nil { 196 return utils.LoggedErrorStack(errors.Trace(err)) 197 } 198 defer watcher.Stop(task, &p.tomb) 199 200 for { 201 select { 202 case <-p.tomb.Dying(): 203 return tomb.ErrDying 204 case <-task.Dying(): 205 err := task.Err() 206 logger.Errorf("environ provisioner died: %v", err) 207 return err 208 case _, ok := <-environConfigChanges: 209 if !ok { 210 return watcher.EnsureErr(environWatcher) 211 } 212 environConfig, err := p.st.EnvironConfig() 213 if err != nil { 214 logger.Errorf("cannot load environment configuration: %v", err) 215 return err 216 } 217 if err := p.setConfig(environConfig); err != nil { 218 logger.Errorf("loaded invalid environment configuration: %v", err) 219 } 220 task.SetHarvestMode(environConfig.ProvisionerHarvestMode()) 221 } 222 } 223 } 224 225 func (p *environProvisioner) getMachineWatcher() (apiwatcher.StringsWatcher, error) { 226 return p.st.WatchEnvironMachines() 227 } 228 229 func (p *environProvisioner) getRetryWatcher() (apiwatcher.NotifyWatcher, error) { 230 return p.st.WatchMachineErrorRetry() 231 } 232 233 // setConfig updates the environment configuration and notifies 234 // the config observer. 235 func (p *environProvisioner) setConfig(environConfig *config.Config) error { 236 if err := p.environ.SetConfig(environConfig); err != nil { 237 return err 238 } 239 p.configObserver.notify(environConfig) 240 return nil 241 } 242 243 // NewContainerProvisioner returns a new Provisioner. When new machines 244 // are added to the state, it allocates instances from the environment 245 // and allocates them to the new machines. 246 func NewContainerProvisioner( 247 containerType instance.ContainerType, 248 st *apiprovisioner.State, 249 agentConfig agent.Config, 250 broker environs.InstanceBroker, 251 toolsFinder ToolsFinder, 252 ) Provisioner { 253 254 p := &containerProvisioner{ 255 provisioner: provisioner{ 256 st: st, 257 agentConfig: agentConfig, 258 broker: broker, 259 toolsFinder: toolsFinder, 260 }, 261 containerType: containerType, 262 } 263 p.Provisioner = p 264 logger.Tracef("Starting %s provisioner for %q", p.containerType, p.agentConfig.Tag()) 265 go func() { 266 defer p.tomb.Done() 267 p.tomb.Kill(p.loop()) 268 }() 269 return p 270 } 271 272 func (p *containerProvisioner) loop() error { 273 var environConfigChanges <-chan struct{} 274 environWatcher, err := p.st.WatchForEnvironConfigChanges() 275 if err != nil { 276 return err 277 } 278 environConfigChanges = environWatcher.Changes() 279 defer watcher.Stop(environWatcher, &p.tomb) 280 281 config, err := p.st.EnvironConfig() 282 if err != nil { 283 return err 284 } 285 harvestMode := config.ProvisionerHarvestMode() 286 287 task, err := p.getStartTask(harvestMode) 288 if err != nil { 289 return err 290 } 291 defer watcher.Stop(task, &p.tomb) 292 293 for { 294 select { 295 case <-p.tomb.Dying(): 296 return tomb.ErrDying 297 case <-task.Dying(): 298 err := task.Err() 299 logger.Errorf("%s provisioner died: %v", p.containerType, err) 300 return err 301 case _, ok := <-environConfigChanges: 302 if !ok { 303 return watcher.EnsureErr(environWatcher) 304 } 305 environConfig, err := p.st.EnvironConfig() 306 if err != nil { 307 logger.Errorf("cannot load environment configuration: %v", err) 308 return err 309 } 310 p.configObserver.notify(environConfig) 311 task.SetHarvestMode(environConfig.ProvisionerHarvestMode()) 312 } 313 } 314 } 315 316 func (p *containerProvisioner) getMachine() (*apiprovisioner.Machine, error) { 317 if p.machine == nil { 318 tag := p.agentConfig.Tag() 319 machineTag, ok := tag.(names.MachineTag) 320 if !ok { 321 return nil, errors.Errorf("expected names.MachineTag, got %T", tag) 322 } 323 var err error 324 if p.machine, err = p.st.Machine(machineTag); err != nil { 325 logger.Errorf("%s is not in state", machineTag) 326 return nil, err 327 } 328 } 329 return p.machine, nil 330 } 331 332 func (p *containerProvisioner) getMachineWatcher() (apiwatcher.StringsWatcher, error) { 333 machine, err := p.getMachine() 334 if err != nil { 335 return nil, err 336 } 337 return machine.WatchContainers(p.containerType) 338 } 339 340 func (p *containerProvisioner) getRetryWatcher() (apiwatcher.NotifyWatcher, error) { 341 return nil, errors.NotImplementedf("getRetryWatcher") 342 }