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