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