github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/lease/manager_block_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/testing" 10 jc "github.com/juju/testing/checkers" 11 gc "gopkg.in/check.v1" 12 13 corelease "github.com/juju/juju/core/lease" 14 coretesting "github.com/juju/juju/testing" 15 "github.com/juju/juju/worker/lease" 16 ) 17 18 type WaitUntilExpiredSuite struct { 19 testing.IsolationSuite 20 } 21 22 var _ = gc.Suite(&WaitUntilExpiredSuite{}) 23 24 func (s *WaitUntilExpiredSuite) TestLeadershipNotHeld(c *gc.C) { 25 fix := &Fixture{} 26 fix.RunTest(c, func(manager *lease.Manager, _ *coretesting.Clock) { 27 blockTest := newBlockTest(manager, "redis") 28 err := blockTest.assertUnblocked(c) 29 c.Check(err, jc.ErrorIsNil) 30 }) 31 } 32 33 func (s *WaitUntilExpiredSuite) TestLeadershipExpires(c *gc.C) { 34 fix := &Fixture{ 35 leases: map[string]corelease.Info{ 36 "redis": corelease.Info{ 37 Holder: "redis/0", 38 Expiry: offset(time.Second), 39 }, 40 }, 41 expectCalls: []call{{ 42 method: "Refresh", 43 }, { 44 method: "ExpireLease", 45 args: []interface{}{"redis"}, 46 callback: func(leases map[string]corelease.Info) { 47 delete(leases, "redis") 48 }, 49 }}, 50 } 51 fix.RunTest(c, func(manager *lease.Manager, clock *coretesting.Clock) { 52 blockTest := newBlockTest(manager, "redis") 53 blockTest.assertBlocked(c) 54 55 // Trigger expiry. 56 clock.Advance(time.Second) 57 err := blockTest.assertUnblocked(c) 58 c.Check(err, jc.ErrorIsNil) 59 }) 60 } 61 62 func (s *WaitUntilExpiredSuite) TestLeadershipChanged(c *gc.C) { 63 fix := &Fixture{ 64 leases: map[string]corelease.Info{ 65 "redis": corelease.Info{ 66 Holder: "redis/0", 67 Expiry: offset(time.Second), 68 }, 69 }, 70 expectCalls: []call{{ 71 method: "Refresh", 72 }, { 73 method: "ExpireLease", 74 args: []interface{}{"redis"}, 75 err: corelease.ErrInvalid, 76 callback: func(leases map[string]corelease.Info) { 77 leases["redis"] = corelease.Info{ 78 Holder: "redis/99", 79 Expiry: offset(time.Minute), 80 } 81 }, 82 }}, 83 } 84 fix.RunTest(c, func(manager *lease.Manager, clock *coretesting.Clock) { 85 blockTest := newBlockTest(manager, "redis") 86 blockTest.assertBlocked(c) 87 88 // Trigger abortive expiry. 89 clock.Advance(time.Second) 90 blockTest.assertBlocked(c) 91 }) 92 } 93 94 func (s *WaitUntilExpiredSuite) TestLeadershipExpiredEarly(c *gc.C) { 95 fix := &Fixture{ 96 leases: map[string]corelease.Info{ 97 "redis": corelease.Info{ 98 Holder: "redis/0", 99 Expiry: offset(time.Second), 100 }, 101 }, 102 expectCalls: []call{{ 103 method: "Refresh", 104 callback: func(leases map[string]corelease.Info) { 105 delete(leases, "redis") 106 }, 107 }}, 108 } 109 fix.RunTest(c, func(manager *lease.Manager, clock *coretesting.Clock) { 110 blockTest := newBlockTest(manager, "redis") 111 blockTest.assertBlocked(c) 112 113 // Induce a refresh by making an unexpected check; it turns out the 114 // lease had already been expired by someone else. 115 manager.Token("redis", "redis/99").Check(nil) 116 err := blockTest.assertUnblocked(c) 117 c.Check(err, jc.ErrorIsNil) 118 }) 119 } 120 121 func (s *WaitUntilExpiredSuite) TestMultiple(c *gc.C) { 122 fix := &Fixture{ 123 leases: map[string]corelease.Info{ 124 "redis": corelease.Info{ 125 Holder: "redis/0", 126 Expiry: offset(time.Second), 127 }, 128 "store": corelease.Info{ 129 Holder: "store/0", 130 Expiry: offset(time.Second), 131 }, 132 }, 133 expectCalls: []call{{ 134 method: "Refresh", 135 }, { 136 method: "ExpireLease", 137 args: []interface{}{"redis"}, 138 err: corelease.ErrInvalid, 139 callback: func(leases map[string]corelease.Info) { 140 delete(leases, "redis") 141 leases["store"] = corelease.Info{ 142 Holder: "store/9", 143 Expiry: offset(time.Minute), 144 } 145 }, 146 }, { 147 method: "ExpireLease", 148 args: []interface{}{"store"}, 149 err: corelease.ErrInvalid, 150 }}, 151 } 152 fix.RunTest(c, func(manager *lease.Manager, clock *coretesting.Clock) { 153 redisTest1 := newBlockTest(manager, "redis") 154 redisTest1.assertBlocked(c) 155 redisTest2 := newBlockTest(manager, "redis") 156 redisTest2.assertBlocked(c) 157 storeTest1 := newBlockTest(manager, "store") 158 storeTest1.assertBlocked(c) 159 storeTest2 := newBlockTest(manager, "store") 160 storeTest2.assertBlocked(c) 161 162 // Induce attempted expiry; redis was expired already, store was 163 // refreshed and not expired. 164 clock.Advance(time.Second) 165 err := redisTest2.assertUnblocked(c) 166 c.Check(err, jc.ErrorIsNil) 167 err = redisTest1.assertUnblocked(c) 168 c.Check(err, jc.ErrorIsNil) 169 storeTest2.assertBlocked(c) 170 storeTest1.assertBlocked(c) 171 }) 172 } 173 174 func (s *WaitUntilExpiredSuite) TestKillManager(c *gc.C) { 175 fix := &Fixture{ 176 leases: map[string]corelease.Info{ 177 "redis": corelease.Info{ 178 Holder: "redis/0", 179 Expiry: offset(time.Second), 180 }, 181 }, 182 } 183 fix.RunTest(c, func(manager *lease.Manager, _ *coretesting.Clock) { 184 blockTest := newBlockTest(manager, "redis") 185 blockTest.assertBlocked(c) 186 187 manager.Kill() 188 err := blockTest.assertUnblocked(c) 189 c.Check(err, gc.ErrorMatches, "lease manager stopped") 190 }) 191 } 192 193 // blockTest wraps a goroutine running WaitUntilExpired, and fails if it's used 194 // more than a second after creation (which should be *plenty* of time). 195 type blockTest struct { 196 manager *lease.Manager 197 leaseName string 198 done chan error 199 abort <-chan time.Time 200 } 201 202 // newBlockTest starts a test goroutine blocking until the manager confirms 203 // expiry of the named lease. 204 func newBlockTest(manager *lease.Manager, leaseName string) *blockTest { 205 bt := &blockTest{ 206 manager: manager, 207 leaseName: leaseName, 208 done: make(chan error), 209 abort: time.After(time.Second), 210 } 211 go func() { 212 select { 213 case <-bt.abort: 214 case bt.done <- bt.manager.WaitUntilExpired(bt.leaseName): 215 } 216 }() 217 return bt 218 } 219 220 func (bt *blockTest) assertBlocked(c *gc.C) { 221 select { 222 case err := <-bt.done: 223 c.Fatalf("unblocked unexpectedly with %v", err) 224 default: 225 } 226 } 227 228 func (bt *blockTest) assertUnblocked(c *gc.C) error { 229 select { 230 case err := <-bt.done: 231 return err 232 case <-bt.abort: 233 c.Fatalf("timed out before unblocking") 234 } 235 panic("unreachable") 236 }