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

     1  // Copyright 2022 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package lease_test
     5  
     6  import (
     7  	"context"
     8  	"time"
     9  
    10  	"github.com/juju/errors"
    11  	jc "github.com/juju/testing/checkers"
    12  	gc "gopkg.in/check.v1"
    13  
    14  	corelease "github.com/juju/juju/core/lease"
    15  	"github.com/juju/juju/database/testing"
    16  	"github.com/juju/juju/worker/lease"
    17  )
    18  
    19  type storeSuite struct {
    20  	testing.ControllerSuite
    21  
    22  	store *lease.Store
    23  }
    24  
    25  var _ = gc.Suite(&storeSuite{})
    26  
    27  func (s *storeSuite) SetUpTest(c *gc.C) {
    28  	s.ControllerSuite.SetUpTest(c)
    29  
    30  	s.store = lease.NewStore(lease.StoreConfig{
    31  		TrackedDB: s.TrackedDB(),
    32  		Logger:    lease.StubLogger{},
    33  	})
    34  }
    35  
    36  func (s *storeSuite) TestClaimLeaseSuccessAndLeaseQueries(c *gc.C) {
    37  	pgKey := corelease.Key{
    38  		Namespace: "application-leadership",
    39  		ModelUUID: "model-uuid",
    40  		Lease:     "postgresql",
    41  	}
    42  
    43  	pgReq := corelease.Request{
    44  		Holder:   "postgresql/0",
    45  		Duration: time.Minute,
    46  	}
    47  
    48  	// Add 2 leases.
    49  	err := s.store.ClaimLease(context.Background(), pgKey, pgReq)
    50  	c.Assert(err, jc.ErrorIsNil)
    51  
    52  	mmKey := pgKey
    53  	mmKey.Lease = "mattermost"
    54  
    55  	mmReq := pgReq
    56  	mmReq.Holder = "mattermost/0"
    57  
    58  	err = s.store.ClaimLease(context.Background(), mmKey, mmReq)
    59  	c.Assert(err, jc.ErrorIsNil)
    60  
    61  	// Check all the leases.
    62  	leases, err := s.store.Leases(context.Background())
    63  	c.Assert(err, jc.ErrorIsNil)
    64  	c.Assert(leases, gc.HasLen, 2)
    65  	c.Check(leases[pgKey].Holder, gc.Equals, "postgresql/0")
    66  	c.Check(leases[pgKey].Expiry.After(time.Now().UTC()), jc.IsTrue)
    67  	c.Check(leases[mmKey].Holder, gc.Equals, "mattermost/0")
    68  	c.Check(leases[mmKey].Expiry.After(time.Now().UTC()), jc.IsTrue)
    69  
    70  	// Check with a filter.
    71  	leases, err = s.store.Leases(context.Background(), pgKey)
    72  	c.Assert(err, jc.ErrorIsNil)
    73  	c.Assert(leases, gc.HasLen, 1)
    74  	c.Check(leases[pgKey].Holder, gc.Equals, "postgresql/0")
    75  
    76  	// Add a lease from a different group,
    77  	// and check that the group returns the application leases.
    78  	err = s.store.ClaimLease(context.Background(),
    79  		corelease.Key{
    80  			Namespace: "singular-controller",
    81  			ModelUUID: "controller-model-uuid",
    82  			Lease:     "singular",
    83  		},
    84  		corelease.Request{
    85  			Holder:   "machine/0",
    86  			Duration: time.Minute,
    87  		},
    88  	)
    89  	c.Assert(err, jc.ErrorIsNil)
    90  
    91  	leases, err = s.store.LeaseGroup(context.Background(), "application-leadership", "model-uuid")
    92  	c.Assert(err, jc.ErrorIsNil)
    93  	c.Assert(leases, gc.HasLen, 2)
    94  	c.Check(leases[pgKey].Holder, gc.Equals, "postgresql/0")
    95  	c.Check(leases[mmKey].Holder, gc.Equals, "mattermost/0")
    96  }
    97  
    98  func (s *storeSuite) TestClaimLeaseAlreadyHeld(c *gc.C) {
    99  	key := corelease.Key{
   100  		Namespace: "singular-controller",
   101  		ModelUUID: "controller-model-uuid",
   102  		Lease:     "singular",
   103  	}
   104  
   105  	req := corelease.Request{
   106  		Holder:   "machine/0",
   107  		Duration: time.Minute,
   108  	}
   109  
   110  	err := s.store.ClaimLease(context.Background(), key, req)
   111  	c.Assert(err, jc.ErrorIsNil)
   112  
   113  	err = s.store.ClaimLease(context.Background(), key, req)
   114  	c.Assert(errors.Is(err, corelease.ErrHeld), jc.IsTrue)
   115  }
   116  
   117  func (s *storeSuite) TestExtendLeaseSuccess(c *gc.C) {
   118  	key := corelease.Key{
   119  		Namespace: "application-leadership",
   120  		ModelUUID: "model-uuid",
   121  		Lease:     "postgresql",
   122  	}
   123  
   124  	req := corelease.Request{
   125  		Holder:   "postgresql/0",
   126  		Duration: time.Minute,
   127  	}
   128  
   129  	err := s.store.ClaimLease(context.Background(), key, req)
   130  	c.Assert(err, jc.ErrorIsNil)
   131  
   132  	leases, err := s.store.Leases(context.Background(), key)
   133  	c.Assert(err, jc.ErrorIsNil)
   134  	c.Assert(leases, gc.HasLen, 1)
   135  
   136  	// Save the expiry for later comparison.
   137  	originalExpiry := leases[key].Expiry
   138  
   139  	req.Duration = 2 * time.Minute
   140  	err = s.store.ExtendLease(context.Background(), key, req)
   141  	c.Assert(err, jc.ErrorIsNil)
   142  
   143  	leases, err = s.store.Leases(context.Background(), key)
   144  	c.Assert(err, jc.ErrorIsNil)
   145  	c.Assert(leases, gc.HasLen, 1)
   146  
   147  	// Check that we extended.
   148  	c.Check(leases[key].Expiry.After(originalExpiry), jc.IsTrue)
   149  }
   150  
   151  func (s *storeSuite) TestExtendLeaseNotHeldInvalid(c *gc.C) {
   152  	key := corelease.Key{
   153  		Namespace: "application-leadership",
   154  		ModelUUID: "model-uuid",
   155  		Lease:     "postgresql",
   156  	}
   157  
   158  	req := corelease.Request{
   159  		Holder:   "postgresql/0",
   160  		Duration: time.Minute,
   161  	}
   162  
   163  	err := s.store.ExtendLease(context.Background(), key, req)
   164  	c.Assert(errors.Is(err, corelease.ErrInvalid), jc.IsTrue)
   165  }
   166  
   167  func (s *storeSuite) TestRevokeLeaseSuccess(c *gc.C) {
   168  	key := corelease.Key{
   169  		Namespace: "application-leadership",
   170  		ModelUUID: "model-uuid",
   171  		Lease:     "postgresql",
   172  	}
   173  
   174  	req := corelease.Request{
   175  		Holder:   "postgresql/0",
   176  		Duration: time.Minute,
   177  	}
   178  
   179  	err := s.store.ClaimLease(context.Background(), key, req)
   180  	c.Assert(err, jc.ErrorIsNil)
   181  
   182  	err = s.store.RevokeLease(context.Background(), key, req.Holder)
   183  	c.Assert(err, jc.ErrorIsNil)
   184  }
   185  
   186  func (s *storeSuite) TestRevokeLeaseNotHeldInvalid(c *gc.C) {
   187  	key := corelease.Key{
   188  		Namespace: "application-leadership",
   189  		ModelUUID: "model-uuid",
   190  		Lease:     "postgresql",
   191  	}
   192  
   193  	err := s.store.RevokeLease(context.Background(), key, "not-the-holder")
   194  	c.Assert(errors.Is(err, corelease.ErrInvalid), jc.IsTrue)
   195  }
   196  
   197  func (s *storeSuite) TestPinUnpinLeaseAndPinQueries(c *gc.C) {
   198  	pgKey := corelease.Key{
   199  		Namespace: "application-leadership",
   200  		ModelUUID: "model-uuid",
   201  		Lease:     "postgresql",
   202  	}
   203  
   204  	pgReq := corelease.Request{
   205  		Holder:   "postgresql/0",
   206  		Duration: time.Minute,
   207  	}
   208  
   209  	err := s.store.ClaimLease(context.Background(), pgKey, pgReq)
   210  	c.Assert(err, jc.ErrorIsNil)
   211  
   212  	// One entity pins the lease.
   213  	err = s.store.PinLease(context.Background(), pgKey, "machine/6")
   214  	c.Assert(err, jc.ErrorIsNil)
   215  
   216  	// The same lease/entity is a no-op without error.
   217  	err = s.store.PinLease(context.Background(), pgKey, "machine/6")
   218  	c.Assert(err, jc.ErrorIsNil)
   219  
   220  	// Another entity pinning the same lease.
   221  	err = s.store.PinLease(context.Background(), pgKey, "machine/7")
   222  	c.Assert(err, jc.ErrorIsNil)
   223  
   224  	pins, err := s.store.Pinned(context.Background())
   225  	c.Assert(err, jc.ErrorIsNil)
   226  	c.Assert(pins, gc.HasLen, 1)
   227  	c.Check(pins[pgKey], jc.SameContents, []string{"machine/6", "machine/7"})
   228  
   229  	// Unpin and check the leases.
   230  	err = s.store.UnpinLease(context.Background(), pgKey, "machine/7")
   231  	c.Assert(err, jc.ErrorIsNil)
   232  
   233  	pins, err = s.store.Pinned(context.Background())
   234  	c.Assert(err, jc.ErrorIsNil)
   235  	c.Assert(pins, gc.HasLen, 1)
   236  	c.Check(pins[pgKey], jc.SameContents, []string{"machine/6"})
   237  }
   238  
   239  func (s *storeSuite) TestLeaseOperationCancellation(c *gc.C) {
   240  	ctx, cancel := context.WithCancel(context.Background())
   241  	cancel()
   242  
   243  	key := corelease.Key{
   244  		Namespace: "application-leadership",
   245  		ModelUUID: "model-uuid",
   246  		Lease:     "postgresql",
   247  	}
   248  
   249  	req := corelease.Request{
   250  		Holder:   "postgresql/0",
   251  		Duration: time.Minute,
   252  	}
   253  
   254  	err := s.store.ClaimLease(ctx, key, req)
   255  	c.Assert(err, gc.ErrorMatches, "context canceled")
   256  }