github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/lease/manager_expire_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package lease_test 5 6 import ( 7 "time" 8 9 "github.com/juju/clock/testclock" 10 "github.com/juju/errors" 11 "github.com/juju/loggo" 12 "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 gc "gopkg.in/check.v1" 15 16 corelease "github.com/juju/juju/core/lease" 17 coretesting "github.com/juju/juju/testing" 18 "github.com/juju/juju/worker/lease" 19 ) 20 21 type ExpireSuite struct { 22 testing.IsolationSuite 23 } 24 25 var _ = gc.Suite(&ExpireSuite{}) 26 27 func (s *ExpireSuite) SetUpTest(c *gc.C) { 28 s.IsolationSuite.SetUpTest(c) 29 logger := loggo.GetLogger("juju.worker.lease") 30 logger.SetLogLevel(loggo.TRACE) 31 logger = loggo.GetLogger("lease_test") 32 logger.SetLogLevel(loggo.TRACE) 33 } 34 35 func (s *ExpireSuite) TestStartup_ExpiryInPast(c *gc.C) { 36 fix := &Fixture{ 37 leases: map[corelease.Key]corelease.Info{ 38 key("redis"): {Expiry: offset(-time.Second)}, 39 }, 40 expectCalls: []call{{ 41 method: "Refresh", 42 }, { 43 method: "ExpireLease", 44 args: []interface{}{key("redis")}, 45 callback: func(leases map[corelease.Key]corelease.Info) { 46 delete(leases, key("redis")) 47 }, 48 }}, 49 } 50 fix.RunTest(c, func(_ *lease.Manager, _ *testclock.Clock) {}) 51 } 52 53 func (s *ExpireSuite) TestStartup_ExpiryInFuture(c *gc.C) { 54 fix := &Fixture{ 55 leases: map[corelease.Key]corelease.Info{ 56 key("redis"): {Expiry: offset(time.Second)}, 57 }, 58 } 59 fix.RunTest(c, func(_ *lease.Manager, clock *testclock.Clock) { 60 waitAdvance(c, clock, almostSeconds(1), 1) 61 }) 62 } 63 64 func (s *ExpireSuite) TestStartup_ExpiryInFuture_TimePasses(c *gc.C) { 65 fix := &Fixture{ 66 leases: map[corelease.Key]corelease.Info{ 67 key("redis"): {Expiry: offset(time.Second)}, 68 }, 69 expectCalls: []call{{ 70 method: "Refresh", 71 }, { 72 method: "ExpireLease", 73 args: []interface{}{key("redis")}, 74 callback: func(leases map[corelease.Key]corelease.Info) { 75 delete(leases, key("redis")) 76 }, 77 }}, 78 } 79 fix.RunTest(c, func(_ *lease.Manager, clock *testclock.Clock) { 80 waitAdvance(c, clock, time.Second, 1) 81 }) 82 } 83 84 func (s *ExpireSuite) TestStartup_NoExpiry_NotLongEnough(c *gc.C) { 85 fix := &Fixture{} 86 fix.RunTest(c, func(_ *lease.Manager, clock *testclock.Clock) { 87 waitAdvance(c, clock, almostSeconds(3600), 1) 88 }) 89 } 90 91 func (s *ExpireSuite) TestStartup_NoExpiry_LongEnough(c *gc.C) { 92 fix := &Fixture{ 93 leases: map[corelease.Key]corelease.Info{ 94 key("goose"): {Expiry: offset(3 * time.Hour)}, 95 }, 96 expectCalls: []call{{ 97 method: "Refresh", 98 callback: func(leases map[corelease.Key]corelease.Info) { 99 leases[key("redis")] = corelease.Info{ 100 Expiry: offset(time.Minute), 101 } 102 }, 103 }, { 104 method: "ExpireLease", 105 args: []interface{}{key("redis")}, 106 callback: func(leases map[corelease.Key]corelease.Info) { 107 delete(leases, key("redis")) 108 }, 109 }}, 110 } 111 fix.RunTest(c, func(_ *lease.Manager, clock *testclock.Clock) { 112 waitAdvance(c, clock, time.Hour, 1) 113 }) 114 } 115 116 func (s *ExpireSuite) TestExpire_ErrInvalid_Expired(c *gc.C) { 117 fix := &Fixture{ 118 leases: map[corelease.Key]corelease.Info{ 119 key("redis"): {Expiry: offset(time.Second)}, 120 }, 121 expectCalls: []call{{ 122 method: "Refresh", 123 }, { 124 method: "ExpireLease", 125 args: []interface{}{key("redis")}, 126 err: corelease.ErrInvalid, 127 callback: func(leases map[corelease.Key]corelease.Info) { 128 delete(leases, key("redis")) 129 }, 130 }}, 131 } 132 fix.RunTest(c, func(_ *lease.Manager, clock *testclock.Clock) { 133 waitAdvance(c, clock, time.Second, 1) 134 }) 135 } 136 137 func (s *ExpireSuite) TestAutoexpire(c *gc.C) { 138 // Handles the claim, doesn't try to do anything about the expired 139 // lease which will go away automatically. 140 fix := &Fixture{ 141 autoexpire: true, 142 leases: map[corelease.Key]corelease.Info{ 143 key("redis"): {Expiry: offset(time.Second)}, 144 }, 145 expectCalls: []call{{ 146 method: "ClaimLease", 147 args: []interface{}{ 148 corelease.Key{ 149 Namespace: "namespace", 150 ModelUUID: "modelUUID", 151 Lease: "postgresql", 152 }, 153 corelease.Request{"postgresql/0", time.Minute}, 154 }, 155 }}, 156 } 157 fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) { 158 waitAdvance(c, clock, time.Second, 1) 159 err := getClaimer(c, manager).Claim("postgresql", "postgresql/0", time.Minute) 160 c.Check(err, jc.ErrorIsNil) 161 }) 162 } 163 164 func (s *ExpireSuite) TestExpire_ErrInvalid_Updated(c *gc.C) { 165 fix := &Fixture{ 166 leases: map[corelease.Key]corelease.Info{ 167 key("redis"): {Expiry: offset(time.Second)}, 168 }, 169 expectCalls: []call{{ 170 method: "Refresh", 171 }, { 172 method: "ExpireLease", 173 args: []interface{}{key("redis")}, 174 err: corelease.ErrInvalid, 175 callback: func(leases map[corelease.Key]corelease.Info) { 176 leases[key("redis")] = corelease.Info{Expiry: offset(time.Minute)} 177 }, 178 }}, 179 } 180 fix.RunTest(c, func(_ *lease.Manager, clock *testclock.Clock) { 181 waitAdvance(c, clock, time.Second, 1) 182 }) 183 } 184 185 func (s *ExpireSuite) TestExpire_OtherError(c *gc.C) { 186 fix := &Fixture{ 187 leases: map[corelease.Key]corelease.Info{ 188 key("redis"): {Expiry: offset(time.Second)}, 189 }, 190 expectCalls: []call{{ 191 method: "Refresh", 192 }, { 193 method: "ExpireLease", 194 args: []interface{}{key("redis")}, 195 err: errors.New("snarfblat hobalob"), 196 }}, 197 expectDirty: true, 198 } 199 fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) { 200 waitAdvance(c, clock, time.Second, 1) 201 err := manager.Wait() 202 c.Check(err, gc.ErrorMatches, "snarfblat hobalob") 203 }) 204 } 205 206 func (s *ExpireSuite) TestClaim_ExpiryInFuture(c *gc.C) { 207 const newLeaseSecs = 63 208 fix := &Fixture{ 209 expectCalls: []call{{ 210 method: "ClaimLease", 211 args: []interface{}{ 212 key("redis"), 213 corelease.Request{"redis/0", time.Minute}, 214 }, 215 callback: func(leases map[corelease.Key]corelease.Info) { 216 leases[key("redis")] = corelease.Info{ 217 Holder: "redis/0", 218 Expiry: offset(newLeaseSecs * time.Second), 219 } 220 }, 221 }, { 222 // We should call Refresh at 1 min because that was the requested 223 // time, but we shouldn't expire the lease. 224 method: "Refresh", 225 }}, 226 } 227 fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) { 228 // Ask for a minute, actually get 63s. Don't expire early. 229 err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute) 230 c.Assert(err, jc.ErrorIsNil) 231 // One waiters: 232 // - the timer in the main loop 233 waitAdvance(c, clock, almostSeconds(newLeaseSecs), 1) 234 }) 235 } 236 237 func (s *ExpireSuite) TestClaim_ExpiryInFuture_TimePasses(c *gc.C) { 238 const newLeaseSecs = 63 239 firstRelease := make(chan struct{}) 240 fix := &Fixture{ 241 expectCalls: []call{{ 242 method: "ClaimLease", 243 args: []interface{}{ 244 key("redis"), 245 corelease.Request{"redis/0", time.Minute}, 246 }, 247 callback: func(leases map[corelease.Key]corelease.Info) { 248 leases[key("redis")] = corelease.Info{ 249 Holder: "redis/0", 250 Expiry: offset(newLeaseSecs * time.Second), 251 } 252 }, 253 }, { 254 method: "Refresh", 255 callback: func(leases map[corelease.Key]corelease.Info) { 256 close(firstRelease) 257 }, 258 }, { 259 method: "Refresh", 260 }, { 261 method: "ExpireLease", 262 args: []interface{}{key("redis")}, 263 callback: func(leases map[corelease.Key]corelease.Info) { 264 delete(leases, key("redis")) 265 }, 266 }}, 267 } 268 fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) { 269 // Ask for a minute, actually get 63s. Expire on time. 270 err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute) 271 c.Assert(err, jc.ErrorIsNil) 272 // Move forward 1 minute, and we should wake up to Refresh, but this doesn't 273 // trigger any expiration. It does cause us to notice that it will expire 274 // in 3 more seconds 275 waitAdvance(c, clock, time.Minute, 1) 276 select { 277 case <-firstRelease: 278 case <-time.After(testing.LongWait): 279 c.Errorf("waited too long") 280 } 281 // Now we move forward the remaining 3 seconds and wake up to refresh and expire 282 waitAdvance(c, clock, 3*time.Second, 1) 283 }) 284 } 285 286 func (s *ExpireSuite) TestExtend_ExpiryInFuture(c *gc.C) { 287 const newLeaseSecs = 63 288 fix := &Fixture{ 289 leases: map[corelease.Key]corelease.Info{ 290 key("redis"): { 291 Holder: "redis/0", 292 Expiry: offset(time.Second), 293 }, 294 }, 295 expectCalls: []call{{ 296 method: "ExtendLease", 297 args: []interface{}{ 298 key("redis"), 299 corelease.Request{"redis/0", time.Minute}, 300 }, 301 callback: func(leases map[corelease.Key]corelease.Info) { 302 leases[key("redis")] = corelease.Info{ 303 Holder: "redis/0", 304 Expiry: offset(newLeaseSecs * time.Second), 305 } 306 }, 307 }, { 308 // We do trigger a Refresh, but we don't expire the key. 309 method: "Refresh", 310 }}, 311 } 312 fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) { 313 // Ask for a minute, actually get 63s. Don't expire early. 314 err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute) 315 c.Assert(err, jc.ErrorIsNil) 316 // Only the main loop waiter 317 waitAdvance(c, clock, almostSeconds(newLeaseSecs), 1) 318 }) 319 } 320 321 func (s *ExpireSuite) TestExtend_ExpiryInFuture_TimePasses(c *gc.C) { 322 const newLeaseSecs = 63 323 firstRelease := make(chan struct{}) 324 fix := &Fixture{ 325 leases: map[corelease.Key]corelease.Info{ 326 key("redis"): { 327 Holder: "redis/0", 328 Expiry: offset(time.Second), 329 }, 330 }, 331 expectCalls: []call{{ 332 method: "ExtendLease", 333 args: []interface{}{ 334 key("redis"), 335 corelease.Request{"redis/0", time.Minute}, 336 }, 337 callback: func(leases map[corelease.Key]corelease.Info) { 338 leases[key("redis")] = corelease.Info{ 339 Holder: "redis/0", 340 Expiry: offset(newLeaseSecs * time.Second), 341 } 342 }, 343 }, { 344 method: "Refresh", 345 callback: func(leases map[corelease.Key]corelease.Info) { 346 close(firstRelease) 347 }, 348 }, { 349 method: "Refresh", 350 }, { 351 method: "ExpireLease", 352 args: []interface{}{key("redis")}, 353 callback: func(leases map[corelease.Key]corelease.Info) { 354 delete(leases, key("redis")) 355 }, 356 }}, 357 } 358 fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) { 359 // Ask for a minute, actually get 63s. Expire on time. 360 err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute) 361 c.Assert(err, jc.ErrorIsNil) 362 // Notice the first wakeup, but no expirations, and queued up a second wakeup 363 waitAdvance(c, clock, time.Minute, 1) 364 select { 365 case <-firstRelease: 366 case <-time.After(testing.LongWait): 367 c.Errorf("waited too long") 368 } 369 waitAdvance(c, clock, 3*time.Second, 1) 370 }) 371 } 372 373 func (s *ExpireSuite) TestExpire_Multiple(c *gc.C) { 374 fix := &Fixture{ 375 leases: map[corelease.Key]corelease.Info{ 376 key("redis"): { 377 Holder: "redis/0", 378 Expiry: offset(time.Second), 379 }, 380 key("store"): { 381 Holder: "store/3", 382 Expiry: offset(5 * time.Second), 383 }, 384 key("tokumx"): { 385 Holder: "tokumx/5", 386 Expiry: offset(10 * time.Second), // will not expire. 387 }, 388 key("ultron"): { 389 Holder: "ultron/7", 390 Expiry: offset(5 * time.Second), 391 }, 392 key("vvvvvv"): { 393 Holder: "vvvvvv/2", 394 Expiry: offset(time.Second), // would expire, but errors first. 395 }, 396 }, 397 expectCalls: []call{{ 398 method: "Refresh", 399 }, { 400 method: "ExpireLease", 401 args: []interface{}{key("redis")}, 402 callback: func(leases map[corelease.Key]corelease.Info) { 403 delete(leases, key("redis")) 404 }, 405 }, { 406 method: "ExpireLease", 407 args: []interface{}{key("store")}, 408 err: corelease.ErrInvalid, 409 callback: func(leases map[corelease.Key]corelease.Info) { 410 delete(leases, key("store")) 411 }, 412 }, { 413 method: "ExpireLease", 414 args: []interface{}{key("ultron")}, 415 err: errors.New("what is this?"), 416 }}, 417 expectDirty: true, 418 } 419 fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) { 420 waitAdvance(c, clock, 5*time.Second, 1) 421 err := manager.Wait() 422 c.Check(err, gc.ErrorMatches, "what is this\\?") 423 }) 424 } 425 426 func waitAdvance(c *gc.C, clock *testclock.Clock, amount time.Duration, waiters int) { 427 err := clock.WaitAdvance(amount, coretesting.LongWait, waiters) 428 c.Assert(err, jc.ErrorIsNil) 429 }