github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/lease/manager_claim_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 "sync" 8 "time" 9 10 "github.com/juju/clock/testclock" 11 "github.com/juju/errors" 12 "github.com/juju/testing" 13 jc "github.com/juju/testing/checkers" 14 "github.com/mattn/go-sqlite3" 15 gc "gopkg.in/check.v1" 16 17 corelease "github.com/juju/juju/core/lease" 18 "github.com/juju/juju/worker/lease" 19 ) 20 21 type ClaimSuite struct { 22 testing.IsolationSuite 23 } 24 25 var _ = gc.Suite(&ClaimSuite{}) 26 27 func (s *ClaimSuite) TestClaimLease_Success(c *gc.C) { 28 fix := &Fixture{ 29 expectCalls: []call{{ 30 method: "ClaimLease", 31 args: []interface{}{ 32 corelease.Key{ 33 Namespace: "namespace", 34 ModelUUID: "modelUUID", 35 Lease: "redis", 36 }, 37 corelease.Request{"redis/0", time.Minute}, 38 }, 39 }}, 40 } 41 fix.RunTest(c, func(manager *lease.Manager, _ *testclock.Clock) { 42 err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute) 43 c.Check(err, jc.ErrorIsNil) 44 }) 45 } 46 47 func (s *ClaimSuite) TestClaimLease_Success_SameHolder(c *gc.C) { 48 fix := &Fixture{ 49 expectCalls: []call{{ 50 method: "ClaimLease", 51 args: []interface{}{ 52 corelease.Key{ 53 Namespace: "namespace", 54 ModelUUID: "modelUUID", 55 Lease: "redis", 56 }, 57 corelease.Request{"redis/0", time.Minute}, 58 }, 59 err: corelease.ErrInvalid, 60 callback: func(leases map[corelease.Key]corelease.Info) { 61 leases[key("redis")] = corelease.Info{ 62 Holder: "redis/0", 63 Expiry: offset(time.Second), 64 } 65 }, 66 }, { 67 method: "ExtendLease", 68 args: []interface{}{ 69 corelease.Key{ 70 Namespace: "namespace", 71 ModelUUID: "modelUUID", 72 Lease: "redis", 73 }, 74 corelease.Request{"redis/0", time.Minute}, 75 }, 76 }}, 77 } 78 fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) { 79 // On the first attempt, we don't see ourselves in the leases, so we try 80 // to Claim the lease. But Primary thinks we already have the lease, so it 81 // refuses. After claiming, we wait 50ms to let the refresh happen, then 82 // we notice that we are the holder, so we Extend instead of Claim. 83 var wg sync.WaitGroup 84 wg.Add(1) 85 go func() { 86 err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute) 87 c.Check(err, jc.ErrorIsNil) 88 wg.Done() 89 }() 90 c.Check(clock.WaitAdvance(50*time.Millisecond, testing.LongWait, 2), jc.ErrorIsNil) 91 wg.Wait() 92 }) 93 } 94 95 func (s *ClaimSuite) TestClaimLeaseFailureHeldByClaimer(c *gc.C) { 96 fix := &Fixture{ 97 expectCalls: []call{{ 98 method: "ClaimLease", 99 args: []interface{}{ 100 corelease.Key{ 101 Namespace: "namespace", 102 ModelUUID: "modelUUID", 103 Lease: "redis", 104 }, 105 corelease.Request{Holder: "redis/0", Duration: time.Minute}, 106 }, 107 err: corelease.ErrInvalid, 108 callback: func(leases map[corelease.Key]corelease.Info) { 109 leases[key("redis")] = corelease.Info{ 110 Holder: "redis/1", 111 Expiry: offset(time.Second), 112 } 113 }, 114 }}, 115 } 116 fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) { 117 // When the Claim starts, it will first get a LeaseInvalid, it will then 118 // wait 50ms before trying again, since it is clear that our Leases map 119 // does not have the most up-to-date information. We then wake up again 120 // and see that our leases have expired and thus let things go. 121 var wg sync.WaitGroup 122 wg.Add(1) 123 go func() { 124 err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute) 125 c.Check(err, gc.Equals, corelease.ErrClaimDenied) 126 wg.Done() 127 }() 128 c.Check(clock.WaitAdvance(50*time.Millisecond, testing.LongWait, 2), jc.ErrorIsNil) 129 wg.Wait() 130 }) 131 } 132 133 func (s *ClaimSuite) TestClaimLeaseFailureHeldByOther(c *gc.C) { 134 fix := &Fixture{ 135 expectCalls: []call{{ 136 method: "ClaimLease", 137 args: []interface{}{ 138 corelease.Key{ 139 Namespace: "namespace", 140 ModelUUID: "modelUUID", 141 Lease: "redis", 142 }, 143 corelease.Request{Holder: "redis/0", Duration: time.Minute}, 144 }, 145 err: corelease.ErrHeld, 146 }}, 147 } 148 fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) { 149 err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute) 150 c.Check(err, gc.Equals, corelease.ErrClaimDenied) 151 }) 152 } 153 154 func (s *ClaimSuite) TestClaimLease_Failure_Error(c *gc.C) { 155 fix := &Fixture{ 156 expectCalls: []call{{ 157 method: "ClaimLease", 158 args: []interface{}{ 159 corelease.Key{ 160 Namespace: "namespace", 161 ModelUUID: "modelUUID", 162 Lease: "redis", 163 }, 164 corelease.Request{"redis/0", time.Minute}, 165 }, 166 err: errors.New("lol borken"), 167 }}, 168 expectDirty: true, 169 } 170 fix.RunTest(c, func(manager *lease.Manager, _ *testclock.Clock) { 171 err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute) 172 c.Check(err, gc.ErrorMatches, "lease manager stopped") 173 err = manager.Wait() 174 c.Check(err, gc.ErrorMatches, "lol borken") 175 }) 176 } 177 178 func (s *ClaimSuite) TestExtendLease_Success(c *gc.C) { 179 fix := &Fixture{ 180 leases: map[corelease.Key]corelease.Info{ 181 key("redis"): { 182 Holder: "redis/0", 183 Expiry: offset(time.Second), 184 }, 185 }, 186 expectCalls: []call{{ 187 method: "ExtendLease", 188 args: []interface{}{ 189 corelease.Key{ 190 Namespace: "namespace", 191 ModelUUID: "modelUUID", 192 Lease: "redis", 193 }, 194 corelease.Request{"redis/0", time.Minute}, 195 }, 196 }}, 197 } 198 fix.RunTest(c, func(manager *lease.Manager, _ *testclock.Clock) { 199 err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute) 200 c.Check(err, jc.ErrorIsNil) 201 }) 202 } 203 204 func (s *ClaimSuite) TestExtendLease_Success_Expired(c *gc.C) { 205 fix := &Fixture{ 206 leases: map[corelease.Key]corelease.Info{ 207 key("redis"): { 208 Holder: "redis/0", 209 Expiry: offset(time.Second), 210 }, 211 }, 212 expectCalls: []call{{ 213 method: "ExtendLease", 214 args: []interface{}{ 215 corelease.Key{ 216 Namespace: "namespace", 217 ModelUUID: "modelUUID", 218 Lease: "redis", 219 }, 220 corelease.Request{"redis/0", time.Minute}, 221 }, 222 err: corelease.ErrInvalid, 223 callback: func(leases map[corelease.Key]corelease.Info) { 224 delete(leases, key("redis")) 225 }, 226 }, { 227 method: "ClaimLease", 228 args: []interface{}{ 229 corelease.Key{ 230 Namespace: "namespace", 231 ModelUUID: "modelUUID", 232 Lease: "redis", 233 }, 234 corelease.Request{"redis/0", time.Minute}, 235 }, 236 }}, 237 } 238 fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) { 239 // On the first attempt, we think we are the holder, but Primary says "nope". 240 // So we wait 50ms for the Leases to get updated. At which point, we have 241 // reloaded our Leases and see that *nobody* is the holder. So then we try 242 // again and successfully Claim the lease. 243 var wg sync.WaitGroup 244 wg.Add(1) 245 go func() { 246 err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute) 247 c.Check(err, jc.ErrorIsNil) 248 wg.Done() 249 }() 250 c.Check(clock.WaitAdvance(50*time.Millisecond, testing.LongWait, 2), jc.ErrorIsNil) 251 wg.Wait() 252 }) 253 } 254 255 func (s *ClaimSuite) TestExtendLease_Failure_OtherHolder(c *gc.C) { 256 fix := &Fixture{ 257 leases: map[corelease.Key]corelease.Info{ 258 key("redis"): { 259 Holder: "redis/0", 260 Expiry: offset(time.Second), 261 }, 262 }, 263 expectCalls: []call{{ 264 method: "ExtendLease", 265 args: []interface{}{ 266 corelease.Key{ 267 Namespace: "namespace", 268 ModelUUID: "modelUUID", 269 Lease: "redis", 270 }, 271 corelease.Request{"redis/0", time.Minute}, 272 }, 273 err: corelease.ErrInvalid, 274 callback: func(leases map[corelease.Key]corelease.Info) { 275 leases[key("redis")] = corelease.Info{ 276 Holder: "redis/1", 277 Expiry: offset(time.Second), 278 } 279 }, 280 }}, 281 } 282 fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) { 283 // When the Claim starts, it will first get a LeaseInvalid, it will then 284 // wait 50ms before trying again, since it is clear that our Leases map 285 // does not have the most up-to-date information. We then wake up again 286 // and see that our leases have expired and thus let things go. 287 var wg sync.WaitGroup 288 wg.Add(1) 289 go func() { 290 err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute) 291 c.Check(err, gc.Equals, corelease.ErrClaimDenied) 292 wg.Done() 293 }() 294 c.Check(clock.WaitAdvance(50*time.Millisecond, testing.LongWait, 2), jc.ErrorIsNil) 295 wg.Wait() 296 }) 297 } 298 299 func (s *ClaimSuite) TestExtendLease_Failure_Retryable(c *gc.C) { 300 fix := &Fixture{ 301 leases: map[corelease.Key]corelease.Info{ 302 key("redis"): { 303 Holder: "redis/0", 304 Expiry: offset(time.Second), 305 }, 306 }, 307 expectCalls: []call{{ 308 method: "ExtendLease", 309 args: []interface{}{ 310 corelease.Key{ 311 Namespace: "namespace", 312 ModelUUID: "modelUUID", 313 Lease: "redis", 314 }, 315 corelease.Request{Holder: "redis/0", Duration: time.Minute}, 316 }, 317 err: sqlite3.ErrLocked, 318 callback: func(leases map[corelease.Key]corelease.Info) { 319 leases[key("redis")] = corelease.Info{ 320 Holder: "redis/1", 321 Expiry: offset(time.Second), 322 } 323 }, 324 }}, 325 } 326 fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) { 327 // When the Claim starts, it will first get a LeaseInvalid, it will then 328 // wait 50ms before trying again, since it is clear that our Leases map 329 // does not have the most up-to-date information. We then wake up again 330 // and see that our leases have expired and thus let things go. 331 var wg sync.WaitGroup 332 wg.Add(1) 333 go func() { 334 err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute) 335 c.Check(err, gc.Equals, corelease.ErrClaimDenied) 336 wg.Done() 337 }() 338 c.Check(clock.WaitAdvance(50*time.Millisecond, testing.LongWait, 2), jc.ErrorIsNil) 339 wg.Wait() 340 }) 341 } 342 343 func (s *ClaimSuite) TestExtendLease_Failure_Error(c *gc.C) { 344 fix := &Fixture{ 345 leases: map[corelease.Key]corelease.Info{ 346 key("redis"): { 347 Holder: "redis/0", 348 Expiry: offset(time.Second), 349 }, 350 }, 351 expectCalls: []call{{ 352 method: "ExtendLease", 353 args: []interface{}{ 354 key("redis"), 355 corelease.Request{"redis/0", time.Minute}, 356 }, 357 err: errors.New("boom splat"), 358 }}, 359 expectDirty: true, 360 } 361 fix.RunTest(c, func(manager *lease.Manager, _ *testclock.Clock) { 362 err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute) 363 c.Check(err, gc.ErrorMatches, "lease manager stopped") 364 err = manager.Wait() 365 c.Check(err, gc.ErrorMatches, "boom splat") 366 }) 367 } 368 369 func (s *ClaimSuite) TestOtherHolder_Failure(c *gc.C) { 370 fix := &Fixture{ 371 leases: map[corelease.Key]corelease.Info{ 372 key("redis"): { 373 Holder: "redis/1", 374 Expiry: offset(time.Second), 375 }, 376 }, 377 } 378 fix.RunTest(c, func(manager *lease.Manager, _ *testclock.Clock) { 379 err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute) 380 c.Check(err, gc.Equals, corelease.ErrClaimDenied) 381 }) 382 } 383 384 func getClaimer(c *gc.C, manager *lease.Manager) corelease.Claimer { 385 claimer, err := manager.Claimer("namespace", "modelUUID") 386 c.Assert(err, jc.ErrorIsNil) 387 return claimer 388 }