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 }