github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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  	var machineDead machineDeathTracker
    87  	w, err := machiner.NewMachiner(machiner.Config{
    88  		s.accessor, s.machineTag, false,
    89  		machineDead.machineDead,
    90  	})
    91  	c.Assert(err, jc.ErrorIsNil)
    92  	s.accessor.machine.SetErrors(
    93  		nil, // SetMachineAddresses
    94  		nil, // SetStatus
    95  		nil, // Watch
    96  		&params.Error{Code: params.CodeNotFound}, // Refresh
    97  	)
    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  	w, err := machiner.NewMachiner(machiner.Config{
   106  		MachineAccessor: s.accessor,
   107  		Tag:             s.machineTag,
   108  	})
   109  	c.Assert(err, jc.ErrorIsNil)
   110  	s.accessor.machine.life = params.Dying
   111  	s.accessor.machine.SetErrors(
   112  		nil, // SetMachineAddresses
   113  		nil, // SetStatus (started)
   114  		nil, // Watch
   115  		nil, // Refresh
   116  		errors.New("cannot set status"), // SetStatus (stopped)
   117  	)
   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  	w, err := machiner.NewMachiner(machiner.Config{
   142  		MachineAccessor: s.accessor,
   143  		Tag:             s.machineTag,
   144  	})
   145  	c.Assert(err, jc.ErrorIsNil)
   146  	s.accessor.machine.life = params.Dying
   147  	s.accessor.machine.SetErrors(
   148  		nil, // SetMachineAddresses
   149  		nil, // SetStatus
   150  		nil, // Watch
   151  		nil, // Refresh
   152  		nil, // SetStatus
   153  		errors.New("cannot ensure machine is dead"), // EnsureDead
   154  	)
   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  	w, err := machiner.NewMachiner(machiner.Config{
   165  		MachineAccessor: s.accessor,
   166  		Tag:             s.machineTag,
   167  	})
   168  	c.Assert(err, jc.ErrorIsNil)
   169  	s.accessor.machine.life = params.Dying
   170  	s.accessor.machine.SetErrors(
   171  		nil, // SetMachineAddresses
   172  		nil, // SetStatus
   173  		nil, // Watch
   174  		nil, // Refresh
   175  		nil, // SetStatus
   176  		&params.Error{Code: params.CodeHasAssignedUnits}, // EnsureDead
   177  	)
   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  }