Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automated cherry pick of #77619: In GuaranteedUpdate, retry on any error if we are working #77880

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2108,3 +2108,47 @@ func TestDeletionFinalizersForGarbageCollection(t *testing.T) {
}
}
}

type staleGuaranteedUpdateStorage struct {
storage.Interface
cachedObj runtime.Object
}

// GuaranteedUpdate overwrites the method with one that always suggests the cachedObj.
func (s *staleGuaranteedUpdateStorage) GuaranteedUpdate(
ctx context.Context, key string, ptrToType runtime.Object, ignoreNotFound bool,
preconditions *storage.Preconditions, tryUpdate storage.UpdateFunc, _ ...runtime.Object) error {
return s.Interface.GuaranteedUpdate(ctx, key, ptrToType, ignoreNotFound, preconditions, tryUpdate, s.cachedObj)
}

func TestDeleteWithCachedObject(t *testing.T) {
podName := "foo"
podWithFinalizer := &example.Pod{
ObjectMeta: metav1.ObjectMeta{Name: podName, Finalizers: []string{"foo.com/x"}},
Spec: example.PodSpec{NodeName: "machine"},
}
podWithNoFinalizer := &example.Pod{
ObjectMeta: metav1.ObjectMeta{Name: podName},
Spec: example.PodSpec{NodeName: "machine"},
}
ctx := genericapirequest.WithNamespace(genericapirequest.NewContext(), "test")
destroyFunc, registry := newTestGenericStoreRegistry(t, scheme, false)
defer destroyFunc()
// cached object does not have any finalizer.
registry.Storage.Storage = &staleGuaranteedUpdateStorage{Interface: registry.Storage.Storage, cachedObj: podWithNoFinalizer}
// created object with pending finalizer.
_, err := registry.Create(ctx, podWithFinalizer, rest.ValidateAllObjectFunc, &metav1.CreateOptions{})
if err != nil {
t.Fatal(err)
}
// The object shouldn't be deleted, because the persisted object has pending finalizers.
_, _, err = registry.Delete(ctx, podName, nil)
if err != nil {
t.Fatal(err)
}
// The object should still be there
_, err = registry.Get(ctx, podName, &metav1.GetOptions{})
if err != nil {
t.Fatal(err)
}
}
23 changes: 12 additions & 11 deletions staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -300,19 +300,20 @@ func (s *store) GuaranteedUpdate(

ret, ttl, err := s.updateState(origState, tryUpdate)
if err != nil {
// It's possible we were working with stale data
if mustCheckData && apierrors.IsConflict(err) {
// Actually fetch
origState, err = getCurrentState()
if err != nil {
return err
}
mustCheckData = false
// Retry
continue
// If our data is already up to date, return the error
if !mustCheckData {
return err
}

return err
// It's possible we were working with stale data
// Actually fetch
origState, err = getCurrentState()
if err != nil {
return err
}
mustCheckData = false
// Retry
continue
}

data, err := runtime.Encode(s.codec, ret)
Expand Down