github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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  	"gopkg.in/juju/names.v2"
     9  	"gopkg.in/juju/worker.v1"
    10  	"gopkg.in/juju/worker.v1/catacomb"
    11  
    12  	"github.com/juju/juju/apiserver/params"
    13  	"github.com/juju/juju/core/instance"
    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, err := newMachineWatcher(ctx.config.Machines, tag, ctx.machineChanges)
    25  	if err != nil {
    26  		ctx.kill(errors.Trace(err))
    27  	} else if err := ctx.addWorker(w); err != nil {
    28  		ctx.kill(errors.Trace(err))
    29  	} else {
    30  		ctx.machines[tag] = w
    31  	}
    32  }
    33  
    34  // refreshMachine refreshes the specified machine's instance ID. If it is set,
    35  // then the machine watcher is stopped and pending entities' parameters are
    36  // updated. If the machine is not provisioned yet, this method is a no-op.
    37  func refreshMachine(ctx *context, tag names.MachineTag) error {
    38  	w, ok := ctx.machines[tag]
    39  	if !ok {
    40  		return errors.Errorf("machine %s is not being watched", tag.Id())
    41  	}
    42  	stopAndRemove := func() error {
    43  		worker.Stop(w)
    44  		delete(ctx.machines, tag)
    45  		return nil
    46  	}
    47  	results, err := ctx.config.Machines.InstanceIds([]names.MachineTag{tag})
    48  	if err != nil {
    49  		return errors.Annotate(err, "getting machine instance ID")
    50  	}
    51  	if err := results[0].Error; err != nil {
    52  		if params.IsCodeNotProvisioned(err) {
    53  			return nil
    54  		} else if params.IsCodeNotFound(err) {
    55  			// Machine is gone, so stop watching.
    56  			return stopAndRemove()
    57  		}
    58  		return errors.Annotate(err, "getting machine instance ID")
    59  	}
    60  	machineProvisioned(ctx, tag, instance.Id(results[0].Result))
    61  	// machine provisioning is the only thing we care about;
    62  	// stop the watcher.
    63  	return stopAndRemove()
    64  }
    65  
    66  // machineProvisioned is called when a watched machine is provisioned.
    67  func machineProvisioned(ctx *context, tag names.MachineTag, instanceId instance.Id) {
    68  	for _, params := range ctx.incompleteVolumeParams {
    69  		if params.Attachment.Machine != tag || params.Attachment.InstanceId != "" {
    70  			continue
    71  		}
    72  		params.Attachment.InstanceId = instanceId
    73  		updatePendingVolume(ctx, params)
    74  	}
    75  	for id, params := range ctx.incompleteVolumeAttachmentParams {
    76  		if params.Machine != tag || params.InstanceId != "" {
    77  			continue
    78  		}
    79  		params.InstanceId = instanceId
    80  		updatePendingVolumeAttachment(ctx, id, params)
    81  	}
    82  	for id, params := range ctx.incompleteFilesystemAttachmentParams {
    83  		if params.Machine != tag || params.InstanceId != "" {
    84  			continue
    85  		}
    86  		params.InstanceId = instanceId
    87  		updatePendingFilesystemAttachment(ctx, id, params)
    88  	}
    89  }
    90  
    91  type machineWatcher struct {
    92  	catacomb   catacomb.Catacomb
    93  	accessor   MachineAccessor
    94  	tag        names.MachineTag
    95  	instanceId instance.Id
    96  	out        chan<- names.MachineTag
    97  }
    98  
    99  func newMachineWatcher(
   100  	accessor MachineAccessor,
   101  	tag names.MachineTag,
   102  	out chan<- names.MachineTag,
   103  ) (*machineWatcher, error) {
   104  	w := &machineWatcher{
   105  		accessor: accessor,
   106  		tag:      tag,
   107  		out:      out,
   108  	}
   109  	err := catacomb.Invoke(catacomb.Plan{
   110  		Site: &w.catacomb,
   111  		Work: w.loop,
   112  	})
   113  	if err != nil {
   114  		return nil, errors.Trace(err)
   115  	}
   116  	return w, nil
   117  }
   118  
   119  func (mw *machineWatcher) loop() error {
   120  	w, err := mw.accessor.WatchMachine(mw.tag)
   121  	if err != nil {
   122  		return errors.Annotate(err, "watching machine")
   123  	}
   124  	if err := mw.catacomb.Add(w); err != nil {
   125  		return errors.Trace(err)
   126  	}
   127  	logger.Debugf("watching machine %s", mw.tag.Id())
   128  	defer logger.Debugf("finished watching machine %s", mw.tag.Id())
   129  	var out chan<- names.MachineTag
   130  	for {
   131  		select {
   132  		case <-mw.catacomb.Dying():
   133  			return mw.catacomb.ErrDying()
   134  		case _, ok := <-w.Changes():
   135  			if !ok {
   136  				return errors.New("machine watcher closed")
   137  			}
   138  			out = mw.out
   139  		case out <- mw.tag:
   140  			out = nil
   141  		}
   142  	}
   143  }
   144  
   145  // Kill is part of the worker.Worker interface.
   146  func (mw *machineWatcher) Kill() {
   147  	mw.catacomb.Kill(nil)
   148  }
   149  
   150  // Wait is part of the worker.Worker interface.
   151  func (mw *machineWatcher) Wait() error {
   152  	return mw.catacomb.Wait()
   153  }