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