github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/lease/manager_claim_test.go (about)

     1  // Copyright 2015 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package lease_test
     5  
     6  import (
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/juju/clock/testclock"
    11  	"github.com/juju/errors"
    12  	"github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	corelease "github.com/juju/juju/core/lease"
    17  	"github.com/juju/juju/worker/lease"
    18  )
    19  
    20  type ClaimSuite struct {
    21  	testing.IsolationSuite
    22  }
    23  
    24  var _ = gc.Suite(&ClaimSuite{})
    25  
    26  func (s *ClaimSuite) TestClaimLease_Success(c *gc.C) {
    27  	fix := &Fixture{
    28  		expectCalls: []call{{
    29  			method: "ClaimLease",
    30  			args: []interface{}{
    31  				corelease.Key{
    32  					Namespace: "namespace",
    33  					ModelUUID: "modelUUID",
    34  					Lease:     "redis",
    35  				},
    36  				corelease.Request{"redis/0", time.Minute},
    37  			},
    38  		}},
    39  	}
    40  	fix.RunTest(c, func(manager *lease.Manager, _ *testclock.Clock) {
    41  		err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute)
    42  		c.Check(err, jc.ErrorIsNil)
    43  	})
    44  }
    45  
    46  func (s *ClaimSuite) TestClaimLease_Success_SameHolder(c *gc.C) {
    47  	fix := &Fixture{
    48  		expectCalls: []call{{
    49  			method: "ClaimLease",
    50  			args: []interface{}{
    51  				corelease.Key{
    52  					Namespace: "namespace",
    53  					ModelUUID: "modelUUID",
    54  					Lease:     "redis",
    55  				},
    56  				corelease.Request{"redis/0", time.Minute},
    57  			},
    58  			err: corelease.ErrInvalid,
    59  			callback: func(leases map[corelease.Key]corelease.Info) {
    60  				leases[key("redis")] = corelease.Info{
    61  					Holder: "redis/0",
    62  					Expiry: offset(time.Second),
    63  				}
    64  			},
    65  		}, {
    66  			method: "ExtendLease",
    67  			args: []interface{}{
    68  				corelease.Key{
    69  					Namespace: "namespace",
    70  					ModelUUID: "modelUUID",
    71  					Lease:     "redis",
    72  				},
    73  				corelease.Request{"redis/0", time.Minute},
    74  			},
    75  		}},
    76  	}
    77  	fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) {
    78  		// On the first attempt, we don't see ourselves in the leases, so we try
    79  		// to Claim the lease. But Primary thinks we already have the lease, so it
    80  		// refuses. After claiming, we wait 50ms to let the refresh happen, then
    81  		// we notice that we are the holder, so we Extend instead of Claim.
    82  		var wg sync.WaitGroup
    83  		wg.Add(1)
    84  		go func() {
    85  			err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute)
    86  			c.Check(err, jc.ErrorIsNil)
    87  			wg.Done()
    88  		}()
    89  		c.Check(clock.WaitAdvance(50*time.Millisecond, testing.LongWait, 2), jc.ErrorIsNil)
    90  		wg.Wait()
    91  	})
    92  }
    93  
    94  func (s *ClaimSuite) TestClaimLease_Failure_OtherHolder(c *gc.C) {
    95  	fix := &Fixture{
    96  		expectCalls: []call{{
    97  			method: "ClaimLease",
    98  			args: []interface{}{
    99  				corelease.Key{
   100  					Namespace: "namespace",
   101  					ModelUUID: "modelUUID",
   102  					Lease:     "redis",
   103  				},
   104  				corelease.Request{"redis/0", time.Minute},
   105  			},
   106  			err: corelease.ErrInvalid,
   107  			callback: func(leases map[corelease.Key]corelease.Info) {
   108  				leases[key("redis")] = corelease.Info{
   109  					Holder: "redis/1",
   110  					Expiry: offset(time.Second),
   111  				}
   112  			},
   113  		}},
   114  	}
   115  	fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) {
   116  		// When the Claim starts, it will first get a LeaseInvalid, it will then
   117  		// wait 50ms before trying again, since it is clear that our Leases map
   118  		// does not have the most up-to-date information. We then wake up again
   119  		// and see that our leases have expired and thus let things go.
   120  		var wg sync.WaitGroup
   121  		wg.Add(1)
   122  		go func() {
   123  			err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute)
   124  			c.Check(err, gc.Equals, corelease.ErrClaimDenied)
   125  			wg.Done()
   126  		}()
   127  		c.Check(clock.WaitAdvance(50*time.Millisecond, testing.LongWait, 2), jc.ErrorIsNil)
   128  		wg.Wait()
   129  	})
   130  }
   131  
   132  func (s *ClaimSuite) TestClaimLease_Failure_Error(c *gc.C) {
   133  	fix := &Fixture{
   134  		expectCalls: []call{{
   135  			method: "ClaimLease",
   136  			args: []interface{}{
   137  				corelease.Key{
   138  					Namespace: "namespace",
   139  					ModelUUID: "modelUUID",
   140  					Lease:     "redis",
   141  				},
   142  				corelease.Request{"redis/0", time.Minute},
   143  			},
   144  			err: errors.New("lol borken"),
   145  		}},
   146  		expectDirty: true,
   147  	}
   148  	fix.RunTest(c, func(manager *lease.Manager, _ *testclock.Clock) {
   149  		err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute)
   150  		c.Check(err, gc.ErrorMatches, "lease manager stopped")
   151  		err = manager.Wait()
   152  		c.Check(err, gc.ErrorMatches, "lol borken")
   153  	})
   154  }
   155  
   156  func (s *ClaimSuite) TestExtendLease_Success(c *gc.C) {
   157  	fix := &Fixture{
   158  		leases: map[corelease.Key]corelease.Info{
   159  			key("redis"): {
   160  				Holder: "redis/0",
   161  				Expiry: offset(time.Second),
   162  			},
   163  		},
   164  		expectCalls: []call{{
   165  			method: "ExtendLease",
   166  			args: []interface{}{
   167  				corelease.Key{
   168  					Namespace: "namespace",
   169  					ModelUUID: "modelUUID",
   170  					Lease:     "redis",
   171  				},
   172  				corelease.Request{"redis/0", time.Minute},
   173  			},
   174  		}},
   175  	}
   176  	fix.RunTest(c, func(manager *lease.Manager, _ *testclock.Clock) {
   177  		err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute)
   178  		c.Check(err, jc.ErrorIsNil)
   179  	})
   180  }
   181  
   182  func (s *ClaimSuite) TestExtendLease_Success_Expired(c *gc.C) {
   183  	fix := &Fixture{
   184  		leases: map[corelease.Key]corelease.Info{
   185  			key("redis"): {
   186  				Holder: "redis/0",
   187  				Expiry: offset(time.Second),
   188  			},
   189  		},
   190  		expectCalls: []call{{
   191  			method: "ExtendLease",
   192  			args: []interface{}{
   193  				corelease.Key{
   194  					Namespace: "namespace",
   195  					ModelUUID: "modelUUID",
   196  					Lease:     "redis",
   197  				},
   198  				corelease.Request{"redis/0", time.Minute},
   199  			},
   200  			err: corelease.ErrInvalid,
   201  			callback: func(leases map[corelease.Key]corelease.Info) {
   202  				delete(leases, key("redis"))
   203  			},
   204  		}, {
   205  			method: "ClaimLease",
   206  			args: []interface{}{
   207  				corelease.Key{
   208  					Namespace: "namespace",
   209  					ModelUUID: "modelUUID",
   210  					Lease:     "redis",
   211  				},
   212  				corelease.Request{"redis/0", time.Minute},
   213  			},
   214  		}},
   215  	}
   216  	fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) {
   217  		// On the first attempt, we think we are the holder, but Primary says "nope".
   218  		// So we wait 50ms for the Leases to get updated. At which point, we have
   219  		// reloaded our Leases and see that *nobody* is the holder. So then we try
   220  		// again and successfully Claim the lease.
   221  		var wg sync.WaitGroup
   222  		wg.Add(1)
   223  		go func() {
   224  			err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute)
   225  			c.Check(err, jc.ErrorIsNil)
   226  			wg.Done()
   227  		}()
   228  		c.Check(clock.WaitAdvance(50*time.Millisecond, testing.LongWait, 2), jc.ErrorIsNil)
   229  		wg.Wait()
   230  	})
   231  }
   232  
   233  func (s *ClaimSuite) TestExtendLease_Failure_OtherHolder(c *gc.C) {
   234  	fix := &Fixture{
   235  		leases: map[corelease.Key]corelease.Info{
   236  			key("redis"): {
   237  				Holder: "redis/0",
   238  				Expiry: offset(time.Second),
   239  			},
   240  		},
   241  		expectCalls: []call{{
   242  			method: "ExtendLease",
   243  			args: []interface{}{
   244  				corelease.Key{
   245  					Namespace: "namespace",
   246  					ModelUUID: "modelUUID",
   247  					Lease:     "redis",
   248  				},
   249  				corelease.Request{"redis/0", time.Minute},
   250  			},
   251  			err: corelease.ErrInvalid,
   252  			callback: func(leases map[corelease.Key]corelease.Info) {
   253  				leases[key("redis")] = corelease.Info{
   254  					Holder: "redis/1",
   255  					Expiry: offset(time.Second),
   256  				}
   257  			},
   258  		}},
   259  	}
   260  	fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) {
   261  		// When the Claim starts, it will first get a LeaseInvalid, it will then
   262  		// wait 50ms before trying again, since it is clear that our Leases map
   263  		// does not have the most up-to-date information. We then wake up again
   264  		// and see that our leases have expired and thus let things go.
   265  		var wg sync.WaitGroup
   266  		wg.Add(1)
   267  		go func() {
   268  			err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute)
   269  			c.Check(err, gc.Equals, corelease.ErrClaimDenied)
   270  			wg.Done()
   271  		}()
   272  		c.Check(clock.WaitAdvance(50*time.Millisecond, testing.LongWait, 2), jc.ErrorIsNil)
   273  		wg.Wait()
   274  	})
   275  }
   276  
   277  func (s *ClaimSuite) TestExtendLease_Failure_Error(c *gc.C) {
   278  	fix := &Fixture{
   279  		leases: map[corelease.Key]corelease.Info{
   280  			key("redis"): {
   281  				Holder: "redis/0",
   282  				Expiry: offset(time.Second),
   283  			},
   284  		},
   285  		expectCalls: []call{{
   286  			method: "ExtendLease",
   287  			args: []interface{}{
   288  				key("redis"),
   289  				corelease.Request{"redis/0", time.Minute},
   290  			},
   291  			err: errors.New("boom splat"),
   292  		}},
   293  		expectDirty: true,
   294  	}
   295  	fix.RunTest(c, func(manager *lease.Manager, _ *testclock.Clock) {
   296  		err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute)
   297  		c.Check(err, gc.ErrorMatches, "lease manager stopped")
   298  		err = manager.Wait()
   299  		c.Check(err, gc.ErrorMatches, "boom splat")
   300  	})
   301  }
   302  
   303  func (s *ClaimSuite) TestOtherHolder_Failure(c *gc.C) {
   304  	fix := &Fixture{
   305  		leases: map[corelease.Key]corelease.Info{
   306  			key("redis"): {
   307  				Holder: "redis/1",
   308  				Expiry: offset(time.Second),
   309  			},
   310  		},
   311  	}
   312  	fix.RunTest(c, func(manager *lease.Manager, _ *testclock.Clock) {
   313  		err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute)
   314  		c.Check(err, gc.Equals, corelease.ErrClaimDenied)
   315  	})
   316  }
   317  
   318  func getClaimer(c *gc.C, manager *lease.Manager) corelease.Claimer {
   319  	claimer, err := manager.Claimer("namespace", "modelUUID")
   320  	c.Assert(err, jc.ErrorIsNil)
   321  	return claimer
   322  }