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 }