6
"launchpad.net/juju-core/environs"
7
"launchpad.net/juju-core/environs/config"
8
"launchpad.net/juju-core/log"
9
"launchpad.net/juju-core/state"
10
"launchpad.net/juju-core/state/api"
11
"launchpad.net/juju-core/state/api/params"
12
"launchpad.net/juju-core/state/watcher"
13
"launchpad.net/juju-core/utils"
14
"launchpad.net/juju-core/worker"
19
// Provisioner represents a running provisioning worker.
20
type Provisioner struct {
22
machineId string // Which machine runs the provisioner.
25
environ environs.Environ
28
// machine.Id => environs.Instance
29
instances map[string]environs.Instance
30
// instance.Id => machine id
31
machines map[state.InstanceId]string
36
type configObserver struct {
38
observer chan<- *config.Config
41
// nofity notifies the observer of a configuration change.
42
func (o *configObserver) notify(cfg *config.Config) {
44
if o.observer != nil {
50
// NewProvisioner returns a new Provisioner. When new machines
51
// are added to the state, it allocates instances from the environment
52
// and allocates them to the new machines.
53
func NewProvisioner(st *state.State, machineId string) *Provisioner {
57
instances: make(map[string]environs.Instance),
58
machines: make(map[state.InstanceId]string),
67
func (p *Provisioner) loop() error {
68
environWatcher := p.st.WatchEnvironConfig()
69
defer watcher.Stop(environWatcher, &p.tomb)
72
p.environ, err = worker.WaitForEnviron(environWatcher, p.tomb.Dying())
77
// Get a new StateInfo from the environment: the one used to
78
// launch the agent may refer to localhost, which will be
79
// unhelpful when attempting to run an agent on a new machine.
80
if p.stateInfo, p.apiInfo, err = p.environ.StateInfo(); err != nil {
84
// Call processMachines to stop any unknown instances before watching machines.
85
if err := p.processMachines(nil); err != nil {
89
// Start responding to changes in machines, and to any further updates
90
// to the environment config.
91
machinesWatcher := p.st.WatchMachines()
92
defer watcher.Stop(machinesWatcher, &p.tomb)
95
case <-p.tomb.Dying():
97
case cfg, ok := <-environWatcher.Changes():
99
return watcher.MustErr(environWatcher)
101
if err := p.setConfig(cfg); err != nil {
102
log.Errorf("worker/provisioner: loaded invalid environment configuration: %v", err)
104
case ids, ok := <-machinesWatcher.Changes():
106
return watcher.MustErr(machinesWatcher)
108
// TODO(dfc; lp:1042717) fire process machines periodically to shut down unknown
110
if err := p.processMachines(ids); err != nil {
118
// setConfig updates the environment configuration and notifies
119
// the config observer.
120
func (p *Provisioner) setConfig(config *config.Config) error {
121
if err := p.environ.SetConfig(config); err != nil {
124
p.configObserver.notify(config)
128
// Err returns the reason why the Provisioner has stopped or tomb.ErrStillAlive
129
// when it is still alive.
130
func (p *Provisioner) Err() (reason error) {
134
// Wait waits for the Provisioner to exit.
135
func (p *Provisioner) Wait() error {
139
func (p *Provisioner) String() string {
140
return "provisioning worker"
143
// Stop stops the Provisioner and returns any error encountered while
145
func (p *Provisioner) Stop() error {
150
func (p *Provisioner) processMachines(ids []string) error {
151
// Find machines without an instance id or that are dead
152
pending, dead, err := p.pendingOrDead(ids)
157
// Find running instances that have no machines associated
158
unknown, err := p.findUnknownInstances()
163
// Stop all machines that are dead
164
stopping, err := p.instancesForMachines(dead)
169
// It's important that we stop unknown instances before starting
170
// pending ones, because if we start an instance and then fail to
171
// set its InstanceId on the machine we don't want to start a new
172
// instance for the same machine ID.
173
if err := p.stopInstances(append(stopping, unknown...)); err != nil {
177
// Start an instance for the pending ones
178
return p.startMachines(pending)
181
// findUnknownInstances finds instances which are not associated with a machine.
182
func (p *Provisioner) findUnknownInstances() ([]environs.Instance, error) {
183
all, err := p.environ.AllInstances()
187
instances := make(map[state.InstanceId]environs.Instance)
188
for _, i := range all {
189
instances[i.Id()] = i
191
// TODO(dfc) this is very inefficient.
192
machines, err := p.st.AllMachines()
196
for _, m := range machines {
197
if instId, ok := m.InstanceId(); ok {
198
delete(instances, instId)
201
var unknown []environs.Instance
202
for _, i := range instances {
203
unknown = append(unknown, i)
208
// pendingOrDead looks up machines with ids and retuns those that do not
209
// have an instance id assigned yet, and also those that are dead.
210
func (p *Provisioner) pendingOrDead(ids []string) (pending, dead []*state.Machine, err error) {
211
// TODO(niemeyer): ms, err := st.Machines(alive)
212
for _, id := range ids {
213
m, err := p.st.Machine(id)
214
if state.IsNotFound(err) {
215
log.Infof("worker/provisioner: machine %q not found in state", m)
223
if _, ok := m.InstanceId(); ok {
226
log.Infof("worker/provisioner: killing dying, unprovisioned machine %q", m)
227
if err := m.EnsureDead(); err != nil {
232
dead = append(dead, m)
233
log.Infof("worker/provisioner: removing dead machine %q", m)
234
if err := m.Remove(); err != nil {
239
if instId, hasInstId := m.InstanceId(); !hasInstId {
240
status, _, err := m.Status()
242
log.Infof("worker/provisioner: cannot get machine %q status: %v", m, err)
245
if status == params.StatusPending {
246
pending = append(pending, m)
247
log.Infof("worker/provisioner: found machine %q pending provisioning", m)
251
log.Infof("worker/provisioner: machine %v already started as instance %q", m, instId)
257
func (p *Provisioner) startMachines(machines []*state.Machine) error {
258
for _, m := range machines {
259
if err := p.startMachine(m); err != nil {
260
return fmt.Errorf("cannot start machine %v: %v", m, err)
266
func (p *Provisioner) startMachine(m *state.Machine) error {
267
// TODO(dfc) the state.Info passed to environ.StartInstance remains contentious
268
// however as the PA only knows one state.Info, and that info is used by MAs and
269
// UAs to locate the state for this environment, it is logical to use the same
270
// state.Info as the PA.
271
stateInfo, apiInfo, err := p.setupAuthentication(m)
275
cons, err := m.Constraints()
279
// Generate a unique nonce for the new instance.
280
uuid, err := utils.NewUUID()
284
// Generated nonce has the format: "machine-#:UUID". The first
285
// part is a badge, specifying the tag of the machine the provisioner
286
// is running on, while the second part is a random UUID.
287
nonce := fmt.Sprintf("%s:%s", state.MachineTag(p.machineId), uuid.String())
288
inst, err := p.environ.StartInstance(m.Id(), nonce, m.Series(), cons, stateInfo, apiInfo)
290
// Set the state to error, so the machine will be skipped next
291
// time until the error is resolved, but don't return an
292
// error; just keep going with the other machines.
293
log.Errorf("worker/provisioner: cannot start instance for machine %q: %v", m, err)
294
if err1 := m.SetStatus(params.StatusError, err.Error()); err1 != nil {
295
// Something is wrong with this machine, better report it back.
296
log.Errorf("worker/provisioner: cannot set error status for machine %q: %v", m, err1)
301
if err := m.SetProvisioned(inst.Id(), nonce); err != nil {
302
// The machine is started, but we can't record the mapping in
303
// state. It'll keep running while we fail out and restart,
304
// but will then be detected by findUnknownInstances and
307
// TODO(dimitern) Stop the instance right away here.
309
// Multiple instantiations of a given machine (with the same
310
// machine ID) cannot coexist, because findUnknownInstances is
311
// called before startMachines. However, if the first machine
312
// had started to do work before being replaced, we may
313
// encounter surprising problems.
316
// populate the local cache
317
p.instances[m.Id()] = inst
318
p.machines[inst.Id()] = m.Id()
319
log.Noticef("worker/provisioner: started machine %s as instance %s", m, inst.Id())
323
func (p *Provisioner) setupAuthentication(m *state.Machine) (*state.Info, *api.Info, error) {
324
password, err := utils.RandomPassword()
326
return nil, nil, fmt.Errorf("cannot make password for machine %v: %v", m, err)
328
if err := m.SetMongoPassword(password); err != nil {
329
return nil, nil, fmt.Errorf("cannot set password for machine %v: %v", m, err)
331
stateInfo := *p.stateInfo
332
stateInfo.Tag = m.Tag()
333
stateInfo.Password = password
334
apiInfo := *p.apiInfo
335
apiInfo.Tag = m.Tag()
336
apiInfo.Password = password
337
return &stateInfo, &apiInfo, nil
340
func (p *Provisioner) stopInstances(instances []environs.Instance) error {
341
// Although calling StopInstance with an empty slice should produce no change in the
342
// provider, environs like dummy do not consider this a noop.
343
if len(instances) == 0 {
346
if err := p.environ.StopInstances(instances); err != nil {
351
for _, i := range instances {
352
if id, ok := p.machines[i.Id()]; ok {
353
delete(p.machines, i.Id())
354
delete(p.instances, id)
360
var errNotProvisioned = errors.New("machine has no instance id set")
362
// instanceForMachine returns the environs.Instance that represents this machine's instance.
363
func (p *Provisioner) instanceForMachine(m *state.Machine) (environs.Instance, error) {
364
inst, ok := p.instances[m.Id()]
368
instId, ok := m.InstanceId()
370
return nil, errNotProvisioned
372
// TODO(dfc): Ask for all instances at once.
373
insts, err := p.environ.Instances([]state.InstanceId{instId})
381
// instancesForMachines returns a list of environs.Instance that represent
382
// the list of machines running in the provider. Missing machines are
383
// omitted from the list.
384
func (p *Provisioner) instancesForMachines(ms []*state.Machine) ([]environs.Instance, error) {
385
var insts []environs.Instance
386
for _, m := range ms {
387
switch inst, err := p.instanceForMachine(m); err {
389
insts = append(insts, inst)
390
case errNotProvisioned, environs.ErrNoInstances: