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  }