Skip to content

Commit

Permalink
Merge pull request kubernetes#104208 from liggitt/automated-cherry-pi…
Browse files Browse the repository at this point in the history
…ck-of-#104182-upstream-release-1.21

Automated cherry pick of kubernetes#104182: Avoid spurious calls to update/delete validation
  • Loading branch information
k8s-ci-robot committed Aug 10, 2021
2 parents fcdd6d8 + 98d3774 commit f852c52
Show file tree
Hide file tree
Showing 3 changed files with 67 additions and 1 deletion.
Expand Up @@ -1141,6 +1141,8 @@ func TestStoreUpdateHooksInnerRetry(t *testing.T) {
registry.Decorator = tc.decorator
ttlFailDone = false
registry.TTLFunc = tc.ttl
// force storage to use a cached object with a non-matching resourceVersion to guarantee a live lookup + retry
created.(*example.Pod).ResourceVersion += "0"
registry.Storage.Storage = &staleGuaranteedUpdateStorage{Interface: registry.Storage.Storage, cachedObj: created}
_, _, err = registry.Update(testContext, pod.Name, rest.DefaultUpdatedObjectInfo(pod), rest.ValidateAllObjectFunc, rest.ValidateAllObjectUpdateFunc, false, &metav1.UpdateOptions{})
if err != nil && !tc.expectError {
Expand Down
30 changes: 30 additions & 0 deletions staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go
Expand Up @@ -230,12 +230,22 @@ func (s *store) conditionalDelete(
}

// It's possible we're 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
}
origStateIsCurrent = true

// it turns out our cached data was not stale, return the error
if cachedRev == origState.rev {
return cachedUpdateErr
}

// Retry
continue
}
Expand All @@ -246,12 +256,22 @@ func (s *store) conditionalDelete(
}

// It's possible we're 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
}
origStateIsCurrent = true

// it turns out our cached data was not stale, return the error
if cachedRev == origState.rev {
return cachedUpdateErr
}

// Retry
continue
}
Expand Down Expand Up @@ -345,12 +365,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
}
origStateIsCurrent = true

// it turns out our cached data was not stale, return the error
if cachedRev == origState.rev {
return cachedUpdateErr
}

// Retry
continue
}
Expand Down
36 changes: 35 additions & 1 deletion staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go
Expand Up @@ -461,15 +461,20 @@ func TestValidateDeletionWithSuggestion(t *testing.T) {

key, originalPod := testPropogateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name"}})

// Check that validaing fresh object fails.
// Check that validaing fresh object fails is called once and fails.
validationCalls := 0
validationError := fmt.Errorf("validation error")
validateNothing := func(_ context.Context, _ runtime.Object) error {
validationCalls++
return validationError
}
out := &example.Pod{}
if err := store.Delete(ctx, key, out, nil, validateNothing, originalPod); err != validationError {
t.Errorf("Unexpected failure during deletion: %v", err)
}
if validationCalls != 1 {
t.Errorf("validate function should have been called once, called %d", validationCalls)
}

// First update, so originalPod is outdated.
updatedPod := &example.Pod{}
Expand Down Expand Up @@ -977,6 +982,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) {
Expand Down

0 comments on commit f852c52

Please sign in to comment.