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