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