mirror of
https://github.com/kubernetes/client-go.git
synced 2025-08-31 14:44:50 +00:00
Merge pull request #132849 from kei01234kei/update_lease_resource_version_before_update_lease
Prevent the failure of releasing the lock by updating the resource version in case of the resource conflict Kubernetes-commit: 793191529651ab5c9e49914f19e9bdf85d412218
This commit is contained in:
@@ -306,18 +306,30 @@ func (le *LeaderElector) renew(ctx context.Context) {
|
|||||||
|
|
||||||
// release attempts to release the leader lease if we have acquired it.
|
// release attempts to release the leader lease if we have acquired it.
|
||||||
func (le *LeaderElector) release() bool {
|
func (le *LeaderElector) release() bool {
|
||||||
|
ctx := context.Background()
|
||||||
|
timeoutCtx, timeoutCancel := context.WithTimeout(ctx, le.config.RenewDeadline)
|
||||||
|
defer timeoutCancel()
|
||||||
|
// update the resourceVersion of lease
|
||||||
|
oldLeaderElectionRecord, _, err := le.config.Lock.Get(timeoutCtx)
|
||||||
|
if err != nil {
|
||||||
|
if !errors.IsNotFound(err) {
|
||||||
|
klog.Errorf("error retrieving resource lock %v: %v", le.config.Lock.Describe(), err)
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
klog.Infof("lease lock not found: %v", le.config.Lock.Describe())
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if !le.IsLeader() {
|
if !le.IsLeader() {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
now := metav1.NewTime(le.clock.Now())
|
now := metav1.NewTime(le.clock.Now())
|
||||||
leaderElectionRecord := rl.LeaderElectionRecord{
|
leaderElectionRecord := rl.LeaderElectionRecord{
|
||||||
LeaderTransitions: le.observedRecord.LeaderTransitions,
|
LeaderTransitions: oldLeaderElectionRecord.LeaderTransitions,
|
||||||
LeaseDurationSeconds: 1,
|
LeaseDurationSeconds: 1,
|
||||||
RenewTime: now,
|
RenewTime: now,
|
||||||
AcquireTime: now,
|
AcquireTime: now,
|
||||||
}
|
}
|
||||||
timeoutCtx, timeoutCancel := context.WithTimeout(context.Background(), le.config.RenewDeadline)
|
|
||||||
defer timeoutCancel()
|
|
||||||
if err := le.config.Lock.Update(timeoutCtx, leaderElectionRecord); err != nil {
|
if err := le.config.Lock.Update(timeoutCtx, leaderElectionRecord); err != nil {
|
||||||
klog.Errorf("Failed to release lock: %v", err)
|
klog.Errorf("Failed to release lock: %v", err)
|
||||||
return false
|
return false
|
||||||
|
@@ -557,7 +557,7 @@ func testReleaseLease(t *testing.T, objectType string) {
|
|||||||
verb: "get",
|
verb: "get",
|
||||||
objectType: objectType,
|
objectType: objectType,
|
||||||
reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) {
|
reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) {
|
||||||
return true, nil, errors.NewNotFound(action.(fakeclient.GetAction).GetResource().GroupResource(), action.(fakeclient.GetAction).GetName())
|
return true, createLockObject(t, objectType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{HolderIdentity: "baz"}), nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -574,6 +574,13 @@ func testReleaseLease(t *testing.T, objectType string) {
|
|||||||
return true, action.(fakeclient.UpdateAction).GetObject(), nil
|
return true, action.(fakeclient.UpdateAction).GetObject(), nil
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
verb: "get",
|
||||||
|
objectType: objectType,
|
||||||
|
reaction: func(action fakeclient.Action) (handled bool, ret runtime.Object, err error) {
|
||||||
|
return true, nil, errors.NewNotFound(action.(fakeclient.GetAction).GetResource().GroupResource(), action.(fakeclient.GetAction).GetName())
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
expectSuccess: true,
|
expectSuccess: true,
|
||||||
outHolder: "",
|
outHolder: "",
|
||||||
@@ -677,6 +684,59 @@ func TestReleaseLeaseLeases(t *testing.T) {
|
|||||||
testReleaseLease(t, "leases")
|
testReleaseLease(t, "leases")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TestReleaseMethodCallsGet test release method calls Get
|
||||||
|
func TestReleaseMethodCallsGet(t *testing.T) {
|
||||||
|
objectType := "leases"
|
||||||
|
getCalled := false
|
||||||
|
|
||||||
|
lockMeta := metav1.ObjectMeta{Namespace: "foo", Name: "bar"}
|
||||||
|
recorder := record.NewFakeRecorder(100)
|
||||||
|
resourceLockConfig := rl.ResourceLockConfig{
|
||||||
|
Identity: "baz",
|
||||||
|
EventRecorder: recorder,
|
||||||
|
}
|
||||||
|
c := &fake.Clientset{}
|
||||||
|
c.AddReactor("get", objectType, func(action fakeclient.Action) (bool, runtime.Object, error) {
|
||||||
|
// flag to check if Get is called
|
||||||
|
getCalled = true
|
||||||
|
return true, createLockObject(t, objectType, action.GetNamespace(), action.(fakeclient.GetAction).GetName(), &rl.LeaderElectionRecord{
|
||||||
|
HolderIdentity: "baz",
|
||||||
|
LeaseDurationSeconds: 10,
|
||||||
|
}), nil
|
||||||
|
})
|
||||||
|
c.AddReactor("update", objectType, func(action fakeclient.Action) (bool, runtime.Object, error) {
|
||||||
|
return true, action.(fakeclient.UpdateAction).GetObject(), nil
|
||||||
|
})
|
||||||
|
|
||||||
|
lock := &rl.LeaseLock{
|
||||||
|
LeaseMeta: lockMeta,
|
||||||
|
LockConfig: resourceLockConfig,
|
||||||
|
Client: c.CoordinationV1(),
|
||||||
|
}
|
||||||
|
lec := LeaderElectionConfig{
|
||||||
|
Lock: lock,
|
||||||
|
LeaseDuration: 10 * time.Second,
|
||||||
|
Callbacks: LeaderCallbacks{
|
||||||
|
OnNewLeader: func(l string) {},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
observedRawRecord := GetRawRecordOrDie(t, objectType, rl.LeaderElectionRecord{HolderIdentity: "baz"})
|
||||||
|
le := &LeaderElector{
|
||||||
|
config: lec,
|
||||||
|
observedRecord: rl.LeaderElectionRecord{HolderIdentity: "baz"},
|
||||||
|
observedRawRecord: observedRawRecord,
|
||||||
|
observedTime: time.Now(),
|
||||||
|
clock: clock.RealClock{},
|
||||||
|
metrics: globalMetricsFactory.newLeaderMetrics(),
|
||||||
|
}
|
||||||
|
|
||||||
|
le.release()
|
||||||
|
|
||||||
|
if !getCalled {
|
||||||
|
t.Errorf("release method does not call Get")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestReleaseOnCancellation_Leases(t *testing.T) {
|
func TestReleaseOnCancellation_Leases(t *testing.T) {
|
||||||
testReleaseOnCancellation(t, "leases")
|
testReleaseOnCancellation(t, "leases")
|
||||||
}
|
}
|
||||||
@@ -791,9 +851,11 @@ func testReleaseOnCancellation(t *testing.T, objectType string) {
|
|||||||
if lockObj != nil {
|
if lockObj != nil {
|
||||||
// Third and more get (first create, second renew) should return our canceled error
|
// Third and more get (first create, second renew) should return our canceled error
|
||||||
// FakeClient doesn't do anything with the context so we're doing this ourselves
|
// FakeClient doesn't do anything with the context so we're doing this ourselves
|
||||||
if gets >= 3 {
|
if gets >= 4 {
|
||||||
|
if gets == 4 {
|
||||||
close(onRenewCalled)
|
close(onRenewCalled)
|
||||||
<-onRenewResume
|
<-onRenewResume
|
||||||
|
}
|
||||||
return true, nil, context.Canceled
|
return true, nil, context.Canceled
|
||||||
}
|
}
|
||||||
return true, lockObj, nil
|
return true, lockObj, nil
|
||||||
|
Reference in New Issue
Block a user