github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/machiner/machiner.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  package machiner
     4  
     5  import (
     6  	"net"
     7  
     8  	"github.com/juju/errors"
     9  	"github.com/juju/loggo"
    10  	"gopkg.in/juju/names.v2"
    11  	"gopkg.in/juju/worker.v1"
    12  
    13  	"github.com/juju/juju/api/common"
    14  	"github.com/juju/juju/apiserver/params"
    15  	"github.com/juju/juju/core/status"
    16  	"github.com/juju/juju/core/watcher"
    17  	"github.com/juju/juju/network"
    18  	jworker "github.com/juju/juju/worker"
    19  )
    20  
    21  var logger = loggo.GetLogger("juju.worker.machiner")
    22  
    23  // Config defines the configuration for a machiner worker.
    24  type Config struct {
    25  	// MachineAccessor provides a means of observing and updating the
    26  	// machine's state.
    27  	MachineAccessor MachineAccessor
    28  
    29  	// Tag is the machine's tag.
    30  	Tag names.MachineTag
    31  
    32  	// ClearMachineAddressesOnStart indicates whether or not to clear
    33  	// the machine's machine addresses when the worker starts.
    34  	ClearMachineAddressesOnStart bool
    35  
    36  	// NotifyMachineDead will, if non-nil, be called after the machine
    37  	// is transitioned to the Dead lifecycle state.
    38  	NotifyMachineDead func() error
    39  }
    40  
    41  // Validate reports whether or not the configuration is valid.
    42  func (cfg *Config) Validate() error {
    43  	if cfg.MachineAccessor == nil {
    44  		return errors.NotValidf("unspecified MachineAccessor")
    45  	}
    46  	if cfg.Tag == (names.MachineTag{}) {
    47  		return errors.NotValidf("unspecified Tag")
    48  	}
    49  	return nil
    50  }
    51  
    52  // Machiner is responsible for a machine agent's lifecycle.
    53  type Machiner struct {
    54  	config  Config
    55  	machine Machine
    56  }
    57  
    58  // NewMachiner returns a Worker that will wait for the identified machine
    59  // to become Dying and make it Dead; or until the machine becomes Dead by
    60  // other means.
    61  //
    62  // The machineDead function will be called immediately after the machine's
    63  // lifecycle is updated to Dead.
    64  var NewMachiner = func(cfg Config) (worker.Worker, error) {
    65  	if err := cfg.Validate(); err != nil {
    66  		return nil, errors.Annotate(err, "validating config")
    67  	}
    68  	handler := &Machiner{config: cfg}
    69  	w, err := watcher.NewNotifyWorker(watcher.NotifyConfig{
    70  		Handler: handler,
    71  	})
    72  	if err != nil {
    73  		return nil, errors.Trace(err)
    74  	}
    75  	return w, nil
    76  }
    77  
    78  var getObservedNetworkConfig = common.GetObservedNetworkConfig
    79  
    80  func (mr *Machiner) SetUp() (watcher.NotifyWatcher, error) {
    81  	// Find which machine we're responsible for.
    82  	m, err := mr.config.MachineAccessor.Machine(mr.config.Tag)
    83  	if params.IsCodeNotFoundOrCodeUnauthorized(err) {
    84  		return nil, jworker.ErrTerminateAgent
    85  	} else if err != nil {
    86  		return nil, errors.Trace(err)
    87  	}
    88  	mr.machine = m
    89  
    90  	if mr.config.ClearMachineAddressesOnStart {
    91  		logger.Debugf("machiner configured to reset machine %q addresses to empty", mr.config.Tag)
    92  		if err := m.SetMachineAddresses(nil); err != nil {
    93  			return nil, errors.Annotate(err, "reseting machine addresses")
    94  		}
    95  	} else {
    96  		// Set the addresses in state to the host's addresses.
    97  		if err := setMachineAddresses(mr.config.Tag, m); err != nil {
    98  			return nil, errors.Annotate(err, "setting machine addresses")
    99  		}
   100  	}
   101  
   102  	// Mark the machine as started and log it.
   103  	if err := m.SetStatus(status.Started, "", nil); err != nil {
   104  		return nil, errors.Annotatef(err, "%s failed to set status started", mr.config.Tag)
   105  	}
   106  	logger.Infof("%q started", mr.config.Tag)
   107  
   108  	return m.Watch()
   109  }
   110  
   111  var interfaceAddrs = net.InterfaceAddrs
   112  
   113  // setMachineAddresses sets the addresses for this machine to all of the
   114  // host's non-loopback interface IP addresses.
   115  func setMachineAddresses(tag names.MachineTag, m Machine) error {
   116  	addrs, err := interfaceAddrs()
   117  	if err != nil {
   118  		return err
   119  	}
   120  	var hostAddresses []network.Address
   121  	for _, addr := range addrs {
   122  		var ip net.IP
   123  		switch addr := addr.(type) {
   124  		case *net.IPAddr:
   125  			ip = addr.IP
   126  		case *net.IPNet:
   127  			ip = addr.IP
   128  		default:
   129  			continue
   130  		}
   131  		address := network.NewAddress(ip.String())
   132  		// Filter out link-local addresses as we cannot reliably use them.
   133  		if address.Scope == network.ScopeLinkLocal {
   134  			continue
   135  		}
   136  		hostAddresses = append(hostAddresses, address)
   137  	}
   138  	if len(hostAddresses) == 0 {
   139  		return nil
   140  	}
   141  	// Filter out any LXC or LXD bridge addresses.
   142  	hostAddresses = network.FilterBridgeAddresses(hostAddresses)
   143  	logger.Infof("setting addresses for %q to %v", tag, hostAddresses)
   144  	return m.SetMachineAddresses(hostAddresses)
   145  }
   146  
   147  func (mr *Machiner) Handle(_ <-chan struct{}) error {
   148  	if err := mr.machine.Refresh(); params.IsCodeNotFoundOrCodeUnauthorized(err) {
   149  		// NOTE(axw) we can distinguish between NotFound and CodeUnauthorized,
   150  		// so we could call NotifyMachineDead here in case the agent failed to
   151  		// call NotifyMachineDead directly after setting the machine Dead in
   152  		// the first place. We're not doing that to be cautious: the machine
   153  		// could be missing from state due to invalid global state.
   154  		return jworker.ErrTerminateAgent
   155  	} else if err != nil {
   156  		return err
   157  	}
   158  
   159  	life := mr.machine.Life()
   160  	if life == params.Alive {
   161  		observedConfig, err := getObservedNetworkConfig(common.DefaultNetworkConfigSource())
   162  		if err != nil {
   163  			return errors.Annotate(err, "cannot discover observed network config")
   164  		} else if len(observedConfig) == 0 {
   165  			logger.Warningf("not updating network config: no observed config found to update")
   166  		}
   167  		if len(observedConfig) > 0 {
   168  			if err := mr.machine.SetObservedNetworkConfig(observedConfig); err != nil {
   169  				return errors.Annotate(err, "cannot update observed network config")
   170  			}
   171  		}
   172  		logger.Debugf("observed network config updated for %q to %+v", mr.config.Tag, observedConfig)
   173  
   174  		return nil
   175  	}
   176  	logger.Debugf("%q is now %s", mr.config.Tag, life)
   177  	if err := mr.machine.SetStatus(status.Stopped, "", nil); err != nil {
   178  		return errors.Annotatef(err, "%s failed to set status stopped", mr.config.Tag)
   179  	}
   180  
   181  	// Attempt to mark the machine Dead. If the machine still has units
   182  	// assigned, or storage attached, this will fail with
   183  	// CodeHasAssignedUnits or CodeMachineHasAttachedStorage respectively.
   184  	// Once units or storage are removed, the watcher will trigger again
   185  	// and we'll reattempt.
   186  	if err := mr.machine.EnsureDead(); err != nil {
   187  		if params.IsCodeHasAssignedUnits(err) {
   188  			return nil
   189  		}
   190  		if params.IsCodeMachineHasAttachedStorage(err) {
   191  			logger.Tracef("machine still has storage attached")
   192  			return nil
   193  		}
   194  		err = errors.Annotatef(err, "%s failed to set machine to dead", mr.config.Tag)
   195  		if e := mr.machine.SetStatus(status.Error, errors.Annotate(err, "destroying machine").Error(), nil); e != nil {
   196  			logger.Errorf("failed to set status for error %v ", err)
   197  		}
   198  		return errors.Trace(err)
   199  	}
   200  	// Report on the machine's death. It is important that we do this after
   201  	// the machine is Dead, because this is the mechanism we use to clean up
   202  	// the machine (uninstall). If we were to report before marking the machine
   203  	// as Dead, then we would risk uninstalling prematurely.
   204  	if mr.config.NotifyMachineDead != nil {
   205  		if err := mr.config.NotifyMachineDead(); err != nil {
   206  			return errors.Annotate(err, "reporting machine death")
   207  		}
   208  	}
   209  	return jworker.ErrTerminateAgent
   210  }
   211  
   212  func (mr *Machiner) TearDown() error {
   213  	// Nothing to do here.
   214  	return nil
   215  }