github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/lease/lease_test.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package lease
     5  
     6  import (
     7  	"testing"
     8  	"time"
     9  
    10  	gc "gopkg.in/check.v1"
    11  
    12  	coretesting "github.com/juju/juju/testing"
    13  )
    14  
    15  func Test(t *testing.T) { gc.TestingT(t) }
    16  
    17  const (
    18  	testNamespace = "leadership-stub-service"
    19  	testId        = "stub-unit/0"
    20  	testDuration  = 30 * time.Hour
    21  )
    22  
    23  var (
    24  	_ = gc.Suite(&leaseSuite{})
    25  )
    26  
    27  type stubLeasePersistor struct {
    28  	WriteTokenFn      func(string, Token) error
    29  	RemoveTokenFn     func(string) error
    30  	PersistedTokensFn func() ([]Token, error)
    31  }
    32  
    33  func (p *stubLeasePersistor) WriteToken(id string, tok Token) error {
    34  	if p.WriteTokenFn != nil {
    35  		return p.WriteTokenFn(id, tok)
    36  	}
    37  	return nil
    38  }
    39  
    40  func (p *stubLeasePersistor) RemoveToken(id string) error {
    41  	if p.RemoveTokenFn != nil {
    42  		return p.RemoveTokenFn(id)
    43  	}
    44  	return nil
    45  }
    46  
    47  func (p *stubLeasePersistor) PersistedTokens() ([]Token, error) {
    48  	if p.PersistedTokensFn != nil {
    49  		return p.PersistedTokensFn()
    50  	}
    51  	return nil, nil
    52  }
    53  
    54  type leaseSuite struct{}
    55  
    56  func (s *leaseSuite) TestSingleton(c *gc.C) {
    57  	stop := make(chan struct{})
    58  	go WorkerLoop(&stubLeasePersistor{})(stop)
    59  	defer func() { stop <- struct{}{} }()
    60  
    61  	copyA := Manager()
    62  	copyB := Manager()
    63  
    64  	c.Assert(copyA, gc.NotNil)
    65  	c.Assert(copyA, gc.Equals, copyB)
    66  }
    67  
    68  // TestTokenListIsolation ensures that the copy of the lease tokens we
    69  // get is truly a copy and thus isolated from all other code.
    70  func (s *leaseSuite) TestTokenListIsolation(c *gc.C) {
    71  	stop := make(chan struct{})
    72  	go WorkerLoop(&stubLeasePersistor{})(stop)
    73  	defer func() { stop <- struct{}{} }()
    74  
    75  	mgr := Manager()
    76  
    77  	mgr.ClaimLease(testNamespace, testId, testDuration)
    78  	toksA := mgr.CopyOfLeaseTokens()
    79  	toksB := mgr.CopyOfLeaseTokens()
    80  
    81  	// The tokens are equivalent...
    82  	c.Assert(toksA, gc.HasLen, 1)
    83  	c.Check(toksA, gc.DeepEquals, toksB)
    84  
    85  	//...but isolated.
    86  	toksA[0].Id = "I'm a bad, bad programmer. Why would I do this?"
    87  	c.Check(toksA[0], gc.Not(gc.Equals), toksB[0])
    88  
    89  	//...and the cache remains in tact.
    90  	err := mgr.ReleaseLease(testNamespace, testId)
    91  	c.Check(err, gc.IsNil)
    92  }
    93  
    94  func (s *leaseSuite) TestClaimLease(c *gc.C) {
    95  	stop := make(chan struct{})
    96  	go WorkerLoop(&stubLeasePersistor{})(stop)
    97  	defer func() { stop <- struct{}{} }()
    98  
    99  	mgr := Manager()
   100  	ownerId, err := mgr.ClaimLease(testNamespace, testId, testDuration)
   101  
   102  	c.Assert(err, gc.IsNil)
   103  	c.Assert(ownerId, gc.Equals, testId)
   104  
   105  	toks := mgr.CopyOfLeaseTokens()
   106  	c.Assert(toks, gc.HasLen, 1)
   107  	c.Assert(toks[0].Namespace, gc.Equals, testNamespace)
   108  	c.Assert(toks[0].Id, gc.Equals, testId)
   109  }
   110  
   111  func (s *leaseSuite) TestReleaseLease(c *gc.C) {
   112  	stop := make(chan struct{})
   113  	go WorkerLoop(&stubLeasePersistor{})(stop)
   114  	defer func() { stop <- struct{}{} }()
   115  
   116  	mgr := Manager()
   117  
   118  	ownerId, err := mgr.ClaimLease(testNamespace, testId, 30*time.Hour)
   119  	c.Assert(err, gc.IsNil)
   120  	c.Assert(ownerId, gc.Equals, testId)
   121  
   122  	err = mgr.ReleaseLease(testNamespace, testId)
   123  	c.Assert(err, gc.IsNil)
   124  
   125  	toks := mgr.CopyOfLeaseTokens()
   126  	c.Assert(toks, gc.HasLen, 0)
   127  }
   128  
   129  func (s *leaseSuite) TestRetrieveLease(c *gc.C) {
   130  	stop := make(chan struct{})
   131  	go WorkerLoop(&stubLeasePersistor{})(stop)
   132  	defer func() { stop <- struct{}{} }()
   133  
   134  	mgr := Manager()
   135  
   136  	ownerId, err := mgr.ClaimLease(testNamespace, testId, 30*time.Hour)
   137  	c.Assert(err, gc.IsNil)
   138  	c.Assert(ownerId, gc.Equals, testId)
   139  
   140  	tok := mgr.RetrieveLease(testNamespace)
   141  	c.Check(tok.Id, gc.Equals, testId)
   142  	c.Check(tok.Namespace, gc.Equals, testNamespace)
   143  }
   144  
   145  func (s *leaseSuite) TestRetrieveLeaseWithBadNamespaceFails(c *gc.C) {
   146  	stop := make(chan struct{})
   147  	go WorkerLoop(&stubLeasePersistor{})(stop)
   148  	defer func() { stop <- struct{}{} }()
   149  
   150  	mgr := Manager()
   151  
   152  	tok := mgr.RetrieveLease(testNamespace)
   153  	c.Assert(tok, gc.DeepEquals, Token{})
   154  }
   155  
   156  func (s *leaseSuite) TestReleaseLeaseNotification(c *gc.C) {
   157  	stop := make(chan struct{})
   158  	go WorkerLoop(&stubLeasePersistor{})(stop)
   159  	defer func() { stop <- struct{}{} }()
   160  
   161  	mgr := Manager()
   162  
   163  	// Grab a lease.
   164  	_, err := mgr.ClaimLease(testNamespace, testId, 30*time.Hour)
   165  	c.Assert(err, gc.IsNil)
   166  
   167  	// Listen for it to be released.
   168  	subscription := mgr.LeaseReleasedNotifier(testNamespace)
   169  	receivedSignal := make(chan struct{})
   170  	go func() {
   171  		<-subscription
   172  		receivedSignal <- struct{}{}
   173  	}()
   174  
   175  	// Release it
   176  	err = mgr.ReleaseLease(testNamespace, testId)
   177  	c.Assert(err, gc.IsNil)
   178  
   179  	select {
   180  	case <-receivedSignal:
   181  	case <-time.After(coretesting.LongWait):
   182  		c.Errorf("Failed to unblock after release. Waited for %s", coretesting.LongWait)
   183  	}
   184  }
   185  
   186  func (s *leaseSuite) TestLeaseExpiration(c *gc.C) {
   187  
   188  	// WARNING: This code may be load-sensitive. Unfortunately it must
   189  	// deal with ellapsed time since this is the nature of the code
   190  	// it is testing. For that reason, we try a few times to see if we
   191  	// can get a successful run.
   192  
   193  	stop := make(chan struct{})
   194  	go WorkerLoop(&stubLeasePersistor{})(stop)
   195  	defer func() { stop <- struct{}{} }()
   196  
   197  	const (
   198  		leaseDuration      = 500 * time.Millisecond
   199  		acceptableOverhead = 50 * time.Millisecond
   200  	)
   201  
   202  	if leaseDuration+acceptableOverhead > coretesting.LongWait {
   203  		panic("This test will always fail.")
   204  	}
   205  
   206  	// Listen for releases before sending the claim to avoid the
   207  	// overhead which may affect our timing measurements.
   208  	mgr := Manager()
   209  	subscription := mgr.LeaseReleasedNotifier(testNamespace)
   210  	receivedSignal := make(chan struct{})
   211  
   212  	var leaseClaimedTime time.Time
   213  	go func() {
   214  
   215  		<-subscription
   216  		leaseReleasedTime := time.Now()
   217  
   218  		// Ensure we didn't release too early or too late.
   219  		switch elapsed := leaseReleasedTime.Sub(leaseClaimedTime); {
   220  		default:
   221  			receivedSignal <- struct{}{}
   222  		case elapsed > leaseDuration+acceptableOverhead:
   223  			fallthrough
   224  		case elapsed < leaseDuration-acceptableOverhead:
   225  			c.Errorf(
   226  				"Expected the lease to be released in %s, but it was released in %s",
   227  				leaseDuration,
   228  				elapsed,
   229  			)
   230  		}
   231  	}()
   232  
   233  	// Grab a lease.
   234  	_, err := mgr.ClaimLease(testNamespace, testId, leaseDuration)
   235  	leaseClaimedTime = time.Now()
   236  	c.Assert(err, gc.IsNil)
   237  
   238  	// Wait for the all-clear, or a time-out.
   239  	select {
   240  	case <-receivedSignal:
   241  	case <-time.After(coretesting.LongWait):
   242  		c.Errorf("Failed to unblock after release. Waited for %s", coretesting.LongWait)
   243  	}
   244  }
   245  
   246  func (s *leaseSuite) TestManagerPeresistsOnClaims(c *gc.C) {
   247  
   248  	persistor := &stubLeasePersistor{}
   249  
   250  	stop := make(chan struct{})
   251  	go WorkerLoop(persistor)(stop)
   252  	defer func() { stop <- struct{}{} }()
   253  
   254  	mgr := Manager()
   255  
   256  	numWriteCalls := 0
   257  	persistor.WriteTokenFn = func(id string, tok Token) error {
   258  		numWriteCalls++
   259  
   260  		c.Assert(tok, gc.NotNil)
   261  		c.Check(tok.Namespace, gc.Equals, testNamespace)
   262  		c.Check(tok.Id, gc.Equals, testId)
   263  		c.Check(id, gc.Equals, testNamespace)
   264  
   265  		return nil
   266  	}
   267  
   268  	mgr.ClaimLease(testNamespace, testId, testDuration)
   269  
   270  	c.Check(numWriteCalls, gc.Equals, 1)
   271  }
   272  
   273  func (s *leaseSuite) TestManagerRemovesOnRelease(c *gc.C) {
   274  
   275  	persistor := &stubLeasePersistor{}
   276  
   277  	stop := make(chan struct{})
   278  	go WorkerLoop(persistor)(stop)
   279  	defer func() { stop <- struct{}{} }()
   280  
   281  	mgr := Manager()
   282  
   283  	// Grab a lease.
   284  	_, err := mgr.ClaimLease(testNamespace, testId, testDuration)
   285  	c.Assert(err, gc.IsNil)
   286  
   287  	numRemoveCalls := 0
   288  	persistor.RemoveTokenFn = func(id string) error {
   289  		numRemoveCalls++
   290  		c.Check(id, gc.Equals, testNamespace)
   291  		return nil
   292  	}
   293  
   294  	// Release the lease, and the peresitor should be called.
   295  	mgr.ReleaseLease(testNamespace, testId)
   296  
   297  	c.Check(numRemoveCalls, gc.Equals, 1)
   298  }
   299  
   300  func (s *leaseSuite) TestManagerDepersistsAllTokensOnStart(c *gc.C) {
   301  
   302  	persistor := &stubLeasePersistor{}
   303  
   304  	numCalls := 0
   305  	testToks := []Token{
   306  		{testNamespace, testId, time.Now().Add(testDuration)},
   307  		{testNamespace + "2", "a" + testId, time.Now().Add(testDuration)},
   308  	}
   309  	persistor.PersistedTokensFn = func() ([]Token, error) {
   310  
   311  		numCalls++
   312  		return testToks, nil
   313  	}
   314  
   315  	stop := make(chan struct{})
   316  	go WorkerLoop(persistor)(stop)
   317  	defer func() { stop <- struct{}{} }()
   318  
   319  	mgr := Manager()
   320  
   321  	// NOTE: This call will naturally block until the worker loop is
   322  	// sucessfully pumping. Place all checks below here.
   323  	heldToks := mgr.CopyOfLeaseTokens()
   324  
   325  	c.Assert(numCalls, gc.Equals, 1)
   326  
   327  	for _, heldTok := range heldToks {
   328  		found := false
   329  		for _, testTok := range testToks {
   330  			found, _ = gc.DeepEquals.Check([]interface{}{testTok, heldTok}, []string{})
   331  			if found {
   332  				break
   333  			}
   334  		}
   335  		if !found {
   336  			c.Log("The manager is not managing the expected token list.\nNOTE: Test is coded so that order does not matter.")
   337  			c.Assert(heldToks, gc.DeepEquals, testToks)
   338  		}
   339  	}
   340  }