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  }