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