github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/machineundertaker/undertaker.go (about)

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package machineundertaker
     5  
     6  import (
     7  	stdcontext "context"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/names/v5"
    11  	"github.com/juju/worker/v3"
    12  
    13  	"github.com/juju/juju/core/network"
    14  	"github.com/juju/juju/core/watcher"
    15  	"github.com/juju/juju/environs"
    16  	"github.com/juju/juju/environs/context"
    17  	"github.com/juju/juju/worker/common"
    18  )
    19  
    20  // Logger is here to stop the desire of creating a package level Logger.
    21  // Don't do this, instead use the one passed as manifold config.
    22  type logger interface{}
    23  
    24  var _ logger = struct{}{}
    25  
    26  // Facade defines the interface we require from the machine undertaker
    27  // facade.
    28  type Facade interface {
    29  	WatchMachineRemovals() (watcher.NotifyWatcher, error)
    30  	AllMachineRemovals() ([]names.MachineTag, error)
    31  	GetProviderInterfaceInfo(names.MachineTag) ([]network.ProviderInterfaceInfo, error)
    32  	CompleteRemoval(names.MachineTag) error
    33  }
    34  
    35  // AddressReleaser defines the interface we need from the environment
    36  // networking.
    37  type AddressReleaser interface {
    38  	ReleaseContainerAddresses(context.ProviderCallContext, []network.ProviderInterfaceInfo) error
    39  }
    40  
    41  // Undertaker is responsible for doing any provider-level
    42  // cleanup needed and then removing the machine.
    43  type Undertaker struct {
    44  	API             Facade
    45  	Releaser        AddressReleaser
    46  	CallContextFunc common.CloudCallContextFunc
    47  	Logger          Logger
    48  }
    49  
    50  // NewWorker returns a machine undertaker worker that will watch for
    51  // machines that need to be removed and remove them, cleaning up any
    52  // necessary provider-level resources first.
    53  func NewWorker(api Facade, env environs.Environ, credentialAPI common.CredentialAPI, logger Logger) (worker.Worker, error) {
    54  	envNetworking, _ := environs.SupportsNetworking(env)
    55  	w, err := watcher.NewNotifyWorker(watcher.NotifyConfig{
    56  		Handler: &Undertaker{
    57  			API:             api,
    58  			Releaser:        envNetworking,
    59  			CallContextFunc: common.NewCloudCallContextFunc(credentialAPI),
    60  			Logger:          logger,
    61  		},
    62  	})
    63  	if err != nil {
    64  		return nil, errors.Trace(err)
    65  	}
    66  	return w, nil
    67  }
    68  
    69  // SetUp (part of watcher.NotifyHandler) starts watching for machine
    70  // removals.
    71  func (u *Undertaker) SetUp() (watcher.NotifyWatcher, error) {
    72  	u.Logger.Infof("setting up machine undertaker")
    73  	return u.API.WatchMachineRemovals()
    74  }
    75  
    76  // Handle (part of watcher.NotifyHandler) cleans up provider resources
    77  // and removes machines that have been marked for removal.
    78  func (u *Undertaker) Handle(<-chan struct{}) error {
    79  	removals, err := u.API.AllMachineRemovals()
    80  	if err != nil {
    81  		return errors.Trace(err)
    82  	}
    83  	u.Logger.Debugf("handling removals: %v", removals)
    84  	// TODO(babbageclunk): shuffle the removals so if there's a
    85  	// problem with one others can still get past?
    86  	for _, machine := range removals {
    87  		err := u.MaybeReleaseAddresses(machine)
    88  		if err != nil {
    89  			u.Logger.Errorf("couldn't release addresses for %s: %s", machine, err)
    90  			continue
    91  		}
    92  		err = u.API.CompleteRemoval(machine)
    93  		if err != nil {
    94  			u.Logger.Errorf("couldn't complete removal for %s: %s", machine, err)
    95  		} else {
    96  			u.Logger.Debugf("completed removal: %s", machine)
    97  		}
    98  	}
    99  	return nil
   100  }
   101  
   102  // MaybeReleaseAddresses releases any addresses that have been
   103  // allocated to this machine by the provider (if the provider supports
   104  // that).
   105  func (u *Undertaker) MaybeReleaseAddresses(machine names.MachineTag) error {
   106  	if u.Releaser == nil {
   107  		// This environ doesn't support releasing addresses.
   108  		return nil
   109  	}
   110  	if !names.IsContainerMachine(machine.Id()) {
   111  		// At the moment, only containers need their addresses releasing.
   112  		return nil
   113  	}
   114  	interfaceInfos, err := u.API.GetProviderInterfaceInfo(machine)
   115  	if err != nil {
   116  		return errors.Trace(err)
   117  	}
   118  	if len(interfaceInfos) == 0 {
   119  		u.Logger.Debugf("%s has no addresses to release", machine)
   120  		return nil
   121  	}
   122  	err = u.Releaser.ReleaseContainerAddresses(u.CallContextFunc(stdcontext.Background()), interfaceInfos)
   123  	// Some providers say they support networking but don't
   124  	// actually support container addressing; don't freak out
   125  	// about those.
   126  	if errors.IsNotSupported(err) {
   127  		u.Logger.Debugf("%s has addresses but provider doesn't support releasing them", machine)
   128  	} else if err != nil {
   129  		return errors.Trace(err)
   130  	}
   131  	return nil
   132  }
   133  
   134  // TearDown (part of watcher.NotifyHandler) is an opportunity to stop
   135  // or release any resources created in SetUp other than the watcher,
   136  // which watcher.NotifyWorker takes care of for us.
   137  func (u *Undertaker) TearDown() error {
   138  	u.Logger.Infof("tearing down machine undertaker")
   139  	return nil
   140  }