diff --git a/staging/src/k8s.io/apiserver/pkg/storage/errors.go b/staging/src/k8s.io/apiserver/pkg/storage/errors.go index ed4f4d0d0e8..5f29097c59c 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/errors.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/errors.go @@ -17,13 +17,16 @@ limitations under the License. package storage import ( + "errors" "fmt" - "k8s.io/apimachinery/pkg/api/errors" + apierrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/validation/field" ) +var ErrResourceVersionSetOnCreate = errors.New("resourceVersion should not be set on objects to be created") + const ( ErrCodeKeyNotFound int = iota + 1 ErrCodeKeyExists @@ -176,7 +179,7 @@ var tooLargeResourceVersionCauseMsg = "Too large resource version" // NewTooLargeResourceVersionError returns a timeout error with the given retrySeconds for a request for // a minimum resource version that is larger than the largest currently available resource version for a requested resource. func NewTooLargeResourceVersionError(minimumResourceVersion, currentRevision uint64, retrySeconds int) error { - err := errors.NewTimeoutError(fmt.Sprintf("Too large resource version: %d, current: %d", minimumResourceVersion, currentRevision), retrySeconds) + err := apierrors.NewTimeoutError(fmt.Sprintf("Too large resource version: %d, current: %d", minimumResourceVersion, currentRevision), retrySeconds) err.ErrStatus.Details.Causes = []metav1.StatusCause{ { Type: metav1.CauseTypeResourceVersionTooLarge, @@ -188,8 +191,8 @@ func NewTooLargeResourceVersionError(minimumResourceVersion, currentRevision uin // IsTooLargeResourceVersion returns true if the error is a TooLargeResourceVersion error. func IsTooLargeResourceVersion(err error) bool { - if !errors.IsTimeout(err) { + if !apierrors.IsTimeout(err) { return false } - return errors.HasStatusCause(err, metav1.CauseTypeResourceVersionTooLarge) + return apierrors.HasStatusCause(err, metav1.CauseTypeResourceVersionTooLarge) } 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 a2472123498..ad2896f7957 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go @@ -199,7 +199,7 @@ func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object, ) defer span.End(500 * time.Millisecond) if version, err := s.versioner.ObjectResourceVersion(obj); err == nil && version != 0 { - return errors.New("resourceVersion should not be set on objects to be created") + return storage.ErrResourceVersionSetOnCreate } if err := s.versioner.PrepareObjectForStorage(obj); err != nil { return fmt.Errorf("PrepareObjectForStorage failed: %v", err) diff --git a/staging/src/k8s.io/apiserver/pkg/storage/testing/store_tests.go b/staging/src/k8s.io/apiserver/pkg/storage/testing/store_tests.go index da859a57ee7..ac3618057f6 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/testing/store_tests.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/testing/store_tests.go @@ -44,30 +44,45 @@ import ( type KeyValidation func(ctx context.Context, t *testing.T, key string) func RunTestCreate(ctx context.Context, t *testing.T, store storage.Interface, validation KeyValidation) { - out := &example.Pod{} - obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns", SelfLink: "testlink"}} + tests := []struct { + name string + inputObj *example.Pod + expectedError error + }{{ + name: "successful create", + inputObj: &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}}, + }, { + name: "create with ResourceVersion set", + inputObj: &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "test-ns", ResourceVersion: "1"}}, + expectedError: storage.ErrResourceVersionSetOnCreate, + }} - // verify that kv pair is empty before set - key := computePodKey(obj) - if err := store.Get(ctx, key, storage.GetOptions{}, out); !storage.IsNotFound(err) { - t.Fatalf("expecting empty result on key %s, got %v", key, err) - } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + out := &example.Pod{} // reset + // verify that kv pair is empty before set + key := computePodKey(tt.inputObj) + if err := store.Get(ctx, key, storage.GetOptions{}, out); !storage.IsNotFound(err) { + t.Fatalf("expecting empty result on key %s, got %v", key, err) + } - if err := store.Create(ctx, key, obj, out, 0); err != nil { - t.Fatalf("Set failed: %v", err) + err := store.Create(ctx, key, tt.inputObj, out, 0) + if !errors.Is(err, tt.expectedError) { + t.Errorf("expecting error %v, but get: %v", tt.expectedError, err) + } + if err != nil { + return + } + // basic tests of the output + if tt.inputObj.ObjectMeta.Name != out.ObjectMeta.Name { + t.Errorf("pod name want=%s, get=%s", tt.inputObj.ObjectMeta.Name, out.ObjectMeta.Name) + } + if out.ResourceVersion == "" { + t.Errorf("output should have non-empty resource version") + } + validation(ctx, t, key) + }) } - // basic tests of the output - if obj.ObjectMeta.Name != out.ObjectMeta.Name { - t.Errorf("pod name want=%s, get=%s", obj.ObjectMeta.Name, out.ObjectMeta.Name) - } - if out.ResourceVersion == "" { - t.Errorf("output should have non-empty resource version") - } - if out.SelfLink != "" { - t.Errorf("output should have empty selfLink") - } - - validation(ctx, t, key) } func RunTestCreateWithTTL(ctx context.Context, t *testing.T, store storage.Interface) {