github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/worker/fortress/fortress_test.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package fortress_test 5 6 import ( 7 "sync" 8 "time" 9 10 "github.com/juju/testing" 11 jc "github.com/juju/testing/checkers" 12 gc "gopkg.in/check.v1" 13 14 coretesting "github.com/juju/juju/testing" 15 "github.com/juju/juju/worker" 16 "github.com/juju/juju/worker/fortress" 17 ) 18 19 type FortressSuite struct { 20 testing.IsolationSuite 21 } 22 23 var _ = gc.Suite(&FortressSuite{}) 24 25 func (s *FortressSuite) TestOutputBadSource(c *gc.C) { 26 fix := newFixture(c) 27 defer fix.TearDown(c) 28 29 var dummy struct{ worker.Worker } 30 var out fortress.Guard 31 err := fix.manifold.Output(dummy, &out) 32 c.Check(err, gc.ErrorMatches, "in should be \\*fortress\\.fortress; is .*") 33 c.Check(out, gc.IsNil) 34 } 35 36 func (s *FortressSuite) TestOutputBadTarget(c *gc.C) { 37 fix := newFixture(c) 38 defer fix.TearDown(c) 39 40 var out interface{} 41 err := fix.manifold.Output(fix.worker, &out) 42 c.Check(err.Error(), gc.Equals, "out should be *fortress.Guest or *fortress.Guard; is *interface {}") 43 c.Check(out, gc.IsNil) 44 } 45 46 func (s *FortressSuite) TestStoppedUnlock(c *gc.C) { 47 fix := newFixture(c) 48 fix.TearDown(c) 49 50 err := fix.Guard(c).Unlock() 51 c.Check(err, gc.ErrorMatches, "fortress worker shutting down") 52 } 53 54 func (s *FortressSuite) TestStoppedLockdown(c *gc.C) { 55 fix := newFixture(c) 56 fix.TearDown(c) 57 58 err := fix.Guard(c).Lockdown(nil) 59 c.Check(err, gc.ErrorMatches, "fortress worker shutting down") 60 } 61 62 func (s *FortressSuite) TestStoppedVisit(c *gc.C) { 63 fix := newFixture(c) 64 fix.TearDown(c) 65 66 err := fix.Guest(c).Visit(nil, nil) 67 c.Check(err, gc.ErrorMatches, "fortress worker shutting down") 68 } 69 70 func (s *FortressSuite) TestStartsLocked(c *gc.C) { 71 fix := newFixture(c) 72 defer fix.TearDown(c) 73 74 AssertLocked(c, fix.Guest(c)) 75 } 76 77 func (s *FortressSuite) TestInitialLockdown(c *gc.C) { 78 fix := newFixture(c) 79 defer fix.TearDown(c) 80 81 err := fix.Guard(c).Lockdown(nil) 82 c.Check(err, jc.ErrorIsNil) 83 AssertLocked(c, fix.Guest(c)) 84 } 85 86 func (s *FortressSuite) TestInitialUnlock(c *gc.C) { 87 fix := newFixture(c) 88 defer fix.TearDown(c) 89 90 err := fix.Guard(c).Unlock() 91 c.Check(err, jc.ErrorIsNil) 92 AssertUnlocked(c, fix.Guest(c)) 93 } 94 95 func (s *FortressSuite) TestDoubleUnlock(c *gc.C) { 96 fix := newFixture(c) 97 defer fix.TearDown(c) 98 99 guard := fix.Guard(c) 100 err := guard.Unlock() 101 c.Check(err, jc.ErrorIsNil) 102 103 err = guard.Unlock() 104 c.Check(err, jc.ErrorIsNil) 105 AssertUnlocked(c, fix.Guest(c)) 106 } 107 108 func (s *FortressSuite) TestDoubleLockdown(c *gc.C) { 109 fix := newFixture(c) 110 defer fix.TearDown(c) 111 112 guard := fix.Guard(c) 113 err := guard.Unlock() 114 c.Check(err, jc.ErrorIsNil) 115 err = guard.Lockdown(nil) 116 c.Check(err, jc.ErrorIsNil) 117 118 err = guard.Lockdown(nil) 119 c.Check(err, jc.ErrorIsNil) 120 AssertLocked(c, fix.Guest(c)) 121 } 122 123 func (s *FortressSuite) TestWorkersIndependent(c *gc.C) { 124 fix := newFixture(c) 125 defer fix.TearDown(c) 126 127 // Create a separate worker and associated guard from the same manifold. 128 worker2, err := fix.manifold.Start(nil) 129 c.Assert(err, jc.ErrorIsNil) 130 defer CheckStop(c, worker2) 131 var guard2 fortress.Guard 132 err = fix.manifold.Output(worker2, &guard2) 133 c.Assert(err, jc.ErrorIsNil) 134 135 // Unlock the separate worker; check the original worker is unaffected. 136 err = guard2.Unlock() 137 c.Assert(err, jc.ErrorIsNil) 138 AssertLocked(c, fix.Guest(c)) 139 } 140 141 func (s *FortressSuite) TestVisitError(c *gc.C) { 142 fix := newFixture(c) 143 defer fix.TearDown(c) 144 err := fix.Guard(c).Unlock() 145 c.Check(err, jc.ErrorIsNil) 146 147 err = fix.Guest(c).Visit(badVisit, nil) 148 c.Check(err, gc.ErrorMatches, "bad!") 149 } 150 151 func (s *FortressSuite) TestVisitSuccess(c *gc.C) { 152 fix := newFixture(c) 153 defer fix.TearDown(c) 154 err := fix.Guard(c).Unlock() 155 c.Check(err, jc.ErrorIsNil) 156 157 err = fix.Guest(c).Visit(func() error { return nil }, nil) 158 c.Check(err, jc.ErrorIsNil) 159 } 160 161 func (s *FortressSuite) TestConcurrentVisit(c *gc.C) { 162 fix := newFixture(c) 163 defer fix.TearDown(c) 164 err := fix.Guard(c).Unlock() 165 c.Check(err, jc.ErrorIsNil) 166 guest := fix.Guest(c) 167 168 // Start a bunch of concurrent, blocking, Visits. 169 const count = 10 170 var started sync.WaitGroup 171 finishes := make(chan int, count) 172 unblocked := make(chan struct{}) 173 for i := 0; i < count; i++ { 174 started.Add(1) 175 go func(i int) { 176 visit := func() error { 177 started.Done() 178 <-unblocked 179 return nil 180 } 181 err := guest.Visit(visit, nil) 182 c.Check(err, jc.ErrorIsNil) 183 finishes <- i 184 185 }(i) 186 } 187 started.Wait() 188 189 // Just for fun, make sure a separate Visit still works as expected. 190 AssertUnlocked(c, guest) 191 192 // Unblock them all, and wait for them all to complete. 193 close(unblocked) 194 timeout := time.After(coretesting.LongWait) 195 seen := make(map[int]bool) 196 for i := 0; i < count; i++ { 197 select { 198 case finished := <-finishes: 199 c.Logf("visit %d finished", finished) 200 seen[finished] = true 201 case <-timeout: 202 c.Errorf("timed out waiting for %dth result", i) 203 } 204 } 205 c.Check(seen, gc.HasLen, count) 206 } 207 208 func (s *FortressSuite) TestUnlockUnblocksVisit(c *gc.C) { 209 fix := newFixture(c) 210 defer fix.TearDown(c) 211 212 // Start a Visit on a locked fortress, and check it's blocked. 213 visited := make(chan error, 1) 214 go func() { 215 visited <- fix.Guest(c).Visit(badVisit, nil) 216 }() 217 select { 218 case err := <-visited: 219 c.Fatalf("unexpected Visit result: %v", err) 220 case <-time.After(coretesting.ShortWait): 221 } 222 223 // Unlock the fortress, and check the Visit is unblocked. 224 err := fix.Guard(c).Unlock() 225 c.Assert(err, jc.ErrorIsNil) 226 select { 227 case err := <-visited: 228 c.Check(err, gc.ErrorMatches, "bad!") 229 case <-time.After(coretesting.LongWait): 230 c.Fatalf("timed out") 231 } 232 } 233 234 func (s *FortressSuite) TestVisitUnblocksLockdown(c *gc.C) { 235 fix := newFixture(c) 236 defer fix.TearDown(c) 237 238 // Start a long Visit to an unlocked fortress. 239 unblockVisit := fix.startBlockingVisit(c) 240 defer close(unblockVisit) 241 242 // Start a Lockdown call, and check that nothing progresses... 243 locked := make(chan error, 1) 244 go func() { 245 locked <- fix.Guard(c).Lockdown(nil) 246 }() 247 select { 248 case err := <-locked: 249 c.Fatalf("unexpected Lockdown result: %v", err) 250 case <-time.After(coretesting.ShortWait): 251 } 252 253 // ...including new Visits. 254 AssertLocked(c, fix.Guest(c)) 255 256 // Complete the running Visit, and check that the Lockdown completes too. 257 unblockVisit <- struct{}{} 258 select { 259 case err := <-locked: 260 c.Check(err, jc.ErrorIsNil) 261 case <-time.After(coretesting.LongWait): 262 c.Fatalf("timed out") 263 } 264 } 265 266 func (s *FortressSuite) TestAbortedLockdownStillLocks(c *gc.C) { 267 fix := newFixture(c) 268 defer fix.TearDown(c) 269 270 // Start a long Visit to an unlocked fortress. 271 unblockVisit := fix.startBlockingVisit(c) 272 defer close(unblockVisit) 273 274 // Start a Lockdown call, and check that nothing progresses... 275 locked := make(chan error, 1) 276 abort := make(chan struct{}) 277 go func() { 278 locked <- fix.Guard(c).Lockdown(abort) 279 }() 280 select { 281 case err := <-locked: 282 c.Fatalf("unexpected Lockdown result: %v", err) 283 case <-time.After(coretesting.ShortWait): 284 } 285 286 // ...then abort the lockdown. 287 close(abort) 288 select { 289 case err := <-locked: 290 c.Check(err, gc.Equals, fortress.ErrAborted) 291 case <-time.After(coretesting.LongWait): 292 c.Fatalf("timed out") 293 } 294 295 // Check the fortress is already locked, even as the old visit continues. 296 AssertLocked(c, fix.Guest(c)) 297 } 298 299 func (s *FortressSuite) TestAbortedLockdownUnlock(c *gc.C) { 300 fix := newFixture(c) 301 defer fix.TearDown(c) 302 303 // Start a long Visit to an unlocked fortress. 304 unblockVisit := fix.startBlockingVisit(c) 305 defer close(unblockVisit) 306 307 // Start and abort a Lockdown. 308 abort := make(chan struct{}) 309 close(abort) 310 guard := fix.Guard(c) 311 err := guard.Lockdown(abort) 312 c.Assert(err, gc.Equals, fortress.ErrAborted) 313 314 // Unlock the fortress again, leaving the original visit running, and 315 // check that new Visits are immediately accepted. 316 err = guard.Unlock() 317 c.Assert(err, jc.ErrorIsNil) 318 AssertUnlocked(c, fix.Guest(c)) 319 }