github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/worker/storageprovisioner/machines.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package storageprovisioner
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/names"
     9  	"launchpad.net/tomb"
    10  
    11  	"github.com/juju/juju/apiserver/params"
    12  	"github.com/juju/juju/instance"
    13  	"github.com/juju/juju/state/watcher"
    14  )
    15  
    16  // watchMachine starts a machine watcher if there is not already one for the
    17  // specified tag. The watcher will notify the worker when the machine changes,
    18  // for example when it is provisioned.
    19  func watchMachine(ctx *context, tag names.MachineTag) {
    20  	_, ok := ctx.machines[tag]
    21  	if ok {
    22  		return
    23  	}
    24  	w := newMachineWatcher(ctx.machineAccessor, tag, ctx.machineChanges)
    25  	ctx.machines[tag] = w
    26  }
    27  
    28  // refreshMachine refreshes the specified machine's instance ID. If it is set,
    29  // then the machine watcher is stopped and pending entities' parameters are
    30  // updated. If the machine is not provisioned yet, this method is a no-op.
    31  func refreshMachine(ctx *context, tag names.MachineTag) error {
    32  	w, ok := ctx.machines[tag]
    33  	if !ok {
    34  		return errors.Errorf("machine %s is not being watched", tag.Id())
    35  	}
    36  	stopAndRemove := func() error {
    37  		if err := w.stop(); err != nil {
    38  			return errors.Annotate(err, "stopping machine watcher")
    39  		}
    40  		delete(ctx.machines, tag)
    41  		return nil
    42  	}
    43  	results, err := ctx.machineAccessor.InstanceIds([]names.MachineTag{tag})
    44  	if err != nil {
    45  		return errors.Annotate(err, "getting machine instance ID")
    46  	}
    47  	if err := results[0].Error; err != nil {
    48  		if params.IsCodeNotProvisioned(err) {
    49  			return nil
    50  		} else if params.IsCodeNotFound(err) {
    51  			// Machine is gone, so stop watching.
    52  			return stopAndRemove()
    53  		}
    54  		return errors.Annotate(err, "getting machine instance ID")
    55  	}
    56  	machineProvisioned(ctx, tag, instance.Id(results[0].Result))
    57  	// machine provisioning is the only thing we care about;
    58  	// stop the watcher.
    59  	return stopAndRemove()
    60  }
    61  
    62  // machineProvisioned is called when a watched machine is provisioned.
    63  func machineProvisioned(ctx *context, tag names.MachineTag, instanceId instance.Id) {
    64  	for _, params := range ctx.incompleteVolumeParams {
    65  		if params.Attachment.Machine != tag || params.Attachment.InstanceId != "" {
    66  			continue
    67  		}
    68  		params.Attachment.InstanceId = instanceId
    69  		updatePendingVolume(ctx, params)
    70  	}
    71  	for id, params := range ctx.incompleteVolumeAttachmentParams {
    72  		if params.Machine != tag || params.InstanceId != "" {
    73  			continue
    74  		}
    75  		params.InstanceId = instanceId
    76  		updatePendingVolumeAttachment(ctx, id, params)
    77  	}
    78  	for id, params := range ctx.pendingFilesystemAttachments {
    79  		if params.Machine != tag || params.InstanceId != "" {
    80  			continue
    81  		}
    82  		params.InstanceId = instanceId
    83  		ctx.pendingFilesystemAttachments[id] = params
    84  	}
    85  }
    86  
    87  type machineWatcher struct {
    88  	tomb       tomb.Tomb
    89  	accessor   MachineAccessor
    90  	tag        names.MachineTag
    91  	instanceId instance.Id
    92  	out        chan<- names.MachineTag
    93  }
    94  
    95  func newMachineWatcher(
    96  	accessor MachineAccessor,
    97  	tag names.MachineTag,
    98  	out chan<- names.MachineTag,
    99  ) *machineWatcher {
   100  	w := &machineWatcher{
   101  		accessor: accessor,
   102  		tag:      tag,
   103  		out:      out,
   104  	}
   105  	go func() {
   106  		defer w.tomb.Done()
   107  		w.tomb.Kill(w.loop())
   108  	}()
   109  	return w
   110  }
   111  
   112  func (mw *machineWatcher) stop() error {
   113  	mw.tomb.Kill(nil)
   114  	return mw.tomb.Wait()
   115  }
   116  
   117  func (mw *machineWatcher) loop() error {
   118  	w, err := mw.accessor.WatchMachine(mw.tag)
   119  	if err != nil {
   120  		return errors.Annotate(err, "watching machine")
   121  	}
   122  	logger.Debugf("watching machine %s", mw.tag.Id())
   123  	defer logger.Debugf("finished watching machine %s", mw.tag.Id())
   124  	var out chan<- names.MachineTag
   125  	for {
   126  		select {
   127  		case <-mw.tomb.Dying():
   128  			return tomb.ErrDying
   129  		case _, ok := <-w.Changes():
   130  			if !ok {
   131  				return watcher.EnsureErr(w)
   132  			}
   133  			out = mw.out
   134  		case out <- mw.tag:
   135  			out = nil
   136  		}
   137  	}
   138  }