github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/worker/machiner/machiner_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package machiner_test
     5  
     6  import (
     7  	"io/ioutil"
     8  	"net"
     9  	"path/filepath"
    10  	stdtesting "testing"
    11  	"time"
    12  
    13  	"github.com/juju/errors"
    14  	gitjujutesting "github.com/juju/testing"
    15  	jc "github.com/juju/testing/checkers"
    16  	gc "gopkg.in/check.v1"
    17  	"gopkg.in/juju/names.v2"
    18  
    19  	"github.com/juju/juju/api"
    20  	apimachiner "github.com/juju/juju/api/machiner"
    21  	"github.com/juju/juju/apiserver/common/networkingcommon"
    22  	"github.com/juju/juju/apiserver/params"
    23  	"github.com/juju/juju/juju/testing"
    24  	"github.com/juju/juju/network"
    25  	"github.com/juju/juju/state"
    26  	"github.com/juju/juju/status"
    27  	coretesting "github.com/juju/juju/testing"
    28  	"github.com/juju/juju/worker"
    29  	"github.com/juju/juju/worker/machiner"
    30  )
    31  
    32  func TestPackage(t *stdtesting.T) {
    33  	coretesting.MgoTestPackage(t)
    34  }
    35  
    36  type MachinerSuite struct {
    37  	coretesting.BaseSuite
    38  	accessor   *mockMachineAccessor
    39  	machineTag names.MachineTag
    40  	addresses  []net.Addr
    41  }
    42  
    43  var _ = gc.Suite(&MachinerSuite{})
    44  
    45  func (s *MachinerSuite) SetUpTest(c *gc.C) {
    46  	s.BaseSuite.SetUpTest(c)
    47  	s.accessor = &mockMachineAccessor{}
    48  	s.accessor.machine.watcher.changes = make(chan struct{})
    49  	s.accessor.machine.life = params.Alive
    50  	s.machineTag = names.NewMachineTag("123")
    51  	s.addresses = []net.Addr{ // anything will do
    52  		&net.IPAddr{IP: net.IPv4bcast},
    53  		&net.IPAddr{IP: net.IPv4zero},
    54  	}
    55  	s.PatchValue(machiner.InterfaceAddrs, func() ([]net.Addr, error) {
    56  		return s.addresses, nil
    57  	})
    58  	s.PatchValue(machiner.GetObservedNetworkConfig, func(_ networkingcommon.NetworkConfigSource) ([]params.NetworkConfig, error) {
    59  		return nil, nil
    60  	})
    61  }
    62  
    63  func (s *MachinerSuite) TestMachinerConfigValidate(c *gc.C) {
    64  	_, err := machiner.NewMachiner(machiner.Config{})
    65  	c.Assert(err, gc.ErrorMatches, "validating config: unspecified MachineAccessor not valid")
    66  	_, err = machiner.NewMachiner(machiner.Config{
    67  		MachineAccessor: &mockMachineAccessor{},
    68  	})
    69  	c.Assert(err, gc.ErrorMatches, "validating config: unspecified Tag not valid")
    70  
    71  	w, err := machiner.NewMachiner(machiner.Config{
    72  		MachineAccessor: &mockMachineAccessor{},
    73  		Tag:             names.NewMachineTag("123"),
    74  	})
    75  	c.Assert(err, jc.ErrorIsNil)
    76  
    77  	// must stop the worker to prevent a data race when cleanup suite
    78  	// rolls back the patches
    79  	err = stopWorker(w)
    80  	c.Assert(err, jc.ErrorIsNil)
    81  }
    82  
    83  func (s *MachinerSuite) TestMachinerMachineNotFound(c *gc.C) {
    84  	// Accessing the machine initially yields "not found or unauthorized".
    85  	// We don't know which, so we don't report that the machine is dead.
    86  	s.accessor.machine.SetErrors(
    87  		nil, // SetMachineAddresses
    88  		nil, // SetStatus
    89  		nil, // Watch
    90  		&params.Error{Code: params.CodeNotFound}, // Refresh
    91  	)
    92  	var machineDead machineDeathTracker
    93  	w, err := machiner.NewMachiner(machiner.Config{
    94  		s.accessor, s.machineTag, false,
    95  		machineDead.machineDead,
    96  	})
    97  	c.Assert(err, jc.ErrorIsNil)
    98  	s.accessor.machine.watcher.changes <- struct{}{}
    99  	err = stopWorker(w)
   100  	c.Assert(errors.Cause(err), gc.Equals, worker.ErrTerminateAgent)
   101  	c.Assert(bool(machineDead), jc.IsFalse)
   102  }
   103  
   104  func (s *MachinerSuite) TestMachinerSetStatusStopped(c *gc.C) {
   105  	s.accessor.machine.life = params.Dying
   106  	s.accessor.machine.SetErrors(
   107  		nil, // SetMachineAddresses
   108  		nil, // SetStatus (started)
   109  		nil, // Watch
   110  		nil, // Refresh
   111  		errors.New("cannot set status"), // SetStatus (stopped)
   112  	)
   113  	w, err := machiner.NewMachiner(machiner.Config{
   114  		MachineAccessor: s.accessor,
   115  		Tag:             s.machineTag,
   116  	})
   117  	c.Assert(err, jc.ErrorIsNil)
   118  	s.accessor.machine.watcher.changes <- struct{}{}
   119  	err = stopWorker(w)
   120  	c.Assert(
   121  		err, gc.ErrorMatches,
   122  		"machine-123 failed to set status stopped: cannot set status",
   123  	)
   124  	s.accessor.machine.CheckCallNames(c,
   125  		"SetMachineAddresses",
   126  		"SetStatus",
   127  		"Watch",
   128  		"Refresh",
   129  		"Life",
   130  		"SetStatus",
   131  	)
   132  	s.accessor.machine.CheckCall(
   133  		c, 5, "SetStatus",
   134  		status.Stopped,
   135  		"",
   136  		map[string]interface{}(nil),
   137  	)
   138  }
   139  
   140  func (s *MachinerSuite) TestMachinerMachineEnsureDeadError(c *gc.C) {
   141  	s.accessor.machine.life = params.Dying
   142  	s.accessor.machine.SetErrors(
   143  		nil, // SetMachineAddresses
   144  		nil, // SetStatus
   145  		nil, // Watch
   146  		nil, // Refresh
   147  		nil, // SetStatus
   148  		errors.New("cannot ensure machine is dead"), // EnsureDead
   149  	)
   150  	w, err := machiner.NewMachiner(machiner.Config{
   151  		MachineAccessor: s.accessor,
   152  		Tag:             s.machineTag,
   153  	})
   154  	c.Assert(err, jc.ErrorIsNil)
   155  	s.accessor.machine.watcher.changes <- struct{}{}
   156  	err = stopWorker(w)
   157  	c.Check(
   158  		err, gc.ErrorMatches,
   159  		"machine-123 failed to set machine to dead: cannot ensure machine is dead",
   160  	)
   161  }
   162  
   163  func (s *MachinerSuite) TestMachinerMachineAssignedUnits(c *gc.C) {
   164  	s.accessor.machine.life = params.Dying
   165  	s.accessor.machine.SetErrors(
   166  		nil, // SetMachineAddresses
   167  		nil, // SetStatus
   168  		nil, // Watch
   169  		nil, // Refresh
   170  		nil, // SetStatus
   171  		&params.Error{Code: params.CodeHasAssignedUnits}, // EnsureDead
   172  	)
   173  	w, err := machiner.NewMachiner(machiner.Config{
   174  		MachineAccessor: s.accessor,
   175  		Tag:             s.machineTag,
   176  	})
   177  	c.Assert(err, jc.ErrorIsNil)
   178  	s.accessor.machine.watcher.changes <- struct{}{}
   179  	err = stopWorker(w)
   180  
   181  	// If EnsureDead fails with "machine has assigned units", then
   182  	// the worker will not fail, but will wait for more events.
   183  	c.Check(err, jc.ErrorIsNil)
   184  
   185  	s.accessor.machine.CheckCallNames(c,
   186  		"SetMachineAddresses",
   187  		"SetStatus",
   188  		"Watch",
   189  		"Refresh",
   190  		"Life",
   191  		"SetStatus",
   192  		"EnsureDead",
   193  	)
   194  }
   195  
   196  func (s *MachinerSuite) TestMachinerStorageAttached(c *gc.C) {
   197  	// Machine is dying. We'll respond to "EnsureDead" by
   198  	// saying that there are still storage attachments;
   199  	// this should not cause an error.
   200  	s.accessor.machine.life = params.Dying
   201  	s.accessor.machine.SetErrors(
   202  		nil, // SetMachineAddresses
   203  		nil, // SetStatus
   204  		nil, // Watch
   205  		nil, // Refresh
   206  		nil, // SetStatus
   207  		&params.Error{Code: params.CodeMachineHasAttachedStorage},
   208  	)
   209  
   210  	worker, err := machiner.NewMachiner(machiner.Config{
   211  		s.accessor, s.machineTag, false,
   212  		func() error { return nil },
   213  	})
   214  	c.Assert(err, jc.ErrorIsNil)
   215  	s.accessor.machine.watcher.changes <- struct{}{}
   216  	err = stopWorker(worker)
   217  	c.Check(err, jc.ErrorIsNil)
   218  
   219  	s.accessor.CheckCalls(c, []gitjujutesting.StubCall{{
   220  		FuncName: "Machine",
   221  		Args:     []interface{}{s.machineTag},
   222  	}})
   223  
   224  	s.accessor.machine.CheckCalls(c, []gitjujutesting.StubCall{{
   225  		FuncName: "SetMachineAddresses",
   226  		Args: []interface{}{
   227  			network.NewAddresses(
   228  				"255.255.255.255",
   229  				"0.0.0.0",
   230  			),
   231  		},
   232  	}, {
   233  		FuncName: "SetStatus",
   234  		Args: []interface{}{
   235  			status.Started,
   236  			"",
   237  			map[string]interface{}(nil),
   238  		},
   239  	}, {
   240  		FuncName: "Watch",
   241  	}, {
   242  		FuncName: "Refresh",
   243  	}, {
   244  		FuncName: "Life",
   245  	}, {
   246  		FuncName: "SetStatus",
   247  		Args: []interface{}{
   248  			status.Stopped,
   249  			"",
   250  			map[string]interface{}(nil),
   251  		},
   252  	}, {
   253  		FuncName: "EnsureDead",
   254  	}})
   255  }
   256  
   257  // worstCase is used for timeouts when timing out
   258  // will fail the test. Raising this value should
   259  // not affect the overall running time of the tests
   260  // unless they fail.
   261  const worstCase = 5 * time.Second
   262  
   263  type MachinerStateSuite struct {
   264  	testing.JujuConnSuite
   265  
   266  	st            api.Connection
   267  	machinerState *apimachiner.State
   268  	machine       *state.Machine
   269  	apiMachine    *apimachiner.Machine
   270  
   271  	getObservedNetworkConfigError error
   272  }
   273  
   274  var _ = gc.Suite(&MachinerStateSuite{})
   275  
   276  func (s *MachinerStateSuite) SetUpTest(c *gc.C) {
   277  	s.JujuConnSuite.SetUpTest(c)
   278  	s.st, s.machine = s.OpenAPIAsNewMachine(c)
   279  
   280  	// Create the machiner API facade.
   281  	s.machinerState = apimachiner.NewState(s.st)
   282  	c.Assert(s.machinerState, gc.NotNil)
   283  
   284  	// Get the machine through the facade.
   285  	var err error
   286  	s.apiMachine, err = s.machinerState.Machine(s.machine.Tag().(names.MachineTag))
   287  	c.Assert(err, jc.ErrorIsNil)
   288  	c.Assert(s.apiMachine.Tag(), gc.Equals, s.machine.Tag())
   289  	// Isolate tests better by not using real interface addresses.
   290  	s.PatchValue(machiner.InterfaceAddrs, func() ([]net.Addr, error) {
   291  		return nil, nil
   292  	})
   293  	s.PatchValue(&network.InterfaceByNameAddrs, func(string) ([]net.Addr, error) {
   294  		return nil, nil
   295  	})
   296  	s.PatchValue(&network.LXCNetDefaultConfig, "")
   297  	s.getObservedNetworkConfigError = nil
   298  	s.PatchValue(machiner.GetObservedNetworkConfig, func(_ networkingcommon.NetworkConfigSource) ([]params.NetworkConfig, error) {
   299  		return nil, s.getObservedNetworkConfigError
   300  	})
   301  }
   302  
   303  func (s *MachinerStateSuite) waitMachineStatus(c *gc.C, m *state.Machine, expectStatus status.Status) {
   304  	timeout := time.After(worstCase)
   305  	for {
   306  		select {
   307  		case <-timeout:
   308  			c.Fatalf("timeout while waiting for machine status to change")
   309  		case <-time.After(10 * time.Millisecond):
   310  			statusInfo, err := m.Status()
   311  			c.Assert(err, jc.ErrorIsNil)
   312  			if statusInfo.Status != expectStatus {
   313  				c.Logf("machine %q status is %s, still waiting", m, statusInfo.Status)
   314  				continue
   315  			}
   316  			return
   317  		}
   318  	}
   319  }
   320  
   321  func (s *MachinerStateSuite) TestNotFoundOrUnauthorized(c *gc.C) {
   322  	mr, err := machiner.NewMachiner(machiner.Config{
   323  		machiner.APIMachineAccessor{s.machinerState},
   324  		names.NewMachineTag("99"),
   325  		false,
   326  		// the "machineDead" callback should not be invoked
   327  		// because we don't know whether the agent is
   328  		// legimitately not found or unauthorized; we err on
   329  		// the side of caution, in case the password got mucked
   330  		// up, or state got mucked up (e.g. during an upgrade).
   331  		func() error { return errors.New("should not be called") },
   332  	})
   333  	c.Assert(err, jc.ErrorIsNil)
   334  	c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent)
   335  }
   336  
   337  func (s *MachinerStateSuite) makeMachiner(
   338  	c *gc.C,
   339  	ignoreAddresses bool,
   340  	machineDead func() error,
   341  ) worker.Worker {
   342  	if machineDead == nil {
   343  		machineDead = func() error { return nil }
   344  	}
   345  	w, err := machiner.NewMachiner(machiner.Config{
   346  		machiner.APIMachineAccessor{s.machinerState},
   347  		s.apiMachine.Tag().(names.MachineTag),
   348  		ignoreAddresses,
   349  		machineDead,
   350  	})
   351  	c.Assert(err, jc.ErrorIsNil)
   352  	return w
   353  }
   354  
   355  type machineDeathTracker bool
   356  
   357  func (t *machineDeathTracker) machineDead() error {
   358  	*t = true
   359  	return nil
   360  }
   361  
   362  func (s *MachinerStateSuite) TestRunStop(c *gc.C) {
   363  	var machineDead machineDeathTracker
   364  	mr := s.makeMachiner(c, false, machineDead.machineDead)
   365  	c.Assert(worker.Stop(mr), jc.ErrorIsNil)
   366  	c.Assert(s.apiMachine.Refresh(), jc.ErrorIsNil)
   367  	c.Assert(s.apiMachine.Life(), gc.Equals, params.Alive)
   368  	c.Assert(bool(machineDead), jc.IsFalse)
   369  }
   370  
   371  func (s *MachinerStateSuite) TestStartSetsStatus(c *gc.C) {
   372  	statusInfo, err := s.machine.Status()
   373  	c.Assert(err, jc.ErrorIsNil)
   374  	c.Assert(statusInfo.Status, gc.Equals, status.Pending)
   375  	c.Assert(statusInfo.Message, gc.Equals, "")
   376  
   377  	mr := s.makeMachiner(c, false, nil)
   378  	defer worker.Stop(mr)
   379  
   380  	s.waitMachineStatus(c, s.machine, status.Started)
   381  }
   382  
   383  func (s *MachinerStateSuite) TestSetsStatusWhenDying(c *gc.C) {
   384  	mr := s.makeMachiner(c, false, nil)
   385  	defer worker.Stop(mr)
   386  	c.Assert(s.machine.Destroy(), jc.ErrorIsNil)
   387  	s.waitMachineStatus(c, s.machine, status.Stopped)
   388  }
   389  
   390  func (s *MachinerStateSuite) TestSetDead(c *gc.C) {
   391  	var machineDead machineDeathTracker
   392  	mr := s.makeMachiner(c, false, machineDead.machineDead)
   393  	defer worker.Stop(mr)
   394  	c.Assert(s.machine.Destroy(), jc.ErrorIsNil)
   395  	s.State.StartSync()
   396  	c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent)
   397  	c.Assert(s.machine.Refresh(), jc.ErrorIsNil)
   398  	c.Assert(s.machine.Life(), gc.Equals, state.Dead)
   399  	c.Assert(bool(machineDead), jc.IsTrue)
   400  }
   401  
   402  func (s *MachinerStateSuite) TestSetDeadWithDyingUnit(c *gc.C) {
   403  	var machineDead machineDeathTracker
   404  	mr := s.makeMachiner(c, false, machineDead.machineDead)
   405  	defer worker.Stop(mr)
   406  
   407  	// Add a service, assign to machine.
   408  	wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   409  	unit, err := wordpress.AddUnit()
   410  	c.Assert(err, jc.ErrorIsNil)
   411  	err = unit.AssignToMachine(s.machine)
   412  	c.Assert(err, jc.ErrorIsNil)
   413  
   414  	// Service alive, can't destroy machine.
   415  	err = s.machine.Destroy()
   416  	c.Assert(err, jc.Satisfies, state.IsHasAssignedUnitsError)
   417  
   418  	err = wordpress.Destroy()
   419  	c.Assert(err, jc.ErrorIsNil)
   420  
   421  	// With dying unit, machine can now be marked as dying.
   422  	c.Assert(s.machine.Destroy(), jc.ErrorIsNil)
   423  	s.State.StartSync()
   424  	c.Assert(s.machine.Refresh(), jc.ErrorIsNil)
   425  	c.Assert(s.machine.Life(), gc.Equals, state.Dying)
   426  	c.Assert(bool(machineDead), jc.IsFalse)
   427  
   428  	// When the unit is ultimately destroyed, the machine becomes dead.
   429  	err = unit.Destroy()
   430  	c.Assert(err, jc.ErrorIsNil)
   431  	s.State.StartSync()
   432  	c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent)
   433  	c.Assert(bool(machineDead), jc.IsTrue)
   434  }
   435  
   436  func (s *MachinerStateSuite) TestAliveErrorGetObservedNetworkConfig(c *gc.C) {
   437  	s.getObservedNetworkConfigError = errors.New("no config!")
   438  	var machineDead machineDeathTracker
   439  	mr := s.makeMachiner(c, false, machineDead.machineDead)
   440  	defer worker.Stop(mr)
   441  	s.State.StartSync()
   442  
   443  	c.Assert(mr.Wait(), gc.ErrorMatches, "cannot discover observed network config: no config!")
   444  	c.Assert(s.machine.Refresh(), jc.ErrorIsNil)
   445  	c.Assert(s.machine.Life(), gc.Equals, state.Alive)
   446  	c.Assert(bool(machineDead), jc.IsFalse)
   447  }
   448  
   449  func (s *MachinerStateSuite) setupSetMachineAddresses(c *gc.C, ignore bool) {
   450  	lxcFakeNetConfig := filepath.Join(c.MkDir(), "lxc-net")
   451  	netConf := []byte(`
   452    # comments ignored
   453  LXC_BR= ignored
   454  LXC_ADDR = "fooo"
   455  LXC_BRIDGE="foobar" # detected
   456  anything else ignored
   457  LXC_BRIDGE="ignored"`[1:])
   458  	err := ioutil.WriteFile(lxcFakeNetConfig, netConf, 0644)
   459  	c.Assert(err, jc.ErrorIsNil)
   460  	s.PatchValue(machiner.InterfaceAddrs, func() ([]net.Addr, error) {
   461  		addrs := []net.Addr{
   462  			&net.IPAddr{IP: net.IPv4(10, 0, 0, 1)},
   463  			&net.IPAddr{IP: net.IPv4(127, 0, 0, 1)},
   464  			&net.IPAddr{IP: net.IPv4(10, 0, 3, 1)}, // lxc bridge address ignored
   465  			&net.IPAddr{IP: net.IPv6loopback},
   466  			&net.UnixAddr{},                        // not IP, ignored
   467  			&net.IPAddr{IP: net.IPv4(10, 0, 3, 4)}, // lxc bridge address ignored
   468  			&net.IPNet{IP: net.ParseIP("2001:db8::1")},
   469  			&net.IPAddr{IP: net.IPv4(169, 254, 1, 20)}, // LinkLocal Ignored
   470  			&net.IPNet{IP: net.ParseIP("fe80::1")},     // LinkLocal Ignored
   471  		}
   472  		return addrs, nil
   473  	})
   474  	s.PatchValue(&network.InterfaceByNameAddrs, func(name string) ([]net.Addr, error) {
   475  		if name == "foobar" {
   476  			// The addresses on the LXC bridge
   477  			return []net.Addr{
   478  				&net.IPAddr{IP: net.IPv4(10, 0, 3, 1)},
   479  				&net.IPAddr{IP: net.IPv4(10, 0, 3, 4)},
   480  			}, nil
   481  		} else if name == network.DefaultLXDBridge {
   482  			// The addresses on the LXD bridge
   483  			return []net.Addr{
   484  				&net.IPAddr{IP: net.IPv4(10, 0, 4, 1)},
   485  				&net.IPAddr{IP: net.IPv4(10, 0, 4, 4)},
   486  			}, nil
   487  		}
   488  		c.Fatalf("unknown bridge in testing: %v", name)
   489  		return nil, nil
   490  	})
   491  	s.PatchValue(&network.LXCNetDefaultConfig, lxcFakeNetConfig)
   492  
   493  	mr := s.makeMachiner(c, ignore, nil)
   494  	defer worker.Stop(mr)
   495  	c.Assert(s.machine.Destroy(), jc.ErrorIsNil)
   496  	s.State.StartSync()
   497  	c.Assert(mr.Wait(), gc.Equals, worker.ErrTerminateAgent)
   498  	c.Assert(s.machine.Refresh(), jc.ErrorIsNil)
   499  }
   500  
   501  func (s *MachinerStateSuite) TestMachineAddresses(c *gc.C) {
   502  	s.setupSetMachineAddresses(c, false)
   503  	c.Assert(s.machine.MachineAddresses(), jc.SameContents, []network.Address{
   504  		network.NewAddress("2001:db8::1"),
   505  		network.NewScopedAddress("10.0.0.1", network.ScopeCloudLocal),
   506  		network.NewScopedAddress("::1", network.ScopeMachineLocal),
   507  		network.NewScopedAddress("127.0.0.1", network.ScopeMachineLocal),
   508  	})
   509  }
   510  
   511  func (s *MachinerStateSuite) TestMachineAddressesWithIgnoreFlag(c *gc.C) {
   512  	s.setupSetMachineAddresses(c, true)
   513  	c.Assert(s.machine.MachineAddresses(), gc.HasLen, 0)
   514  }
   515  
   516  func stopWorker(w worker.Worker) error {
   517  	w.Kill()
   518  	return w.Wait()
   519  }