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