github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/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/v5"
     9  	"github.com/juju/worker/v3"
    10  	"github.com/juju/worker/v3/catacomb"
    11  
    12  	"github.com/juju/juju/core/instance"
    13  	"github.com/juju/juju/rpc/params"
    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, ctx.config.Logger)
    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  	out      chan<- names.MachineTag
    96  	logger   Logger
    97  }
    98  
    99  func newMachineWatcher(
   100  	accessor MachineAccessor,
   101  	tag names.MachineTag,
   102  	out chan<- names.MachineTag,
   103  	logger Logger,
   104  ) (*machineWatcher, error) {
   105  	w := &machineWatcher{
   106  		accessor: accessor,
   107  		tag:      tag,
   108  		out:      out,
   109  		logger:   logger,
   110  	}
   111  	err := catacomb.Invoke(catacomb.Plan{
   112  		Site: &w.catacomb,
   113  		Work: w.loop,
   114  	})
   115  	if err != nil {
   116  		return nil, errors.Trace(err)
   117  	}
   118  	return w, nil
   119  }
   120  
   121  func (mw *machineWatcher) loop() error {
   122  	w, err := mw.accessor.WatchMachine(mw.tag)
   123  	if err != nil {
   124  		return errors.Annotate(err, "watching machine")
   125  	}
   126  	if err := mw.catacomb.Add(w); err != nil {
   127  		return errors.Trace(err)
   128  	}
   129  	mw.logger.Debugf("watching machine %s", mw.tag.Id())
   130  	defer mw.logger.Debugf("finished watching machine %s", mw.tag.Id())
   131  	var out chan<- names.MachineTag
   132  	for {
   133  		select {
   134  		case <-mw.catacomb.Dying():
   135  			return mw.catacomb.ErrDying()
   136  		case _, ok := <-w.Changes():
   137  			if !ok {
   138  				return errors.New("machine watcher closed")
   139  			}
   140  			out = mw.out
   141  		case out <- mw.tag:
   142  			out = nil
   143  		}
   144  	}
   145  }
   146  
   147  // Kill is part of the worker.Worker interface.
   148  func (mw *machineWatcher) Kill() {
   149  	mw.catacomb.Kill(nil)
   150  }
   151  
   152  // Wait is part of the worker.Worker interface.
   153  func (mw *machineWatcher) Wait() error {
   154  	return mw.catacomb.Wait()
   155  }