github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/lease/lease_test.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package lease 5 6 import ( 7 "testing" 8 "time" 9 10 gc "gopkg.in/check.v1" 11 12 coretesting "github.com/juju/juju/testing" 13 ) 14 15 func Test(t *testing.T) { gc.TestingT(t) } 16 17 const ( 18 testNamespace = "leadership-stub-service" 19 testId = "stub-unit/0" 20 testDuration = 30 * time.Hour 21 ) 22 23 var ( 24 _ = gc.Suite(&leaseSuite{}) 25 ) 26 27 type stubLeasePersistor struct { 28 WriteTokenFn func(string, Token) error 29 RemoveTokenFn func(string) error 30 PersistedTokensFn func() ([]Token, error) 31 } 32 33 func (p *stubLeasePersistor) WriteToken(id string, tok Token) error { 34 if p.WriteTokenFn != nil { 35 return p.WriteTokenFn(id, tok) 36 } 37 return nil 38 } 39 40 func (p *stubLeasePersistor) RemoveToken(id string) error { 41 if p.RemoveTokenFn != nil { 42 return p.RemoveTokenFn(id) 43 } 44 return nil 45 } 46 47 func (p *stubLeasePersistor) PersistedTokens() ([]Token, error) { 48 if p.PersistedTokensFn != nil { 49 return p.PersistedTokensFn() 50 } 51 return nil, nil 52 } 53 54 type leaseSuite struct{} 55 56 func (s *leaseSuite) TestSingleton(c *gc.C) { 57 stop := make(chan struct{}) 58 go WorkerLoop(&stubLeasePersistor{})(stop) 59 defer func() { stop <- struct{}{} }() 60 61 copyA := Manager() 62 copyB := Manager() 63 64 c.Assert(copyA, gc.NotNil) 65 c.Assert(copyA, gc.Equals, copyB) 66 } 67 68 // TestTokenListIsolation ensures that the copy of the lease tokens we 69 // get is truly a copy and thus isolated from all other code. 70 func (s *leaseSuite) TestTokenListIsolation(c *gc.C) { 71 stop := make(chan struct{}) 72 go WorkerLoop(&stubLeasePersistor{})(stop) 73 defer func() { stop <- struct{}{} }() 74 75 mgr := Manager() 76 77 mgr.ClaimLease(testNamespace, testId, testDuration) 78 toksA := mgr.CopyOfLeaseTokens() 79 toksB := mgr.CopyOfLeaseTokens() 80 81 // The tokens are equivalent... 82 c.Assert(toksA, gc.HasLen, 1) 83 c.Check(toksA, gc.DeepEquals, toksB) 84 85 //...but isolated. 86 toksA[0].Id = "I'm a bad, bad programmer. Why would I do this?" 87 c.Check(toksA[0], gc.Not(gc.Equals), toksB[0]) 88 89 //...and the cache remains in tact. 90 err := mgr.ReleaseLease(testNamespace, testId) 91 c.Check(err, gc.IsNil) 92 } 93 94 func (s *leaseSuite) TestClaimLease(c *gc.C) { 95 stop := make(chan struct{}) 96 go WorkerLoop(&stubLeasePersistor{})(stop) 97 defer func() { stop <- struct{}{} }() 98 99 mgr := Manager() 100 ownerId, err := mgr.ClaimLease(testNamespace, testId, testDuration) 101 102 c.Assert(err, gc.IsNil) 103 c.Assert(ownerId, gc.Equals, testId) 104 105 toks := mgr.CopyOfLeaseTokens() 106 c.Assert(toks, gc.HasLen, 1) 107 c.Assert(toks[0].Namespace, gc.Equals, testNamespace) 108 c.Assert(toks[0].Id, gc.Equals, testId) 109 } 110 111 func (s *leaseSuite) TestReleaseLease(c *gc.C) { 112 stop := make(chan struct{}) 113 go WorkerLoop(&stubLeasePersistor{})(stop) 114 defer func() { stop <- struct{}{} }() 115 116 mgr := Manager() 117 118 ownerId, err := mgr.ClaimLease(testNamespace, testId, 30*time.Hour) 119 c.Assert(err, gc.IsNil) 120 c.Assert(ownerId, gc.Equals, testId) 121 122 err = mgr.ReleaseLease(testNamespace, testId) 123 c.Assert(err, gc.IsNil) 124 125 toks := mgr.CopyOfLeaseTokens() 126 c.Assert(toks, gc.HasLen, 0) 127 } 128 129 func (s *leaseSuite) TestRetrieveLease(c *gc.C) { 130 stop := make(chan struct{}) 131 go WorkerLoop(&stubLeasePersistor{})(stop) 132 defer func() { stop <- struct{}{} }() 133 134 mgr := Manager() 135 136 ownerId, err := mgr.ClaimLease(testNamespace, testId, 30*time.Hour) 137 c.Assert(err, gc.IsNil) 138 c.Assert(ownerId, gc.Equals, testId) 139 140 tok := mgr.RetrieveLease(testNamespace) 141 c.Check(tok.Id, gc.Equals, testId) 142 c.Check(tok.Namespace, gc.Equals, testNamespace) 143 } 144 145 func (s *leaseSuite) TestRetrieveLeaseWithBadNamespaceFails(c *gc.C) { 146 stop := make(chan struct{}) 147 go WorkerLoop(&stubLeasePersistor{})(stop) 148 defer func() { stop <- struct{}{} }() 149 150 mgr := Manager() 151 152 tok := mgr.RetrieveLease(testNamespace) 153 c.Assert(tok, gc.DeepEquals, Token{}) 154 } 155 156 func (s *leaseSuite) TestReleaseLeaseNotification(c *gc.C) { 157 stop := make(chan struct{}) 158 go WorkerLoop(&stubLeasePersistor{})(stop) 159 defer func() { stop <- struct{}{} }() 160 161 mgr := Manager() 162 163 // Grab a lease. 164 _, err := mgr.ClaimLease(testNamespace, testId, 30*time.Hour) 165 c.Assert(err, gc.IsNil) 166 167 // Listen for it to be released. 168 subscription := mgr.LeaseReleasedNotifier(testNamespace) 169 receivedSignal := make(chan struct{}) 170 go func() { 171 <-subscription 172 receivedSignal <- struct{}{} 173 }() 174 175 // Release it 176 err = mgr.ReleaseLease(testNamespace, testId) 177 c.Assert(err, gc.IsNil) 178 179 select { 180 case <-receivedSignal: 181 case <-time.After(coretesting.LongWait): 182 c.Errorf("Failed to unblock after release. Waited for %s", coretesting.LongWait) 183 } 184 } 185 186 func (s *leaseSuite) TestLeaseExpiration(c *gc.C) { 187 188 // WARNING: This code may be load-sensitive. Unfortunately it must 189 // deal with ellapsed time since this is the nature of the code 190 // it is testing. For that reason, we try a few times to see if we 191 // can get a successful run. 192 193 stop := make(chan struct{}) 194 go WorkerLoop(&stubLeasePersistor{})(stop) 195 defer func() { stop <- struct{}{} }() 196 197 const ( 198 leaseDuration = 500 * time.Millisecond 199 acceptableOverhead = 50 * time.Millisecond 200 ) 201 202 if leaseDuration+acceptableOverhead > coretesting.LongWait { 203 panic("This test will always fail.") 204 } 205 206 // Listen for releases before sending the claim to avoid the 207 // overhead which may affect our timing measurements. 208 mgr := Manager() 209 subscription := mgr.LeaseReleasedNotifier(testNamespace) 210 receivedSignal := make(chan struct{}) 211 212 var leaseClaimedTime time.Time 213 go func() { 214 215 <-subscription 216 leaseReleasedTime := time.Now() 217 218 // Ensure we didn't release too early or too late. 219 switch elapsed := leaseReleasedTime.Sub(leaseClaimedTime); { 220 default: 221 receivedSignal <- struct{}{} 222 case elapsed > leaseDuration+acceptableOverhead: 223 fallthrough 224 case elapsed < leaseDuration-acceptableOverhead: 225 c.Errorf( 226 "Expected the lease to be released in %s, but it was released in %s", 227 leaseDuration, 228 elapsed, 229 ) 230 } 231 }() 232 233 // Grab a lease. 234 _, err := mgr.ClaimLease(testNamespace, testId, leaseDuration) 235 leaseClaimedTime = time.Now() 236 c.Assert(err, gc.IsNil) 237 238 // Wait for the all-clear, or a time-out. 239 select { 240 case <-receivedSignal: 241 case <-time.After(coretesting.LongWait): 242 c.Errorf("Failed to unblock after release. Waited for %s", coretesting.LongWait) 243 } 244 } 245 246 func (s *leaseSuite) TestManagerPeresistsOnClaims(c *gc.C) { 247 248 persistor := &stubLeasePersistor{} 249 250 stop := make(chan struct{}) 251 go WorkerLoop(persistor)(stop) 252 defer func() { stop <- struct{}{} }() 253 254 mgr := Manager() 255 256 numWriteCalls := 0 257 persistor.WriteTokenFn = func(id string, tok Token) error { 258 numWriteCalls++ 259 260 c.Assert(tok, gc.NotNil) 261 c.Check(tok.Namespace, gc.Equals, testNamespace) 262 c.Check(tok.Id, gc.Equals, testId) 263 c.Check(id, gc.Equals, testNamespace) 264 265 return nil 266 } 267 268 mgr.ClaimLease(testNamespace, testId, testDuration) 269 270 c.Check(numWriteCalls, gc.Equals, 1) 271 } 272 273 func (s *leaseSuite) TestManagerRemovesOnRelease(c *gc.C) { 274 275 persistor := &stubLeasePersistor{} 276 277 stop := make(chan struct{}) 278 go WorkerLoop(persistor)(stop) 279 defer func() { stop <- struct{}{} }() 280 281 mgr := Manager() 282 283 // Grab a lease. 284 _, err := mgr.ClaimLease(testNamespace, testId, testDuration) 285 c.Assert(err, gc.IsNil) 286 287 numRemoveCalls := 0 288 persistor.RemoveTokenFn = func(id string) error { 289 numRemoveCalls++ 290 c.Check(id, gc.Equals, testNamespace) 291 return nil 292 } 293 294 // Release the lease, and the peresitor should be called. 295 mgr.ReleaseLease(testNamespace, testId) 296 297 c.Check(numRemoveCalls, gc.Equals, 1) 298 } 299 300 func (s *leaseSuite) TestManagerDepersistsAllTokensOnStart(c *gc.C) { 301 302 persistor := &stubLeasePersistor{} 303 304 numCalls := 0 305 testToks := []Token{ 306 {testNamespace, testId, time.Now().Add(testDuration)}, 307 {testNamespace + "2", "a" + testId, time.Now().Add(testDuration)}, 308 } 309 persistor.PersistedTokensFn = func() ([]Token, error) { 310 311 numCalls++ 312 return testToks, nil 313 } 314 315 stop := make(chan struct{}) 316 go WorkerLoop(persistor)(stop) 317 defer func() { stop <- struct{}{} }() 318 319 mgr := Manager() 320 321 // NOTE: This call will naturally block until the worker loop is 322 // sucessfully pumping. Place all checks below here. 323 heldToks := mgr.CopyOfLeaseTokens() 324 325 c.Assert(numCalls, gc.Equals, 1) 326 327 for _, heldTok := range heldToks { 328 found := false 329 for _, testTok := range testToks { 330 found, _ = gc.DeepEquals.Check([]interface{}{testTok, heldTok}, []string{}) 331 if found { 332 break 333 } 334 } 335 if !found { 336 c.Log("The manager is not managing the expected token list.\nNOTE: Test is coded so that order does not matter.") 337 c.Assert(heldToks, gc.DeepEquals, testToks) 338 } 339 } 340 }