github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/lease/manager_expire_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  	"time"
     8  
     9  	"github.com/juju/clock/testclock"
    10  	"github.com/juju/errors"
    11  	"github.com/juju/loggo"
    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  	coretesting "github.com/juju/juju/testing"
    18  	"github.com/juju/juju/worker/lease"
    19  )
    20  
    21  type ExpireSuite struct {
    22  	testing.IsolationSuite
    23  }
    24  
    25  var _ = gc.Suite(&ExpireSuite{})
    26  
    27  func (s *ExpireSuite) SetUpTest(c *gc.C) {
    28  	s.IsolationSuite.SetUpTest(c)
    29  	logger := loggo.GetLogger("juju.worker.lease")
    30  	logger.SetLogLevel(loggo.TRACE)
    31  	logger = loggo.GetLogger("lease_test")
    32  	logger.SetLogLevel(loggo.TRACE)
    33  }
    34  
    35  func (s *ExpireSuite) TestStartup_ExpiryInPast(c *gc.C) {
    36  	fix := &Fixture{
    37  		leases: map[corelease.Key]corelease.Info{
    38  			key("redis"): {Expiry: offset(-time.Second)},
    39  		},
    40  		expectCalls: []call{{
    41  			method: "Refresh",
    42  		}, {
    43  			method: "ExpireLease",
    44  			args:   []interface{}{key("redis")},
    45  			callback: func(leases map[corelease.Key]corelease.Info) {
    46  				delete(leases, key("redis"))
    47  			},
    48  		}},
    49  	}
    50  	fix.RunTest(c, func(_ *lease.Manager, _ *testclock.Clock) {})
    51  }
    52  
    53  func (s *ExpireSuite) TestStartup_ExpiryInFuture(c *gc.C) {
    54  	fix := &Fixture{
    55  		leases: map[corelease.Key]corelease.Info{
    56  			key("redis"): {Expiry: offset(time.Second)},
    57  		},
    58  	}
    59  	fix.RunTest(c, func(_ *lease.Manager, clock *testclock.Clock) {
    60  		waitAdvance(c, clock, almostSeconds(1), 1)
    61  	})
    62  }
    63  
    64  func (s *ExpireSuite) TestStartup_ExpiryInFuture_TimePasses(c *gc.C) {
    65  	fix := &Fixture{
    66  		leases: map[corelease.Key]corelease.Info{
    67  			key("redis"): {Expiry: offset(time.Second)},
    68  		},
    69  		expectCalls: []call{{
    70  			method: "Refresh",
    71  		}, {
    72  			method: "ExpireLease",
    73  			args:   []interface{}{key("redis")},
    74  			callback: func(leases map[corelease.Key]corelease.Info) {
    75  				delete(leases, key("redis"))
    76  			},
    77  		}},
    78  	}
    79  	fix.RunTest(c, func(_ *lease.Manager, clock *testclock.Clock) {
    80  		waitAdvance(c, clock, time.Second, 1)
    81  	})
    82  }
    83  
    84  func (s *ExpireSuite) TestStartup_NoExpiry_NotLongEnough(c *gc.C) {
    85  	fix := &Fixture{}
    86  	fix.RunTest(c, func(_ *lease.Manager, clock *testclock.Clock) {
    87  		waitAdvance(c, clock, almostSeconds(3600), 1)
    88  	})
    89  }
    90  
    91  func (s *ExpireSuite) TestStartup_NoExpiry_LongEnough(c *gc.C) {
    92  	fix := &Fixture{
    93  		leases: map[corelease.Key]corelease.Info{
    94  			key("goose"): {Expiry: offset(3 * time.Hour)},
    95  		},
    96  		expectCalls: []call{{
    97  			method: "Refresh",
    98  			callback: func(leases map[corelease.Key]corelease.Info) {
    99  				leases[key("redis")] = corelease.Info{
   100  					Expiry: offset(time.Minute),
   101  				}
   102  			},
   103  		}, {
   104  			method: "ExpireLease",
   105  			args:   []interface{}{key("redis")},
   106  			callback: func(leases map[corelease.Key]corelease.Info) {
   107  				delete(leases, key("redis"))
   108  			},
   109  		}},
   110  	}
   111  	fix.RunTest(c, func(_ *lease.Manager, clock *testclock.Clock) {
   112  		waitAdvance(c, clock, time.Hour, 1)
   113  	})
   114  }
   115  
   116  func (s *ExpireSuite) TestExpire_ErrInvalid_Expired(c *gc.C) {
   117  	fix := &Fixture{
   118  		leases: map[corelease.Key]corelease.Info{
   119  			key("redis"): {Expiry: offset(time.Second)},
   120  		},
   121  		expectCalls: []call{{
   122  			method: "Refresh",
   123  		}, {
   124  			method: "ExpireLease",
   125  			args:   []interface{}{key("redis")},
   126  			err:    corelease.ErrInvalid,
   127  			callback: func(leases map[corelease.Key]corelease.Info) {
   128  				delete(leases, key("redis"))
   129  			},
   130  		}},
   131  	}
   132  	fix.RunTest(c, func(_ *lease.Manager, clock *testclock.Clock) {
   133  		waitAdvance(c, clock, time.Second, 1)
   134  	})
   135  }
   136  
   137  func (s *ExpireSuite) TestAutoexpire(c *gc.C) {
   138  	// Handles the claim, doesn't try to do anything about the expired
   139  	// lease which will go away automatically.
   140  	fix := &Fixture{
   141  		autoexpire: true,
   142  		leases: map[corelease.Key]corelease.Info{
   143  			key("redis"): {Expiry: offset(time.Second)},
   144  		},
   145  		expectCalls: []call{{
   146  			method: "ClaimLease",
   147  			args: []interface{}{
   148  				corelease.Key{
   149  					Namespace: "namespace",
   150  					ModelUUID: "modelUUID",
   151  					Lease:     "postgresql",
   152  				},
   153  				corelease.Request{"postgresql/0", time.Minute},
   154  			},
   155  		}},
   156  	}
   157  	fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) {
   158  		waitAdvance(c, clock, time.Second, 1)
   159  		err := getClaimer(c, manager).Claim("postgresql", "postgresql/0", time.Minute)
   160  		c.Check(err, jc.ErrorIsNil)
   161  	})
   162  }
   163  
   164  func (s *ExpireSuite) TestExpire_ErrInvalid_Updated(c *gc.C) {
   165  	fix := &Fixture{
   166  		leases: map[corelease.Key]corelease.Info{
   167  			key("redis"): {Expiry: offset(time.Second)},
   168  		},
   169  		expectCalls: []call{{
   170  			method: "Refresh",
   171  		}, {
   172  			method: "ExpireLease",
   173  			args:   []interface{}{key("redis")},
   174  			err:    corelease.ErrInvalid,
   175  			callback: func(leases map[corelease.Key]corelease.Info) {
   176  				leases[key("redis")] = corelease.Info{Expiry: offset(time.Minute)}
   177  			},
   178  		}},
   179  	}
   180  	fix.RunTest(c, func(_ *lease.Manager, clock *testclock.Clock) {
   181  		waitAdvance(c, clock, time.Second, 1)
   182  	})
   183  }
   184  
   185  func (s *ExpireSuite) TestExpire_OtherError(c *gc.C) {
   186  	fix := &Fixture{
   187  		leases: map[corelease.Key]corelease.Info{
   188  			key("redis"): {Expiry: offset(time.Second)},
   189  		},
   190  		expectCalls: []call{{
   191  			method: "Refresh",
   192  		}, {
   193  			method: "ExpireLease",
   194  			args:   []interface{}{key("redis")},
   195  			err:    errors.New("snarfblat hobalob"),
   196  		}},
   197  		expectDirty: true,
   198  	}
   199  	fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) {
   200  		waitAdvance(c, clock, time.Second, 1)
   201  		err := manager.Wait()
   202  		c.Check(err, gc.ErrorMatches, "snarfblat hobalob")
   203  	})
   204  }
   205  
   206  func (s *ExpireSuite) TestClaim_ExpiryInFuture(c *gc.C) {
   207  	const newLeaseSecs = 63
   208  	fix := &Fixture{
   209  		expectCalls: []call{{
   210  			method: "ClaimLease",
   211  			args: []interface{}{
   212  				key("redis"),
   213  				corelease.Request{"redis/0", time.Minute},
   214  			},
   215  			callback: func(leases map[corelease.Key]corelease.Info) {
   216  				leases[key("redis")] = corelease.Info{
   217  					Holder: "redis/0",
   218  					Expiry: offset(newLeaseSecs * time.Second),
   219  				}
   220  			},
   221  		}, {
   222  			// We should call Refresh at 1 min because that was the requested
   223  			// time, but we shouldn't expire the lease.
   224  			method: "Refresh",
   225  		}},
   226  	}
   227  	fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) {
   228  		// Ask for a minute, actually get 63s. Don't expire early.
   229  		err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute)
   230  		c.Assert(err, jc.ErrorIsNil)
   231  		// One waiters:
   232  		// - the timer in the main loop
   233  		waitAdvance(c, clock, almostSeconds(newLeaseSecs), 1)
   234  	})
   235  }
   236  
   237  func (s *ExpireSuite) TestClaim_ExpiryInFuture_TimePasses(c *gc.C) {
   238  	const newLeaseSecs = 63
   239  	firstRelease := make(chan struct{})
   240  	fix := &Fixture{
   241  		expectCalls: []call{{
   242  			method: "ClaimLease",
   243  			args: []interface{}{
   244  				key("redis"),
   245  				corelease.Request{"redis/0", time.Minute},
   246  			},
   247  			callback: func(leases map[corelease.Key]corelease.Info) {
   248  				leases[key("redis")] = corelease.Info{
   249  					Holder: "redis/0",
   250  					Expiry: offset(newLeaseSecs * time.Second),
   251  				}
   252  			},
   253  		}, {
   254  			method: "Refresh",
   255  			callback: func(leases map[corelease.Key]corelease.Info) {
   256  				close(firstRelease)
   257  			},
   258  		}, {
   259  			method: "Refresh",
   260  		}, {
   261  			method: "ExpireLease",
   262  			args:   []interface{}{key("redis")},
   263  			callback: func(leases map[corelease.Key]corelease.Info) {
   264  				delete(leases, key("redis"))
   265  			},
   266  		}},
   267  	}
   268  	fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) {
   269  		// Ask for a minute, actually get 63s. Expire on time.
   270  		err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute)
   271  		c.Assert(err, jc.ErrorIsNil)
   272  		// Move forward 1 minute, and we should wake up to Refresh, but this doesn't
   273  		// trigger any expiration. It does cause us to notice that it will expire
   274  		// in 3 more seconds
   275  		waitAdvance(c, clock, time.Minute, 1)
   276  		select {
   277  		case <-firstRelease:
   278  		case <-time.After(testing.LongWait):
   279  			c.Errorf("waited too long")
   280  		}
   281  		// Now we move forward the remaining 3 seconds and wake up to refresh and expire
   282  		waitAdvance(c, clock, 3*time.Second, 1)
   283  	})
   284  }
   285  
   286  func (s *ExpireSuite) TestExtend_ExpiryInFuture(c *gc.C) {
   287  	const newLeaseSecs = 63
   288  	fix := &Fixture{
   289  		leases: map[corelease.Key]corelease.Info{
   290  			key("redis"): {
   291  				Holder: "redis/0",
   292  				Expiry: offset(time.Second),
   293  			},
   294  		},
   295  		expectCalls: []call{{
   296  			method: "ExtendLease",
   297  			args: []interface{}{
   298  				key("redis"),
   299  				corelease.Request{"redis/0", time.Minute},
   300  			},
   301  			callback: func(leases map[corelease.Key]corelease.Info) {
   302  				leases[key("redis")] = corelease.Info{
   303  					Holder: "redis/0",
   304  					Expiry: offset(newLeaseSecs * time.Second),
   305  				}
   306  			},
   307  		}, {
   308  			// We do trigger a Refresh, but we don't expire the key.
   309  			method: "Refresh",
   310  		}},
   311  	}
   312  	fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) {
   313  		// Ask for a minute, actually get 63s. Don't expire early.
   314  		err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute)
   315  		c.Assert(err, jc.ErrorIsNil)
   316  		// Only the main loop waiter
   317  		waitAdvance(c, clock, almostSeconds(newLeaseSecs), 1)
   318  	})
   319  }
   320  
   321  func (s *ExpireSuite) TestExtend_ExpiryInFuture_TimePasses(c *gc.C) {
   322  	const newLeaseSecs = 63
   323  	firstRelease := make(chan struct{})
   324  	fix := &Fixture{
   325  		leases: map[corelease.Key]corelease.Info{
   326  			key("redis"): {
   327  				Holder: "redis/0",
   328  				Expiry: offset(time.Second),
   329  			},
   330  		},
   331  		expectCalls: []call{{
   332  			method: "ExtendLease",
   333  			args: []interface{}{
   334  				key("redis"),
   335  				corelease.Request{"redis/0", time.Minute},
   336  			},
   337  			callback: func(leases map[corelease.Key]corelease.Info) {
   338  				leases[key("redis")] = corelease.Info{
   339  					Holder: "redis/0",
   340  					Expiry: offset(newLeaseSecs * time.Second),
   341  				}
   342  			},
   343  		}, {
   344  			method: "Refresh",
   345  			callback: func(leases map[corelease.Key]corelease.Info) {
   346  				close(firstRelease)
   347  			},
   348  		}, {
   349  			method: "Refresh",
   350  		}, {
   351  			method: "ExpireLease",
   352  			args:   []interface{}{key("redis")},
   353  			callback: func(leases map[corelease.Key]corelease.Info) {
   354  				delete(leases, key("redis"))
   355  			},
   356  		}},
   357  	}
   358  	fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) {
   359  		// Ask for a minute, actually get 63s. Expire on time.
   360  		err := getClaimer(c, manager).Claim("redis", "redis/0", time.Minute)
   361  		c.Assert(err, jc.ErrorIsNil)
   362  		// Notice the first wakeup, but no expirations, and queued up a second wakeup
   363  		waitAdvance(c, clock, time.Minute, 1)
   364  		select {
   365  		case <-firstRelease:
   366  		case <-time.After(testing.LongWait):
   367  			c.Errorf("waited too long")
   368  		}
   369  		waitAdvance(c, clock, 3*time.Second, 1)
   370  	})
   371  }
   372  
   373  func (s *ExpireSuite) TestExpire_Multiple(c *gc.C) {
   374  	fix := &Fixture{
   375  		leases: map[corelease.Key]corelease.Info{
   376  			key("redis"): {
   377  				Holder: "redis/0",
   378  				Expiry: offset(time.Second),
   379  			},
   380  			key("store"): {
   381  				Holder: "store/3",
   382  				Expiry: offset(5 * time.Second),
   383  			},
   384  			key("tokumx"): {
   385  				Holder: "tokumx/5",
   386  				Expiry: offset(10 * time.Second), // will not expire.
   387  			},
   388  			key("ultron"): {
   389  				Holder: "ultron/7",
   390  				Expiry: offset(5 * time.Second),
   391  			},
   392  			key("vvvvvv"): {
   393  				Holder: "vvvvvv/2",
   394  				Expiry: offset(time.Second), // would expire, but errors first.
   395  			},
   396  		},
   397  		expectCalls: []call{{
   398  			method: "Refresh",
   399  		}, {
   400  			method: "ExpireLease",
   401  			args:   []interface{}{key("redis")},
   402  			callback: func(leases map[corelease.Key]corelease.Info) {
   403  				delete(leases, key("redis"))
   404  			},
   405  		}, {
   406  			method: "ExpireLease",
   407  			args:   []interface{}{key("store")},
   408  			err:    corelease.ErrInvalid,
   409  			callback: func(leases map[corelease.Key]corelease.Info) {
   410  				delete(leases, key("store"))
   411  			},
   412  		}, {
   413  			method: "ExpireLease",
   414  			args:   []interface{}{key("ultron")},
   415  			err:    errors.New("what is this?"),
   416  		}},
   417  		expectDirty: true,
   418  	}
   419  	fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) {
   420  		waitAdvance(c, clock, 5*time.Second, 1)
   421  		err := manager.Wait()
   422  		c.Check(err, gc.ErrorMatches, "what is this\\?")
   423  	})
   424  }
   425  
   426  func waitAdvance(c *gc.C, clock *testclock.Clock, amount time.Duration, waiters int) {
   427  	err := clock.WaitAdvance(amount, coretesting.LongWait, waiters)
   428  	c.Assert(err, jc.ErrorIsNil)
   429  }