func (t *Tester) testUpdateFailsOnVersionTooOld(obj runtime.Object, setFn SetFunc, getFn GetFunc) { ctx := t.TestContext() foo := copyOrDie(obj) t.setObjectMeta(foo, "foo3") if err := setFn(ctx, foo); err != nil { t.Errorf("unexpected error: %v", err) } storedFoo, err := getFn(ctx, foo) if err != nil { t.Errorf("unexpected error: %v", err) } older := copyOrDie(storedFoo) olderMeta := t.getObjectMetaOrFail(older) olderMeta.ResourceVersion = "1" _, _, err = t.storage.(rest.Updater).Update(t.TestContext(), older) if err == nil { t.Errorf("Expected an error, but we didn't get one") } else if !errors.IsConflict(err) { t.Errorf("Expected Conflict error, got '%v'", err) } }
// RetryConflict executes the provided function repeatedly, retrying if the server returns a conflicting // write. Callers should preserve previous executions if they wish to retry changes. It performs an // exponential backoff. // // var pod *api.Pod // err := RetryOnConflict(DefaultBackoff, func() (err error) { // pod, err = c.Pods("mynamespace").UpdateStatus(podStatus) // return // }) // if err != nil { // // may be conflict if max retries were hit // return err // } // ... // // TODO: Make Backoff an interface? func RetryOnConflict(backoff wait.Backoff, fn func() error) error { var lastConflictErr error err := wait.ExponentialBackoff(backoff, func() (bool, error) { err := fn() switch { case err == nil: return true, nil case errors.IsConflict(err): lastConflictErr = err return false, nil default: return false, err } }) if err == wait.ErrWaitTimeout { err = lastConflictErr } return err }