github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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/juju/worker.v1"
    13  	"gopkg.in/juju/worker.v1/workertest"
    14  	"gopkg.in/tomb.v2"
    15  
    16  	"github.com/juju/juju/core/watcher"
    17  	"github.com/juju/juju/environs"
    18  	"github.com/juju/juju/environs/context"
    19  	"github.com/juju/juju/network"
    20  	"github.com/juju/juju/worker/machineundertaker"
    21  )
    22  
    23  type undertakerSuite struct {
    24  	testing.IsolationSuite
    25  }
    26  
    27  var _ = gc.Suite(&undertakerSuite{})
    28  
    29  // Some tests to check that the handler is wired up to the
    30  // NotifyWorker first.
    31  
    32  func (s *undertakerSuite) TestErrorWatching(c *gc.C) {
    33  	api := s.makeAPIWithWatcher()
    34  	api.SetErrors(errors.New("blam"))
    35  	w, err := machineundertaker.NewWorker(api, &fakeEnviron{}, &fakeCredentialAPI{})
    36  	c.Assert(err, jc.ErrorIsNil)
    37  	err = workertest.CheckKilled(c, w)
    38  	c.Check(err, gc.ErrorMatches, "blam")
    39  	api.CheckCallNames(c, "WatchMachineRemovals")
    40  }
    41  
    42  func (s *undertakerSuite) TestErrorGettingRemovals(c *gc.C) {
    43  	api := s.makeAPIWithWatcher()
    44  	api.SetErrors(nil, errors.New("explodo"))
    45  	w, err := machineundertaker.NewWorker(api, &fakeEnviron{}, &fakeCredentialAPI{})
    46  	c.Assert(err, jc.ErrorIsNil)
    47  	err = workertest.CheckKilled(c, w)
    48  	c.Check(err, gc.ErrorMatches, "explodo")
    49  	api.CheckCallNames(c, "WatchMachineRemovals", "AllMachineRemovals")
    50  }
    51  
    52  // It's really fiddly trying to test the code behind the worker, so
    53  // the rest of the tests use the Undertaker directly to test the
    54  // Handle and MaybeReleaseAddresses methods. This is much simpler
    55  // because everything happens in the same goroutine (and it's safe
    56  // since all of the clever/tricky lifecycle management is taken care
    57  // of in NotifyWorker instead).
    58  
    59  func (*undertakerSuite) TestMaybeReleaseAddresses_NoNetworking(c *gc.C) {
    60  	api := fakeAPI{Stub: &testing.Stub{}}
    61  	u := machineundertaker.Undertaker{API: &api}
    62  	err := u.MaybeReleaseAddresses(names.NewMachineTag("3"))
    63  	c.Assert(err, jc.ErrorIsNil)
    64  	api.CheckCallNames(c)
    65  }
    66  
    67  func (*undertakerSuite) TestMaybeReleaseAddresses_NotContainer(c *gc.C) {
    68  	api := fakeAPI{Stub: &testing.Stub{}}
    69  	releaser := fakeReleaser{}
    70  	u := machineundertaker.Undertaker{
    71  		API:      &api,
    72  		Releaser: &releaser,
    73  	}
    74  	err := u.MaybeReleaseAddresses(names.NewMachineTag("4"))
    75  	c.Assert(err, jc.ErrorIsNil)
    76  	api.CheckCallNames(c)
    77  }
    78  
    79  func (*undertakerSuite) TestMaybeReleaseAddresses_ErrorGettingInfo(c *gc.C) {
    80  	api := fakeAPI{Stub: &testing.Stub{}}
    81  	api.SetErrors(errors.New("a funny thing happened on the way"))
    82  	releaser := fakeReleaser{}
    83  	u := machineundertaker.Undertaker{
    84  		API:      &api,
    85  		Releaser: &releaser,
    86  	}
    87  	err := u.MaybeReleaseAddresses(names.NewMachineTag("4/lxd/2"))
    88  	c.Assert(err, gc.ErrorMatches, "a funny thing happened on the way")
    89  }
    90  
    91  func (*undertakerSuite) TestMaybeReleaseAddresses_NoAddresses(c *gc.C) {
    92  	api := fakeAPI{Stub: &testing.Stub{}}
    93  	releaser := fakeReleaser{Stub: &testing.Stub{}}
    94  	u := machineundertaker.Undertaker{
    95  		API:      &api,
    96  		Releaser: &releaser,
    97  	}
    98  	err := u.MaybeReleaseAddresses(names.NewMachineTag("4/lxd/4"))
    99  	c.Assert(err, jc.ErrorIsNil)
   100  	releaser.CheckCallNames(c)
   101  }
   102  
   103  func (*undertakerSuite) TestMaybeReleaseAddresses_NotSupported(c *gc.C) {
   104  	api := fakeAPI{
   105  		Stub: &testing.Stub{},
   106  		interfaces: map[string][]network.ProviderInterfaceInfo{
   107  			"4/lxd/4": {
   108  				{InterfaceName: "chloe"},
   109  			},
   110  		},
   111  	}
   112  	releaser := fakeReleaser{Stub: &testing.Stub{}}
   113  	releaser.SetErrors(errors.NotSupportedf("this sort of thing"))
   114  	u := machineundertaker.Undertaker{
   115  		API:      &api,
   116  		Releaser: &releaser,
   117  	}
   118  	err := u.MaybeReleaseAddresses(names.NewMachineTag("4/lxd/4"))
   119  	c.Assert(err, jc.ErrorIsNil)
   120  	releaser.CheckCall(c, 0, "ReleaseContainerAddresses",
   121  		[]network.ProviderInterfaceInfo{{InterfaceName: "chloe"}},
   122  	)
   123  }
   124  
   125  func (*undertakerSuite) TestMaybeReleaseAddresses_ErrorReleasing(c *gc.C) {
   126  	api := fakeAPI{
   127  		Stub: &testing.Stub{},
   128  		interfaces: map[string][]network.ProviderInterfaceInfo{
   129  			"4/lxd/4": {
   130  				{InterfaceName: "chloe"},
   131  			},
   132  		},
   133  	}
   134  	releaser := fakeReleaser{Stub: &testing.Stub{}}
   135  	releaser.SetErrors(errors.New("something unexpected"))
   136  	u := machineundertaker.Undertaker{
   137  		API:      &api,
   138  		Releaser: &releaser,
   139  	}
   140  	err := u.MaybeReleaseAddresses(names.NewMachineTag("4/lxd/4"))
   141  	c.Assert(err, gc.ErrorMatches, "something unexpected")
   142  	releaser.CheckCall(c, 0, "ReleaseContainerAddresses",
   143  		[]network.ProviderInterfaceInfo{{InterfaceName: "chloe"}},
   144  	)
   145  }
   146  
   147  func (*undertakerSuite) TestMaybeReleaseAddresses_Success(c *gc.C) {
   148  	api := fakeAPI{
   149  		Stub: &testing.Stub{},
   150  		interfaces: map[string][]network.ProviderInterfaceInfo{
   151  			"4/lxd/4": {
   152  				{InterfaceName: "chloe"},
   153  			},
   154  		},
   155  	}
   156  	releaser := fakeReleaser{Stub: &testing.Stub{}}
   157  	u := machineundertaker.Undertaker{
   158  		API:      &api,
   159  		Releaser: &releaser,
   160  	}
   161  	err := u.MaybeReleaseAddresses(names.NewMachineTag("4/lxd/4"))
   162  	c.Assert(err, jc.ErrorIsNil)
   163  	releaser.CheckCall(c, 0, "ReleaseContainerAddresses",
   164  		[]network.ProviderInterfaceInfo{{InterfaceName: "chloe"}},
   165  	)
   166  }
   167  
   168  func (*undertakerSuite) TestHandle_CompletesRemoval(c *gc.C) {
   169  	api := fakeAPI{
   170  		Stub:     &testing.Stub{},
   171  		removals: []string{"3", "4/lxd/4"},
   172  		interfaces: map[string][]network.ProviderInterfaceInfo{
   173  			"4/lxd/4": {
   174  				{InterfaceName: "chloe"},
   175  			},
   176  		},
   177  	}
   178  	releaser := fakeReleaser{Stub: &testing.Stub{}}
   179  	u := machineundertaker.Undertaker{
   180  		API:      &api,
   181  		Releaser: &releaser,
   182  	}
   183  	err := u.Handle(nil)
   184  	c.Assert(err, jc.ErrorIsNil)
   185  
   186  	c.Assert(releaser.Calls(), gc.HasLen, 1)
   187  	releaser.CheckCall(c, 0, "ReleaseContainerAddresses",
   188  		[]network.ProviderInterfaceInfo{{InterfaceName: "chloe"}},
   189  	)
   190  
   191  	checkRemovalsMatch(c, api.Stub, "3", "4/lxd/4")
   192  }
   193  
   194  func (*undertakerSuite) TestHandle_NoRemovalOnErrorReleasing(c *gc.C) {
   195  	api := fakeAPI{
   196  		Stub:     &testing.Stub{},
   197  		removals: []string{"3", "4/lxd/4", "5"},
   198  		interfaces: map[string][]network.ProviderInterfaceInfo{
   199  			"4/lxd/4": {
   200  				{InterfaceName: "chloe"},
   201  			},
   202  		},
   203  	}
   204  	releaser := fakeReleaser{Stub: &testing.Stub{}}
   205  	releaser.SetErrors(errors.New("couldn't release address"))
   206  	u := machineundertaker.Undertaker{
   207  		API:      &api,
   208  		Releaser: &releaser,
   209  	}
   210  	err := u.Handle(nil)
   211  	c.Assert(err, jc.ErrorIsNil)
   212  
   213  	c.Assert(releaser.Calls(), gc.HasLen, 1)
   214  	releaser.CheckCall(c, 0, "ReleaseContainerAddresses",
   215  		[]network.ProviderInterfaceInfo{{InterfaceName: "chloe"}},
   216  	)
   217  
   218  	checkRemovalsMatch(c, api.Stub, "3", "5")
   219  }
   220  
   221  func (*undertakerSuite) TestHandle_ErrorOnRemoval(c *gc.C) {
   222  	api := fakeAPI{
   223  		Stub:     &testing.Stub{},
   224  		removals: []string{"3", "4/lxd/4"},
   225  	}
   226  	api.SetErrors(nil, errors.New("couldn't remove machine 3"))
   227  	u := machineundertaker.Undertaker{API: &api}
   228  	err := u.Handle(nil)
   229  	c.Assert(err, jc.ErrorIsNil)
   230  	checkRemovalsMatch(c, api.Stub, "3", "4/lxd/4")
   231  }
   232  
   233  func checkRemovalsMatch(c *gc.C, stub *testing.Stub, expected ...string) {
   234  	var completedRemovals []string
   235  	for _, call := range stub.Calls() {
   236  		if call.FuncName == "CompleteRemoval" {
   237  			machineId := call.Args[0].(names.MachineTag).Id()
   238  			completedRemovals = append(completedRemovals, machineId)
   239  		}
   240  	}
   241  	c.Check(completedRemovals, gc.DeepEquals, expected)
   242  }
   243  
   244  func (s *undertakerSuite) makeAPIWithWatcher() *fakeAPI {
   245  	return &fakeAPI{
   246  		Stub:    &testing.Stub{},
   247  		watcher: s.newMockNotifyWatcher(),
   248  	}
   249  }
   250  
   251  func (s *undertakerSuite) newMockNotifyWatcher() *mockNotifyWatcher {
   252  	m := &mockNotifyWatcher{
   253  		changes: make(chan struct{}, 1),
   254  	}
   255  	m.tomb.Go(func() error {
   256  		<-m.tomb.Dying()
   257  		return nil
   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(ctx context.ProviderCallContext, 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  }