github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/migrationminion/worker_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package migrationminion_test 5 6 import ( 7 "reflect" 8 "sync" 9 "time" 10 11 "github.com/juju/errors" 12 jujutesting "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 gc "gopkg.in/check.v1" 15 "gopkg.in/juju/names.v2" 16 "gopkg.in/juju/worker.v1" 17 "gopkg.in/juju/worker.v1/workertest" 18 19 "github.com/juju/juju/agent" 20 "github.com/juju/juju/api" 21 "github.com/juju/juju/api/base" 22 "github.com/juju/juju/core/migration" 23 "github.com/juju/juju/core/watcher" 24 "github.com/juju/juju/network" 25 coretesting "github.com/juju/juju/testing" 26 "github.com/juju/juju/worker/fortress" 27 "github.com/juju/juju/worker/migrationminion" 28 ) 29 30 var ( 31 modelTag = names.NewModelTag("model-uuid") 32 addrs = []string{"1.1.1.1:1111", "2.2.2.2:2222"} 33 agentTag = names.NewMachineTag("42") 34 agentPassword = "sekret" 35 caCert = "cert" 36 ) 37 38 type Suite struct { 39 coretesting.BaseSuite 40 config migrationminion.Config 41 stub *jujutesting.Stub 42 client *stubMinionClient 43 guard *stubGuard 44 agent *stubAgent 45 } 46 47 var _ = gc.Suite(&Suite{}) 48 49 func (s *Suite) SetUpTest(c *gc.C) { 50 s.BaseSuite.SetUpTest(c) 51 s.stub = new(jujutesting.Stub) 52 s.client = newStubMinionClient(s.stub) 53 s.guard = newStubGuard(s.stub) 54 s.agent = newStubAgent() 55 s.config = migrationminion.Config{ 56 Facade: s.client, 57 Guard: s.guard, 58 Agent: s.agent, 59 APIOpen: s.apiOpen, 60 ValidateMigration: func(base.APICaller) error { 61 s.stub.AddCall("ValidateMigration") 62 return nil 63 }, 64 } 65 } 66 67 func (s *Suite) apiOpen(info *api.Info, dialOpts api.DialOpts) (api.Connection, error) { 68 s.stub.AddCall("API open", info) 69 return &stubConnection{stub: s.stub}, nil 70 } 71 72 func (s *Suite) TestStartAndStop(c *gc.C) { 73 w, err := migrationminion.New(s.config) 74 c.Assert(err, jc.ErrorIsNil) 75 workertest.CleanKill(c, w) 76 s.stub.CheckCallNames(c, "Watch") 77 } 78 79 func (s *Suite) TestWatchFailure(c *gc.C) { 80 s.client.watchErr = errors.New("boom") 81 w, err := migrationminion.New(s.config) 82 c.Assert(err, jc.ErrorIsNil) 83 err = workertest.CheckKilled(c, w) 84 c.Check(err, gc.ErrorMatches, "setting up watcher: boom") 85 } 86 87 func (s *Suite) TestClosedWatcherChannel(c *gc.C) { 88 close(s.client.watcher.changes) 89 w, err := migrationminion.New(s.config) 90 c.Assert(err, jc.ErrorIsNil) 91 err = workertest.CheckKilled(c, w) 92 c.Check(err, gc.ErrorMatches, "watcher channel closed") 93 } 94 95 func (s *Suite) TestUnlockError(c *gc.C) { 96 s.client.watcher.changes <- watcher.MigrationStatus{ 97 Phase: migration.NONE, 98 } 99 s.guard.unlockErr = errors.New("squish") 100 w, err := migrationminion.New(s.config) 101 c.Assert(err, jc.ErrorIsNil) 102 103 err = workertest.CheckKilled(c, w) 104 c.Check(err, gc.ErrorMatches, "squish") 105 s.stub.CheckCallNames(c, "Watch", "Unlock") 106 } 107 108 func (s *Suite) TestLockdownError(c *gc.C) { 109 s.client.watcher.changes <- watcher.MigrationStatus{ 110 Phase: migration.QUIESCE, 111 } 112 s.guard.lockdownErr = errors.New("squash") 113 w, err := migrationminion.New(s.config) 114 c.Assert(err, jc.ErrorIsNil) 115 116 err = workertest.CheckKilled(c, w) 117 c.Check(err, gc.ErrorMatches, "squash") 118 s.stub.CheckCallNames(c, "Watch", "Lockdown") 119 } 120 121 func (s *Suite) TestNonRunningPhases(c *gc.C) { 122 phases := []migration.Phase{ 123 migration.UNKNOWN, 124 migration.NONE, 125 migration.LOGTRANSFER, 126 migration.REAP, 127 migration.REAPFAILED, 128 migration.DONE, 129 migration.ABORT, 130 migration.ABORTDONE, 131 } 132 for _, phase := range phases { 133 s.checkNonRunningPhase(c, phase) 134 } 135 } 136 137 func (s *Suite) checkNonRunningPhase(c *gc.C, phase migration.Phase) { 138 c.Logf("checking %s", phase) 139 s.stub.ResetCalls() 140 s.client.watcher.changes <- watcher.MigrationStatus{Phase: phase} 141 w, err := migrationminion.New(s.config) 142 c.Assert(err, jc.ErrorIsNil) 143 workertest.CheckAlive(c, w) 144 workertest.CleanKill(c, w) 145 s.stub.CheckCallNames(c, "Watch", "Unlock") 146 } 147 148 func (s *Suite) TestQUIESCE(c *gc.C) { 149 s.client.watcher.changes <- watcher.MigrationStatus{ 150 MigrationId: "id", 151 Phase: migration.QUIESCE, 152 } 153 w, err := migrationminion.New(s.config) 154 c.Assert(err, jc.ErrorIsNil) 155 defer workertest.CleanKill(c, w) 156 157 s.waitForStubCalls(c, []string{ 158 "Watch", 159 "Lockdown", 160 "Report", 161 }) 162 s.stub.CheckCall(c, 2, "Report", "id", migration.QUIESCE, true) 163 } 164 165 func (s *Suite) TestVALIDATION(c *gc.C) { 166 s.client.watcher.changes <- watcher.MigrationStatus{ 167 MigrationId: "id", 168 Phase: migration.VALIDATION, 169 TargetAPIAddrs: addrs, 170 TargetCACert: caCert, 171 } 172 w, err := migrationminion.New(s.config) 173 c.Assert(err, jc.ErrorIsNil) 174 defer workertest.CleanKill(c, w) 175 176 s.waitForStubCalls(c, []string{ 177 "Watch", 178 "Lockdown", 179 "API open", 180 "ValidateMigration", 181 "API close", 182 "Report", 183 }) 184 s.stub.CheckCall(c, 2, "API open", &api.Info{ 185 ModelTag: modelTag, 186 Tag: agentTag, 187 Password: agentPassword, 188 Addrs: addrs, 189 CACert: caCert, 190 }) 191 s.stub.CheckCall(c, 5, "Report", "id", migration.VALIDATION, true) 192 } 193 194 func (s *Suite) TestVALIDATIONCantConnect(c *gc.C) { 195 s.client.watcher.changes <- watcher.MigrationStatus{ 196 MigrationId: "id", 197 Phase: migration.VALIDATION, 198 } 199 s.config.APIOpen = func(*api.Info, api.DialOpts) (api.Connection, error) { 200 s.stub.AddCall("API open") 201 return nil, errors.New("boom") 202 } 203 w, err := migrationminion.New(s.config) 204 c.Assert(err, jc.ErrorIsNil) 205 defer workertest.CleanKill(c, w) 206 207 s.waitForStubCalls(c, []string{ 208 "Watch", 209 "Lockdown", 210 "API open", 211 "Report", 212 }) 213 s.stub.CheckCall(c, 3, "Report", "id", migration.VALIDATION, false) 214 } 215 216 func (s *Suite) TestVALIDATIONFail(c *gc.C) { 217 s.client.watcher.changes <- watcher.MigrationStatus{ 218 MigrationId: "id", 219 Phase: migration.VALIDATION, 220 } 221 s.config.ValidateMigration = func(base.APICaller) error { 222 s.stub.AddCall("ValidateMigration") 223 return errors.New("boom") 224 } 225 w, err := migrationminion.New(s.config) 226 c.Assert(err, jc.ErrorIsNil) 227 defer workertest.CleanKill(c, w) 228 229 s.waitForStubCalls(c, []string{ 230 "Watch", 231 "Lockdown", 232 "API open", 233 "ValidateMigration", 234 "API close", 235 "Report", 236 }) 237 s.stub.CheckCall(c, 5, "Report", "id", migration.VALIDATION, false) 238 } 239 240 func (s *Suite) TestSUCCESS(c *gc.C) { 241 s.client.watcher.changes <- watcher.MigrationStatus{ 242 MigrationId: "id", 243 Phase: migration.SUCCESS, 244 TargetAPIAddrs: addrs, 245 TargetCACert: caCert, 246 } 247 w, err := migrationminion.New(s.config) 248 c.Assert(err, jc.ErrorIsNil) 249 250 select { 251 case <-s.agent.configChanged: 252 case <-time.After(coretesting.LongWait): 253 c.Fatal("timed out") 254 } 255 workertest.CleanKill(c, w) 256 c.Assert(s.agent.conf.addrs, gc.DeepEquals, addrs) 257 c.Assert(s.agent.conf.caCert, gc.DeepEquals, caCert) 258 s.stub.CheckCallNames(c, "Watch", "Lockdown", "Report") 259 s.stub.CheckCall(c, 2, "Report", "id", migration.SUCCESS, true) 260 } 261 262 func (s *Suite) waitForStubCalls(c *gc.C, expectedCallNames []string) { 263 var callNames []string 264 for a := coretesting.LongAttempt.Start(); a.Next(); { 265 callNames = stubCallNames(s.stub) 266 if reflect.DeepEqual(callNames, expectedCallNames) { 267 return 268 } 269 } 270 c.Fatalf("failed to see expected calls. saw: %v", callNames) 271 } 272 273 // Make this a feature of stub 274 func stubCallNames(stub *jujutesting.Stub) []string { 275 var out []string 276 for _, call := range stub.Calls() { 277 out = append(out, call.FuncName) 278 } 279 return out 280 } 281 282 func newStubGuard(stub *jujutesting.Stub) *stubGuard { 283 return &stubGuard{stub: stub} 284 } 285 286 type stubGuard struct { 287 stub *jujutesting.Stub 288 unlockErr error 289 lockdownErr error 290 } 291 292 func (g *stubGuard) Lockdown(fortress.Abort) error { 293 g.stub.AddCall("Lockdown") 294 return g.lockdownErr 295 } 296 297 func (g *stubGuard) Unlock() error { 298 g.stub.AddCall("Unlock") 299 return g.unlockErr 300 } 301 302 func newStubMinionClient(stub *jujutesting.Stub) *stubMinionClient { 303 return &stubMinionClient{ 304 stub: stub, 305 watcher: newStubWatcher(), 306 } 307 } 308 309 type stubMinionClient struct { 310 stub *jujutesting.Stub 311 watcher *stubWatcher 312 watchErr error 313 } 314 315 func (c *stubMinionClient) Watch() (watcher.MigrationStatusWatcher, error) { 316 c.stub.MethodCall(c, "Watch") 317 if c.watchErr != nil { 318 return nil, c.watchErr 319 } 320 return c.watcher, nil 321 } 322 323 func (c *stubMinionClient) Report(id string, phase migration.Phase, success bool) error { 324 c.stub.MethodCall(c, "Report", id, phase, success) 325 return nil 326 } 327 328 func newStubWatcher() *stubWatcher { 329 return &stubWatcher{ 330 Worker: workertest.NewErrorWorker(nil), 331 changes: make(chan watcher.MigrationStatus, 1), 332 } 333 } 334 335 type stubWatcher struct { 336 worker.Worker 337 changes chan watcher.MigrationStatus 338 } 339 340 func (w *stubWatcher) Changes() <-chan watcher.MigrationStatus { 341 return w.changes 342 } 343 344 func newStubAgent() *stubAgent { 345 return &stubAgent{ 346 configChanged: make(chan bool), 347 } 348 } 349 350 type stubAgent struct { 351 agent.Agent 352 configChanged chan bool 353 conf stubAgentConfig 354 } 355 356 func (ma *stubAgent) CurrentConfig() agent.Config { 357 return &ma.conf 358 } 359 360 func (ma *stubAgent) ChangeConfig(f agent.ConfigMutator) error { 361 defer close(ma.configChanged) 362 return f(&ma.conf) 363 } 364 365 type stubAgentConfig struct { 366 agent.ConfigSetter 367 368 mu sync.Mutex 369 addrs []string 370 caCert string 371 } 372 373 func (mc *stubAgentConfig) SetAPIHostPorts(servers [][]network.HostPort) { 374 mc.mu.Lock() 375 defer mc.mu.Unlock() 376 mc.addrs = nil 377 for _, hps := range servers { 378 for _, hp := range hps { 379 mc.addrs = append(mc.addrs, hp.NetAddr()) 380 } 381 } 382 } 383 384 func (mc *stubAgentConfig) SetCACert(cert string) { 385 mc.mu.Lock() 386 defer mc.mu.Unlock() 387 mc.caCert = cert 388 } 389 390 func (mc *stubAgentConfig) APIInfo() (*api.Info, bool) { 391 mc.mu.Lock() 392 defer mc.mu.Unlock() 393 return &api.Info{ 394 Addrs: mc.addrs, 395 CACert: mc.caCert, 396 ModelTag: modelTag, 397 Tag: agentTag, 398 Password: agentPassword, 399 }, true 400 } 401 402 type stubConnection struct { 403 api.Connection 404 stub *jujutesting.Stub 405 } 406 407 func (c *stubConnection) Close() error { 408 c.stub.AddCall("API close") 409 return nil 410 }