github.com/juju/juju@v0.0.0-20240327075706-a90865de2538/worker/lease/manager_cross_test.go (about)

     1  // Copyright 2018 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  	"github.com/juju/juju/worker/lease"
    18  )
    19  
    20  // Tests that check the manager handles leases across namespaces and
    21  // models correctly.
    22  
    23  type CrossSuite struct {
    24  	testing.IsolationSuite
    25  }
    26  
    27  var _ = gc.Suite(&CrossSuite{})
    28  
    29  func (s *CrossSuite) testClaims(c *gc.C, lease1, lease2 corelease.Key) {
    30  	fix := Fixture{
    31  		expectCalls: []call{{
    32  			method: "ClaimLease",
    33  			args: []interface{}{
    34  				lease1,
    35  				corelease.Request{Holder: "sgt-howie", Duration: time.Minute},
    36  			},
    37  			callback: func(leases map[corelease.Key]corelease.Info) {
    38  				leases[lease1] = corelease.Info{
    39  					Holder: "sgt-howie",
    40  					Expiry: offset(time.Second),
    41  				}
    42  			},
    43  		}, {
    44  			method: "ClaimLease",
    45  			args: []interface{}{
    46  				lease2,
    47  				corelease.Request{Holder: "rowan", Duration: time.Minute},
    48  			},
    49  			callback: func(leases map[corelease.Key]corelease.Info) {
    50  				leases[lease2] = corelease.Info{
    51  					Holder: "rowan",
    52  					Expiry: offset(time.Second),
    53  				}
    54  			},
    55  		}},
    56  	}
    57  	fix.RunTest(c, func(manager *lease.Manager, _ *testclock.Clock) {
    58  		claimer1, err := manager.Claimer(lease1.Namespace, lease1.ModelUUID)
    59  		c.Assert(err, jc.ErrorIsNil)
    60  		claimer2, err := manager.Claimer(lease2.Namespace, lease2.ModelUUID)
    61  		c.Assert(err, jc.ErrorIsNil)
    62  
    63  		err = claimer1.Claim(lease1.Lease, "sgt-howie", time.Minute)
    64  		c.Assert(err, jc.ErrorIsNil)
    65  		err = claimer2.Claim(lease2.Lease, "rowan", time.Minute)
    66  		c.Assert(err, jc.ErrorIsNil)
    67  
    68  		err = claimer1.Claim(lease1.Lease, "lord-summerisle", time.Minute)
    69  		c.Assert(err, gc.Equals, corelease.ErrClaimDenied)
    70  	})
    71  }
    72  
    73  func (s *CrossSuite) TestClaimAcrossNamespaces(c *gc.C) {
    74  	s.testClaims(c,
    75  		key("ns1", "model", "summerisle"),
    76  		key("ns2", "model", "summerisle"),
    77  	)
    78  }
    79  
    80  func (s *CrossSuite) TestClaimAcrossModels(c *gc.C) {
    81  	s.testClaims(c,
    82  		key("ns", "model1", "summerisle"),
    83  		key("ns", "model2", "summerisle"),
    84  	)
    85  }
    86  
    87  func (s *CrossSuite) testWaits(c *gc.C, lease1, lease2 corelease.Key) {
    88  	fix := Fixture{
    89  		leases: map[corelease.Key]corelease.Info{
    90  			lease1: {
    91  				Holder: "sgt-howie",
    92  				Expiry: offset(time.Second),
    93  			},
    94  			lease2: {
    95  				Holder: "willow",
    96  				Expiry: offset(time.Minute),
    97  			},
    98  		},
    99  	}
   100  	fix.RunTest(c, func(manager *lease.Manager, clock *testclock.Clock) {
   101  		b1 := newBlockTest(c, manager, lease1)
   102  		b2 := newBlockTest(c, manager, lease2)
   103  
   104  		b1.assertBlocked(c)
   105  		b2.assertBlocked(c)
   106  
   107  		clock.Advance(2 * time.Second)
   108  
   109  		err := b1.assertUnblocked(c)
   110  		c.Assert(err, jc.ErrorIsNil)
   111  		b2.assertBlocked(c)
   112  	})
   113  }
   114  
   115  func (s *CrossSuite) TestWaitAcrossNamespaces(c *gc.C) {
   116  	s.testWaits(c,
   117  		key("ns1", "model", "summerisle"),
   118  		key("ns2", "model", "summerisle"),
   119  	)
   120  }
   121  
   122  func (s *CrossSuite) TestWaitAcrossModels(c *gc.C) {
   123  	s.testWaits(c,
   124  		key("ns", "model1", "summerisle"),
   125  		key("ns", "model2", "summerisle"),
   126  	)
   127  }
   128  
   129  func (s *CrossSuite) testChecks(c *gc.C, lease1, lease2 corelease.Key) {
   130  	fix := Fixture{
   131  		leases: map[corelease.Key]corelease.Info{
   132  			lease1: {
   133  				Holder: "sgt-howie",
   134  				Expiry: offset(time.Second),
   135  			},
   136  		},
   137  	}
   138  	fix.RunTest(c, func(manager *lease.Manager, _ *testclock.Clock) {
   139  		checker1, err := manager.Checker(lease1.Namespace, lease1.ModelUUID)
   140  		c.Assert(err, jc.ErrorIsNil)
   141  		checker2, err := manager.Checker(lease2.Namespace, lease2.ModelUUID)
   142  		c.Assert(err, jc.ErrorIsNil)
   143  
   144  		t1 := checker1.Token(lease1.Lease, "sgt-howie")
   145  		c.Assert(t1.Check(), gc.Equals, nil)
   146  
   147  		t2 := checker2.Token(lease2.Lease, "sgt-howie")
   148  		err = t2.Check()
   149  		c.Assert(errors.Cause(err), gc.Equals, corelease.ErrNotHeld)
   150  	})
   151  }
   152  
   153  func (s *CrossSuite) TestCheckAcrossNamespaces(c *gc.C) {
   154  	s.testChecks(c,
   155  		key("ns1", "model", "summerisle"),
   156  		key("ns2", "model", "summerisle"),
   157  	)
   158  }
   159  
   160  func (s *CrossSuite) TestCheckAcrossModels(c *gc.C) {
   161  	s.testChecks(c,
   162  		key("ns", "model1", "summerisle"),
   163  		key("ns", "model2", "summerisle"),
   164  	)
   165  }
   166  
   167  func (s *CrossSuite) TestDifferentNamespaceValidation(c *gc.C) {
   168  	clock := testclock.NewClock(defaultClockStart)
   169  	store := NewStore(nil, nil, clock)
   170  	manager, err := lease.NewManager(lease.ManagerConfig{
   171  		Clock: clock,
   172  		Store: store,
   173  		Secretary: func(namespace string) (lease.Secretary, error) {
   174  			switch namespace {
   175  			case "ns1":
   176  				return Secretary{}, nil
   177  			case "ns2":
   178  				return OtherSecretary{}, nil
   179  			default:
   180  				return nil, errors.Errorf("bad namespace!")
   181  			}
   182  		},
   183  		MaxSleep:             defaultMaxSleep,
   184  		Logger:               loggo.GetLogger("lease_test"),
   185  		PrometheusRegisterer: noopRegisterer{},
   186  	})
   187  	c.Assert(err, jc.ErrorIsNil)
   188  	defer func() {
   189  		manager.Kill()
   190  		err := manager.Wait()
   191  		c.Check(err, jc.ErrorIsNil)
   192  	}()
   193  	defer store.Wait(c)
   194  
   195  	_, err = manager.Claimer("something-else", "model")
   196  	c.Assert(err, gc.ErrorMatches, "bad namespace!")
   197  
   198  	c1, err := manager.Claimer("ns1", "model")
   199  	c.Assert(err, jc.ErrorIsNil)
   200  	err = c1.Claim("INVALID", "sgt-howie", time.Minute)
   201  	c.Assert(err, gc.ErrorMatches, `cannot claim lease "INVALID": name not valid`)
   202  
   203  	c2, err := manager.Claimer("ns2", "model")
   204  	c.Assert(err, jc.ErrorIsNil)
   205  	err = c2.Claim("INVALID", "sgt-howie", time.Minute)
   206  	c.Assert(err, gc.ErrorMatches, `cannot claim lease "INVALID": lease name not valid`)
   207  }
   208  
   209  type OtherSecretary struct{}
   210  
   211  // CheckLease is part of the lease.Secretary interface.
   212  func (OtherSecretary) CheckLease(_ corelease.Key) error {
   213  	return errors.NotValidf("lease name")
   214  }
   215  
   216  // CheckHolder is part of the lease.Secretary interface.
   217  func (OtherSecretary) CheckHolder(_ string) error {
   218  	return errors.NotValidf("holder name")
   219  }
   220  
   221  // CheckDuration is part of the lease.Secretary interface.
   222  func (OtherSecretary) CheckDuration(duration time.Duration) error {
   223  	if duration != time.Hour {
   224  		return errors.NotValidf("time")
   225  	}
   226  	return nil
   227  }