github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/leadership/tracker_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package leadership_test 5 6 import ( 7 "time" 8 9 "github.com/juju/errors" 10 "github.com/juju/names" 11 "github.com/juju/testing" 12 jc "github.com/juju/testing/checkers" 13 gc "gopkg.in/check.v1" 14 15 coreleadership "github.com/juju/juju/core/leadership" 16 coretesting "github.com/juju/juju/testing" 17 "github.com/juju/juju/worker" 18 "github.com/juju/juju/worker/leadership" 19 ) 20 21 type TrackerSuite struct { 22 testing.IsolationSuite 23 unitTag names.UnitTag 24 claimer *StubClaimer 25 } 26 27 var _ = gc.Suite(&TrackerSuite{}) 28 29 const ( 30 trackerDuration = coretesting.ShortWait 31 leaseDuration = trackerDuration * 2 32 ) 33 34 func refreshes(count int) time.Duration { 35 halfRefreshes := (2 * count) + 1 36 twiceDuration := trackerDuration * time.Duration(halfRefreshes) 37 return twiceDuration / 2 38 } 39 40 func (s *TrackerSuite) SetUpTest(c *gc.C) { 41 s.IsolationSuite.SetUpTest(c) 42 s.unitTag = names.NewUnitTag("led-service/123") 43 s.claimer = &StubClaimer{ 44 Stub: &testing.Stub{}, 45 releases: make(chan struct{}), 46 } 47 } 48 49 func (s *TrackerSuite) TearDownTest(c *gc.C) { 50 if s.claimer != nil { 51 // It's not impossible that there's a goroutine waiting for a 52 // BlockUntilLeadershipReleased. Make sure it completes. 53 close(s.claimer.releases) 54 s.claimer = nil 55 } 56 s.IsolationSuite.TearDownTest(c) 57 } 58 59 func (s *TrackerSuite) unblockRelease(c *gc.C) { 60 select { 61 case s.claimer.releases <- struct{}{}: 62 case <-time.After(coretesting.LongWait): 63 c.Fatalf("did nobody call BlockUntilLeadershipReleased?") 64 } 65 } 66 67 func (s *TrackerSuite) TestServiceName(c *gc.C) { 68 tracker := leadership.NewTracker(s.unitTag, s.claimer, trackerDuration) 69 defer assertStop(c, tracker) 70 c.Assert(tracker.ServiceName(), gc.Equals, "led-service") 71 } 72 73 func (s *TrackerSuite) TestOnLeaderSuccess(c *gc.C) { 74 tracker := leadership.NewTracker(s.unitTag, s.claimer, trackerDuration) 75 defer assertStop(c, tracker) 76 77 // Check the ticket succeeds. 78 assertClaimLeader(c, tracker, true) 79 80 // Stop the tracker before trying to look at its stub. 81 assertStop(c, tracker) 82 s.claimer.CheckCalls(c, []testing.StubCall{{ 83 FuncName: "ClaimLeadership", 84 Args: []interface{}{ 85 "led-service", "led-service/123", leaseDuration, 86 }, 87 }}) 88 } 89 90 func (s *TrackerSuite) TestOnLeaderFailure(c *gc.C) { 91 s.claimer.Stub.SetErrors(coreleadership.ErrClaimDenied, nil) 92 tracker := leadership.NewTracker(s.unitTag, s.claimer, trackerDuration) 93 defer assertStop(c, tracker) 94 95 // Check the ticket fails. 96 assertClaimLeader(c, tracker, false) 97 98 // Stop the tracker before trying to look at its mocks. 99 assertStop(c, tracker) 100 101 // Unblock the release goroutine, lest data races. 102 s.unblockRelease(c) 103 104 s.claimer.CheckCalls(c, []testing.StubCall{{ 105 FuncName: "ClaimLeadership", 106 Args: []interface{}{ 107 "led-service", "led-service/123", leaseDuration, 108 }, 109 }, { 110 FuncName: "BlockUntilLeadershipReleased", 111 Args: []interface{}{ 112 "led-service", 113 }, 114 }}) 115 } 116 117 func (s *TrackerSuite) TestOnLeaderError(c *gc.C) { 118 s.claimer.Stub.SetErrors(errors.New("pow")) 119 tracker := leadership.NewTracker(s.unitTag, s.claimer, trackerDuration) 120 defer worker.Stop(tracker) 121 122 // Check the ticket fails. 123 assertClaimLeader(c, tracker, false) 124 125 // Stop the tracker before trying to look at its mocks. 126 err := worker.Stop(tracker) 127 c.Check(err, gc.ErrorMatches, "leadership failure: pow") 128 s.claimer.CheckCalls(c, []testing.StubCall{{ 129 FuncName: "ClaimLeadership", 130 Args: []interface{}{ 131 "led-service", "led-service/123", leaseDuration, 132 }, 133 }}) 134 } 135 136 func (s *TrackerSuite) TestLoseLeadership(c *gc.C) { 137 s.claimer.Stub.SetErrors(nil, coreleadership.ErrClaimDenied, nil) 138 tracker := leadership.NewTracker(s.unitTag, s.claimer, trackerDuration) 139 defer assertStop(c, tracker) 140 141 // Check the first ticket succeeds. 142 assertClaimLeader(c, tracker, true) 143 144 // Wait long enough for a single refresh, to trigger ErrClaimDenied; then 145 // check the next ticket fails. 146 <-time.After(refreshes(1)) 147 assertClaimLeader(c, tracker, false) 148 149 // Stop the tracker before trying to look at its stub. 150 assertStop(c, tracker) 151 152 // Unblock the release goroutine, lest data races. 153 s.unblockRelease(c) 154 155 s.claimer.CheckCalls(c, []testing.StubCall{{ 156 FuncName: "ClaimLeadership", 157 Args: []interface{}{ 158 "led-service", "led-service/123", leaseDuration, 159 }, 160 }, { 161 FuncName: "ClaimLeadership", 162 Args: []interface{}{ 163 "led-service", "led-service/123", leaseDuration, 164 }, 165 }, { 166 FuncName: "BlockUntilLeadershipReleased", 167 Args: []interface{}{ 168 "led-service", 169 }, 170 }}) 171 } 172 173 func (s *TrackerSuite) TestGainLeadership(c *gc.C) { 174 s.claimer.Stub.SetErrors(coreleadership.ErrClaimDenied, nil, nil) 175 tracker := leadership.NewTracker(s.unitTag, s.claimer, trackerDuration) 176 defer assertStop(c, tracker) 177 178 // Check initial ticket fails. 179 assertClaimLeader(c, tracker, false) 180 181 // Unblock the release goroutine... 182 s.unblockRelease(c) 183 184 // ...and, uh, voodoo sleep a bit, but not long enough to trigger a refresh... 185 <-time.After(refreshes(0)) 186 187 // ...then check the next ticket succeeds. 188 assertClaimLeader(c, tracker, true) 189 190 // Stop the tracker before trying to look at its stub. 191 assertStop(c, tracker) 192 s.claimer.CheckCalls(c, []testing.StubCall{{ 193 FuncName: "ClaimLeadership", 194 Args: []interface{}{ 195 "led-service", "led-service/123", leaseDuration, 196 }, 197 }, { 198 FuncName: "BlockUntilLeadershipReleased", 199 Args: []interface{}{ 200 "led-service", 201 }, 202 }, { 203 FuncName: "ClaimLeadership", 204 Args: []interface{}{ 205 "led-service", "led-service/123", leaseDuration, 206 }, 207 }}) 208 } 209 210 func (s *TrackerSuite) TestFailGainLeadership(c *gc.C) { 211 s.claimer.Stub.SetErrors( 212 coreleadership.ErrClaimDenied, nil, coreleadership.ErrClaimDenied, nil, 213 ) 214 tracker := leadership.NewTracker(s.unitTag, s.claimer, trackerDuration) 215 defer assertStop(c, tracker) 216 217 // Check initial ticket fails. 218 assertClaimLeader(c, tracker, false) 219 220 // Unblock the release goroutine... 221 s.unblockRelease(c) 222 223 // ...and, uh, voodoo sleep a bit, but not long enough to trigger a refresh... 224 <-time.After(refreshes(0)) 225 226 // ...then check the next ticket fails again. 227 assertClaimLeader(c, tracker, false) 228 229 // This time, sleep long enough that a refresh would trigger if it were 230 // going to... 231 <-time.After(refreshes(1)) 232 233 // ...but it won't, because we Stop the tracker... 234 assertStop(c, tracker) 235 236 // ...and clear out the release goroutine before we look at the stub. 237 s.unblockRelease(c) 238 239 s.claimer.CheckCalls(c, []testing.StubCall{{ 240 FuncName: "ClaimLeadership", 241 Args: []interface{}{ 242 "led-service", "led-service/123", leaseDuration, 243 }, 244 }, { 245 FuncName: "BlockUntilLeadershipReleased", 246 Args: []interface{}{ 247 "led-service", 248 }, 249 }, { 250 FuncName: "ClaimLeadership", 251 Args: []interface{}{ 252 "led-service", "led-service/123", leaseDuration, 253 }, 254 }, { 255 FuncName: "BlockUntilLeadershipReleased", 256 Args: []interface{}{ 257 "led-service", 258 }, 259 }}) 260 } 261 262 func (s *TrackerSuite) TestWaitLeaderAlreadyLeader(c *gc.C) { 263 tracker := leadership.NewTracker(s.unitTag, s.claimer, trackerDuration) 264 defer assertStop(c, tracker) 265 266 // Check the ticket succeeds. 267 assertWaitLeader(c, tracker, true) 268 269 // Stop the tracker before trying to look at its stub. 270 assertStop(c, tracker) 271 s.claimer.CheckCalls(c, []testing.StubCall{{ 272 FuncName: "ClaimLeadership", 273 Args: []interface{}{ 274 "led-service", "led-service/123", leaseDuration, 275 }, 276 }}) 277 } 278 279 func (s *TrackerSuite) TestWaitLeaderBecomeLeader(c *gc.C) { 280 s.claimer.Stub.SetErrors(coreleadership.ErrClaimDenied, nil, nil) 281 tracker := leadership.NewTracker(s.unitTag, s.claimer, trackerDuration) 282 defer assertStop(c, tracker) 283 284 // Check initial ticket fails. 285 assertWaitLeader(c, tracker, false) 286 287 // Unblock the release goroutine... 288 s.unblockRelease(c) 289 290 // ...and, uh, voodoo sleep a bit, but not long enough to trigger a refresh... 291 <-time.After(refreshes(0)) 292 293 // ...then check the next ticket succeeds. 294 assertWaitLeader(c, tracker, true) 295 296 // Stop the tracker before trying to look at its stub. 297 assertStop(c, tracker) 298 s.claimer.CheckCalls(c, []testing.StubCall{{ 299 FuncName: "ClaimLeadership", 300 Args: []interface{}{ 301 "led-service", "led-service/123", leaseDuration, 302 }, 303 }, { 304 FuncName: "BlockUntilLeadershipReleased", 305 Args: []interface{}{ 306 "led-service", 307 }, 308 }, { 309 FuncName: "ClaimLeadership", 310 Args: []interface{}{ 311 "led-service", "led-service/123", leaseDuration, 312 }, 313 }}) 314 } 315 316 func (s *TrackerSuite) TestWaitLeaderNeverBecomeLeader(c *gc.C) { 317 s.claimer.Stub.SetErrors(coreleadership.ErrClaimDenied, nil) 318 tracker := leadership.NewTracker(s.unitTag, s.claimer, trackerDuration) 319 defer assertStop(c, tracker) 320 321 // Check initial ticket fails. 322 assertWaitLeader(c, tracker, false) 323 324 // Get a new ticket and stop the tracker while it's pending. 325 ticket := tracker.WaitLeader() 326 assertStop(c, tracker) 327 328 // Check the ticket got closed without sending true. 329 assertTicket(c, ticket, false) 330 assertTicket(c, ticket, false) 331 332 // Unblock the release goroutine and stop the tracker before trying to 333 // look at its stub. 334 s.unblockRelease(c) 335 s.claimer.CheckCalls(c, []testing.StubCall{{ 336 FuncName: "ClaimLeadership", 337 Args: []interface{}{ 338 "led-service", "led-service/123", leaseDuration, 339 }, 340 }, { 341 FuncName: "BlockUntilLeadershipReleased", 342 Args: []interface{}{ 343 "led-service", 344 }, 345 }}) 346 } 347 348 func (s *TrackerSuite) TestWaitMinionAlreadyMinion(c *gc.C) { 349 s.claimer.Stub.SetErrors(coreleadership.ErrClaimDenied, nil) 350 tracker := leadership.NewTracker(s.unitTag, s.claimer, trackerDuration) 351 defer assertStop(c, tracker) 352 353 // Check initial ticket is closed immediately. 354 assertWaitLeader(c, tracker, false) 355 356 // Stop the tracker before trying to look at its stub. 357 assertStop(c, tracker) 358 s.claimer.CheckCalls(c, []testing.StubCall{{ 359 FuncName: "ClaimLeadership", 360 Args: []interface{}{ 361 "led-service", "led-service/123", leaseDuration, 362 }, 363 }, { 364 FuncName: "BlockUntilLeadershipReleased", 365 Args: []interface{}{ 366 "led-service", 367 }, 368 }}) 369 } 370 371 func (s *TrackerSuite) TestWaitMinionBecomeMinion(c *gc.C) { 372 s.claimer.Stub.SetErrors(nil, coreleadership.ErrClaimDenied, nil) 373 tracker := leadership.NewTracker(s.unitTag, s.claimer, trackerDuration) 374 defer assertStop(c, tracker) 375 376 // Check the first ticket stays open. 377 assertWaitMinion(c, tracker, false) 378 379 // Wait long enough for a single refresh, to trigger ErrClaimDenied; then 380 // check the next ticket is closed. 381 <-time.After(refreshes(1)) 382 assertWaitMinion(c, tracker, true) 383 384 // Stop the tracker before trying to look at its stub. 385 assertStop(c, tracker) 386 387 // Unblock the release goroutine, lest data races. 388 s.unblockRelease(c) 389 390 s.claimer.CheckCalls(c, []testing.StubCall{{ 391 FuncName: "ClaimLeadership", 392 Args: []interface{}{ 393 "led-service", "led-service/123", leaseDuration, 394 }, 395 }, { 396 FuncName: "ClaimLeadership", 397 Args: []interface{}{ 398 "led-service", "led-service/123", leaseDuration, 399 }, 400 }, { 401 FuncName: "BlockUntilLeadershipReleased", 402 Args: []interface{}{ 403 "led-service", 404 }, 405 }}) 406 } 407 408 func (s *TrackerSuite) TestWaitMinionNeverBecomeMinion(c *gc.C) { 409 tracker := leadership.NewTracker(s.unitTag, s.claimer, trackerDuration) 410 defer assertStop(c, tracker) 411 412 ticket := tracker.WaitMinion() 413 select { 414 case <-time.After(refreshes(2)): 415 case <-ticket.Ready(): 416 c.Fatalf("got unexpected readiness: %v", ticket.Wait()) 417 } 418 419 s.claimer.CheckCalls(c, []testing.StubCall{{ 420 FuncName: "ClaimLeadership", 421 Args: []interface{}{ 422 "led-service", "led-service/123", leaseDuration, 423 }, 424 }, { 425 FuncName: "ClaimLeadership", 426 Args: []interface{}{ 427 "led-service", "led-service/123", leaseDuration, 428 }, 429 }, { 430 FuncName: "ClaimLeadership", 431 Args: []interface{}{ 432 "led-service", "led-service/123", leaseDuration, 433 }, 434 }}) 435 } 436 437 func assertClaimLeader(c *gc.C, tracker *leadership.Tracker, expect bool) { 438 // Grab a ticket... 439 ticket := tracker.ClaimLeader() 440 441 // ...and check that it gives the expected result every time it's checked. 442 assertTicket(c, ticket, expect) 443 assertTicket(c, ticket, expect) 444 } 445 446 func assertWaitLeader(c *gc.C, tracker *leadership.Tracker, expect bool) { 447 ticket := tracker.WaitLeader() 448 if expect { 449 assertTicket(c, ticket, true) 450 assertTicket(c, ticket, true) 451 return 452 } 453 select { 454 case <-time.After(trackerDuration / 4): 455 // This wait needs to be small, compared to the resolution we run the 456 // tests at, so as not to disturb client timing too much. 457 case <-ticket.Ready(): 458 c.Fatalf("got unexpected readiness: %v", ticket.Wait()) 459 } 460 } 461 462 func assertWaitMinion(c *gc.C, tracker *leadership.Tracker, expect bool) { 463 ticket := tracker.WaitMinion() 464 if expect { 465 assertTicket(c, ticket, false) 466 assertTicket(c, ticket, false) 467 return 468 } 469 select { 470 case <-time.After(trackerDuration / 4): 471 // This wait needs to be small, compared to the resolution we run the 472 // tests at, so as not to disturb client timing too much. 473 case <-ticket.Ready(): 474 c.Fatalf("got unexpected readiness: %v", ticket.Wait()) 475 } 476 } 477 478 func assertTicket(c *gc.C, ticket coreleadership.Ticket, expect bool) { 479 // Wait for the ticket to give a value... 480 select { 481 case <-time.After(coretesting.LongWait): 482 c.Fatalf("value not sent") 483 case <-ticket.Ready(): 484 c.Assert(ticket.Wait(), gc.Equals, expect) 485 } 486 } 487 488 func assertStop(c *gc.C, w worker.Worker) { 489 c.Assert(worker.Stop(w), jc.ErrorIsNil) 490 }