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 }