diff --git a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go index 67ae9f58f2491..091497b020519 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go @@ -301,12 +301,22 @@ func (s *store) GuaranteedUpdate( } // It's possible we were working with stale data + // Remember the revision of the potentially stale data and the resulting update error + cachedRev := origState.rev + cachedUpdateErr := err + // Actually fetch origState, err = getCurrentState() if err != nil { return err } mustCheckData = false + + // it turns out our cached data was not stale, return the error + if cachedRev == origState.rev { + return cachedUpdateErr + } + // Retry continue } diff --git a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go index 8324f99b9553d..fcae066884d35 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go @@ -817,6 +817,35 @@ func TestGuaranteedUpdateWithSuggestionAndConflict(t *testing.T) { if updatedPod2.Name != "foo-3" { t.Errorf("unexpected pod name: %q", updatedPod2.Name) } + + // Third, update using a current version as the suggestion. + // Return an error and make sure that SimpleUpdate is NOT called a second time, + // since the live lookup shows the suggestion was already up to date. + attempts := 0 + updatedPod3 := &example.Pod{} + err = store.GuaranteedUpdate(ctx, key, updatedPod3, false, nil, + storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) { + pod := obj.(*example.Pod) + if pod.Name != updatedPod2.Name || pod.ResourceVersion != updatedPod2.ResourceVersion { + t.Errorf( + "unexpected live object (name=%s, rv=%s), expected name=%s, rv=%s", + pod.Name, + pod.ResourceVersion, + updatedPod2.Name, + updatedPod2.ResourceVersion, + ) + } + attempts++ + return nil, fmt.Errorf("validation or admission error") + }), + updatedPod2, + ) + if err == nil { + t.Fatalf("expected error, got none") + } + if attempts != 1 { + t.Errorf("expected 1 attempt, got %d", attempts) + } } func TestTransformationFailure(t *testing.T) {