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

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package machineundertaker_test
     5  
     6  import (
     7  	"github.com/juju/errors"
     8  	"github.com/juju/testing"
     9  	jc "github.com/juju/testing/checkers"
    10  	gc "gopkg.in/check.v1"
    11  	"gopkg.in/juju/names.v2"
    12  	"gopkg.in/tomb.v1"
    13  
    14  	"github.com/juju/juju/environs"
    15  	"github.com/juju/juju/network"
    16  	"github.com/juju/juju/watcher"
    17  	"github.com/juju/juju/worker"
    18  	"github.com/juju/juju/worker/machineundertaker"
    19  	"github.com/juju/juju/worker/workertest"
    20  )
    21  
    22  type undertakerSuite struct {
    23  	testing.IsolationSuite
    24  }
    25  
    26  var _ = gc.Suite(&undertakerSuite{})
    27  
    28  // Some tests to check that the handler is wired up to the
    29  // NotifyWorker first.
    30  
    31  func (s *undertakerSuite) TestErrorWatching(c *gc.C) {
    32  	api := s.makeAPIWithWatcher()
    33  	api.SetErrors(errors.New("blam"))
    34  	w, err := machineundertaker.NewWorker(api, &fakeEnviron{})
    35  	c.Assert(err, jc.ErrorIsNil)
    36  	err = workertest.CheckKilled(c, w)
    37  	c.Check(err, gc.ErrorMatches, "blam")
    38  	api.CheckCallNames(c, "WatchMachineRemovals")
    39  }
    40  
    41  func (s *undertakerSuite) TestErrorGettingRemovals(c *gc.C) {
    42  	api := s.makeAPIWithWatcher()
    43  	api.SetErrors(nil, errors.New("explodo"))
    44  	w, err := machineundertaker.NewWorker(api, &fakeEnviron{})
    45  	c.Assert(err, jc.ErrorIsNil)
    46  	err = workertest.CheckKilled(c, w)
    47  	c.Check(err, gc.ErrorMatches, "explodo")
    48  	api.CheckCallNames(c, "WatchMachineRemovals", "AllMachineRemovals")
    49  }
    50  
    51  // It's really fiddly trying to test the code behind the worker, so
    52  // the rest of the tests use the Undertaker directly to test the
    53  // Handle and MaybeReleaseAddresses methods. This is much simpler
    54  // because everything happens in the same goroutine (and it's safe
    55  // since all of the clever/tricky lifecycle management is taken care
    56  // of in NotifyWorker instead).
    57  
    58  func (*undertakerSuite) TestMaybeReleaseAddresses_NoNetworking(c *gc.C) {
    59  	api := fakeAPI{Stub: &testing.Stub{}}
    60  	u := machineundertaker.Undertaker{API: &api}
    61  	err := u.MaybeReleaseAddresses(names.NewMachineTag("3"))
    62  	c.Assert(err, jc.ErrorIsNil)
    63  	api.CheckCallNames(c)
    64  }
    65  
    66  func (*undertakerSuite) TestMaybeReleaseAddresses_NotContainer(c *gc.C) {
    67  	api := fakeAPI{Stub: &testing.Stub{}}
    68  	releaser := fakeReleaser{}
    69  	u := machineundertaker.Undertaker{
    70  		API:      &api,
    71  		Releaser: &releaser,
    72  	}
    73  	err := u.MaybeReleaseAddresses(names.NewMachineTag("4"))
    74  	c.Assert(err, jc.ErrorIsNil)
    75  	api.CheckCallNames(c)
    76  }
    77  
    78  func (*undertakerSuite) TestMaybeReleaseAddresses_ErrorGettingInfo(c *gc.C) {
    79  	api := fakeAPI{Stub: &testing.Stub{}}
    80  	api.SetErrors(errors.New("a funny thing happened on the way"))
    81  	releaser := fakeReleaser{}
    82  	u := machineundertaker.Undertaker{
    83  		API:      &api,
    84  		Releaser: &releaser,
    85  	}
    86  	err := u.MaybeReleaseAddresses(names.NewMachineTag("4/lxd/2"))
    87  	c.Assert(err, gc.ErrorMatches, "a funny thing happened on the way")
    88  }
    89  
    90  func (*undertakerSuite) TestMaybeReleaseAddresses_NoAddresses(c *gc.C) {
    91  	api := fakeAPI{Stub: &testing.Stub{}}
    92  	releaser := fakeReleaser{Stub: &testing.Stub{}}
    93  	u := machineundertaker.Undertaker{
    94  		API:      &api,
    95  		Releaser: &releaser,
    96  	}
    97  	err := u.MaybeReleaseAddresses(names.NewMachineTag("4/lxd/4"))
    98  	c.Assert(err, jc.ErrorIsNil)
    99  	releaser.CheckCallNames(c)
   100  }
   101  
   102  func (*undertakerSuite) TestMaybeReleaseAddresses_NotSupported(c *gc.C) {
   103  	api := fakeAPI{
   104  		Stub: &testing.Stub{},
   105  		interfaces: map[string][]network.ProviderInterfaceInfo{
   106  			"4/lxd/4": []network.ProviderInterfaceInfo{
   107  				{InterfaceName: "chloe"},
   108  			},
   109  		},
   110  	}
   111  	releaser := fakeReleaser{Stub: &testing.Stub{}}
   112  	releaser.SetErrors(errors.NotSupportedf("this sort of thing"))
   113  	u := machineundertaker.Undertaker{
   114  		API:      &api,
   115  		Releaser: &releaser,
   116  	}
   117  	err := u.MaybeReleaseAddresses(names.NewMachineTag("4/lxd/4"))
   118  	c.Assert(err, jc.ErrorIsNil)
   119  	releaser.CheckCall(c, 0, "ReleaseContainerAddresses",
   120  		[]network.ProviderInterfaceInfo{{InterfaceName: "chloe"}},
   121  	)
   122  }
   123  
   124  func (*undertakerSuite) TestMaybeReleaseAddresses_ErrorReleasing(c *gc.C) {
   125  	api := fakeAPI{
   126  		Stub: &testing.Stub{},
   127  		interfaces: map[string][]network.ProviderInterfaceInfo{
   128  			"4/lxd/4": []network.ProviderInterfaceInfo{
   129  				{InterfaceName: "chloe"},
   130  			},
   131  		},
   132  	}
   133  	releaser := fakeReleaser{Stub: &testing.Stub{}}
   134  	releaser.SetErrors(errors.New("something unexpected"))
   135  	u := machineundertaker.Undertaker{
   136  		API:      &api,
   137  		Releaser: &releaser,
   138  	}
   139  	err := u.MaybeReleaseAddresses(names.NewMachineTag("4/lxd/4"))
   140  	c.Assert(err, gc.ErrorMatches, "something unexpected")
   141  	releaser.CheckCall(c, 0, "ReleaseContainerAddresses",
   142  		[]network.ProviderInterfaceInfo{{InterfaceName: "chloe"}},
   143  	)
   144  }
   145  
   146  func (*undertakerSuite) TestMaybeReleaseAddresses_Success(c *gc.C) {
   147  	api := fakeAPI{
   148  		Stub: &testing.Stub{},
   149  		interfaces: map[string][]network.ProviderInterfaceInfo{
   150  			"4/lxd/4": []network.ProviderInterfaceInfo{
   151  				{InterfaceName: "chloe"},
   152  			},
   153  		},
   154  	}
   155  	releaser := fakeReleaser{Stub: &testing.Stub{}}
   156  	u := machineundertaker.Undertaker{
   157  		API:      &api,
   158  		Releaser: &releaser,
   159  	}
   160  	err := u.MaybeReleaseAddresses(names.NewMachineTag("4/lxd/4"))
   161  	c.Assert(err, jc.ErrorIsNil)
   162  	releaser.CheckCall(c, 0, "ReleaseContainerAddresses",
   163  		[]network.ProviderInterfaceInfo{{InterfaceName: "chloe"}},
   164  	)
   165  }
   166  
   167  func (*undertakerSuite) TestHandle_CompletesRemoval(c *gc.C) {
   168  	api := fakeAPI{
   169  		Stub:     &testing.Stub{},
   170  		removals: []string{"3", "4/lxd/4"},
   171  		interfaces: map[string][]network.ProviderInterfaceInfo{
   172  			"4/lxd/4": []network.ProviderInterfaceInfo{
   173  				{InterfaceName: "chloe"},
   174  			},
   175  		},
   176  	}
   177  	releaser := fakeReleaser{Stub: &testing.Stub{}}
   178  	u := machineundertaker.Undertaker{
   179  		API:      &api,
   180  		Releaser: &releaser,
   181  	}
   182  	err := u.Handle(nil)
   183  	c.Assert(err, jc.ErrorIsNil)
   184  
   185  	c.Assert(releaser.Calls(), gc.HasLen, 1)
   186  	releaser.CheckCall(c, 0, "ReleaseContainerAddresses",
   187  		[]network.ProviderInterfaceInfo{{InterfaceName: "chloe"}},
   188  	)
   189  
   190  	checkRemovalsMatch(c, api.Stub, "3", "4/lxd/4")
   191  }
   192  
   193  func (*undertakerSuite) TestHandle_NoRemovalOnErrorReleasing(c *gc.C) {
   194  	api := fakeAPI{
   195  		Stub:     &testing.Stub{},
   196  		removals: []string{"3", "4/lxd/4", "5"},
   197  		interfaces: map[string][]network.ProviderInterfaceInfo{
   198  			"4/lxd/4": []network.ProviderInterfaceInfo{
   199  				{InterfaceName: "chloe"},
   200  			},
   201  		},
   202  	}
   203  	releaser := fakeReleaser{Stub: &testing.Stub{}}
   204  	releaser.SetErrors(errors.New("couldn't release address"))
   205  	u := machineundertaker.Undertaker{
   206  		API:      &api,
   207  		Releaser: &releaser,
   208  	}
   209  	err := u.Handle(nil)
   210  	c.Assert(err, jc.ErrorIsNil)
   211  
   212  	c.Assert(releaser.Calls(), gc.HasLen, 1)
   213  	releaser.CheckCall(c, 0, "ReleaseContainerAddresses",
   214  		[]network.ProviderInterfaceInfo{{InterfaceName: "chloe"}},
   215  	)
   216  
   217  	checkRemovalsMatch(c, api.Stub, "3", "5")
   218  }
   219  
   220  func (*undertakerSuite) TestHandle_ErrorOnRemoval(c *gc.C) {
   221  	api := fakeAPI{
   222  		Stub:     &testing.Stub{},
   223  		removals: []string{"3", "4/lxd/4"},
   224  	}
   225  	api.SetErrors(nil, errors.New("couldn't remove machine 3"))
   226  	u := machineundertaker.Undertaker{API: &api}
   227  	err := u.Handle(nil)
   228  	c.Assert(err, jc.ErrorIsNil)
   229  	checkRemovalsMatch(c, api.Stub, "3", "4/lxd/4")
   230  }
   231  
   232  func checkRemovalsMatch(c *gc.C, stub *testing.Stub, expected ...string) {
   233  	var completedRemovals []string
   234  	for _, call := range stub.Calls() {
   235  		if call.FuncName == "CompleteRemoval" {
   236  			machineId := call.Args[0].(names.MachineTag).Id()
   237  			completedRemovals = append(completedRemovals, machineId)
   238  		}
   239  	}
   240  	c.Check(completedRemovals, gc.DeepEquals, expected)
   241  }
   242  
   243  func (s *undertakerSuite) makeAPIWithWatcher() *fakeAPI {
   244  	return &fakeAPI{
   245  		Stub:    &testing.Stub{},
   246  		watcher: s.newMockNotifyWatcher(),
   247  	}
   248  }
   249  
   250  func (s *undertakerSuite) newMockNotifyWatcher() *mockNotifyWatcher {
   251  	m := &mockNotifyWatcher{
   252  		changes: make(chan struct{}, 1),
   253  	}
   254  	go func() {
   255  		defer m.tomb.Done()
   256  		defer m.tomb.Kill(nil)
   257  		<-m.tomb.Dying()
   258  	}()
   259  	s.AddCleanup(func(c *gc.C) {
   260  		err := worker.Stop(m)
   261  		c.Check(err, jc.ErrorIsNil)
   262  	})
   263  	m.Change()
   264  	return m
   265  }
   266  
   267  type fakeEnviron struct {
   268  	environs.NetworkingEnviron
   269  }
   270  
   271  type fakeNoNetworkingEnviron struct {
   272  	environs.Environ
   273  }
   274  
   275  type fakeReleaser struct {
   276  	*testing.Stub
   277  }
   278  
   279  func (r *fakeReleaser) ReleaseContainerAddresses(interfaces []network.ProviderInterfaceInfo) error {
   280  	r.Stub.AddCall("ReleaseContainerAddresses", interfaces)
   281  	return r.Stub.NextErr()
   282  }
   283  
   284  type fakeAPI struct {
   285  	machineundertaker.Facade
   286  
   287  	*testing.Stub
   288  	watcher    *mockNotifyWatcher
   289  	removals   []string
   290  	interfaces map[string][]network.ProviderInterfaceInfo
   291  }
   292  
   293  func (a *fakeAPI) WatchMachineRemovals() (watcher.NotifyWatcher, error) {
   294  	a.Stub.AddCall("WatchMachineRemovals")
   295  	return a.watcher, a.Stub.NextErr()
   296  }
   297  
   298  func (a *fakeAPI) AllMachineRemovals() ([]names.MachineTag, error) {
   299  	a.Stub.AddCall("AllMachineRemovals")
   300  	result := make([]names.MachineTag, len(a.removals))
   301  	for i := range a.removals {
   302  		result[i] = names.NewMachineTag(a.removals[i])
   303  	}
   304  	return result, a.Stub.NextErr()
   305  }
   306  
   307  func (a *fakeAPI) GetProviderInterfaceInfo(machine names.MachineTag) ([]network.ProviderInterfaceInfo, error) {
   308  	a.Stub.AddCall("GetProviderInterfaceInfo", machine)
   309  	return a.interfaces[machine.Id()], a.Stub.NextErr()
   310  }
   311  
   312  func (a *fakeAPI) CompleteRemoval(machine names.MachineTag) error {
   313  	a.Stub.AddCall("CompleteRemoval", machine)
   314  	return a.Stub.NextErr()
   315  }
   316  
   317  type mockNotifyWatcher struct {
   318  	watcher.NotifyWatcher
   319  
   320  	tomb    tomb.Tomb
   321  	changes chan struct{}
   322  }
   323  
   324  func (m *mockNotifyWatcher) Kill() {
   325  	m.tomb.Kill(nil)
   326  }
   327  
   328  func (m *mockNotifyWatcher) Wait() error {
   329  	return m.tomb.Wait()
   330  }
   331  
   332  func (m *mockNotifyWatcher) Changes() watcher.NotifyChannel {
   333  	return m.changes
   334  }
   335  
   336  func (m *mockNotifyWatcher) Change() {
   337  	m.changes <- struct{}{}
   338  }