diff --git a/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_whitebox_test.go b/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_whitebox_test.go index 6b8e106a71c..f8ce760e55b 100644 --- a/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_whitebox_test.go +++ b/staging/src/k8s.io/apiserver/pkg/storage/cacher/cacher_whitebox_test.go @@ -250,15 +250,23 @@ func TestShouldDelegateList(t *testing.T) { if err != nil { t.Fatalf("Unexpected error: %v", err) } + continueOnNegativeRV, err := storage.EncodeContinue(keyPrefix+"foo", keyPrefix, -1) + if err != nil { + t.Fatalf("Unexpected error: %v", err) + } testCases := map[opts]bool{} testCases[opts{}] = true testCases[opts{Limit: 100}] = true testCases[opts{Continue: continueOnRev1}] = true testCases[opts{Limit: 100, Continue: continueOnRev1}] = true + testCases[opts{Continue: continueOnNegativeRV}] = true + testCases[opts{Limit: 100, Continue: continueOnNegativeRV}] = true testCases[opts{ResourceVersion: "0"}] = false testCases[opts{ResourceVersion: "0", Limit: 100}] = false testCases[opts{ResourceVersion: "0", Continue: continueOnRev1}] = true testCases[opts{ResourceVersion: "0", Limit: 100, Continue: continueOnRev1}] = true + testCases[opts{ResourceVersion: "0", Continue: continueOnNegativeRV}] = true + testCases[opts{ResourceVersion: "0", Limit: 100, Continue: continueOnNegativeRV}] = true testCases[opts{ResourceVersion: "0", ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan}] = false testCases[opts{ResourceVersion: "0", ResourceVersionMatch: metav1.ResourceVersionMatchNotOlderThan, Limit: 100}] = false testCases[opts{ResourceVersion: "1"}] = false @@ -276,10 +284,12 @@ func TestShouldDelegateList(t *testing.T) { // Continue is ignored on non recursive LIST testCases[opts{ResourceVersion: "1", Continue: continueOnRev1}] = true testCases[opts{ResourceVersion: "1", Continue: continueOnRev1, Limit: 100}] = true + testCases[opts{ResourceVersion: "1", Continue: continueOnNegativeRV}] = true + testCases[opts{ResourceVersion: "1", Continue: continueOnNegativeRV, Limit: 100}] = true for _, rv := range []string{"", "0", "1"} { for _, match := range []metav1.ResourceVersionMatch{"", metav1.ResourceVersionMatchExact, metav1.ResourceVersionMatchNotOlderThan} { - for _, continueKey := range []string{"", continueOnRev1} { + for _, continueKey := range []string{"", continueOnRev1, continueOnNegativeRV} { for _, limit := range []int64{0, 100} { for _, recursive := range []bool{true, false} { opt := opts{ 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 3de5afe5866..a7ce4014f25 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 @@ -1493,6 +1493,73 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, inc expectContinueExact: encodeContinueOrDie(createdPods[1].Namespace+"/"+createdPods[1].Name+"\x00", int64(continueRV+1)), expectedRemainingItemCount: utilpointer.Int64(3), }, + { + name: "test List with continue from second pod, negative resource version gives consistent read", + prefix: "/pods/", + pred: storage.SelectionPredicate{ + Label: labels.Everything(), + Field: fields.Everything(), + Continue: encodeContinueOrDie(createdPods[0].Namespace+"/"+createdPods[0].Name+"\x00", -1), + }, + expectedOut: []example.Pod{*createdPods[1], *createdPods[2], *createdPods[3], *createdPods[4]}, + expectRV: currentRV, + }, + { + name: "test List with continue from second pod and limit, negative resource version gives consistent read", + prefix: "/pods/", + pred: storage.SelectionPredicate{ + Label: labels.Everything(), + Field: fields.Everything(), + Limit: 2, + Continue: encodeContinueOrDie(createdPods[0].Namespace+"/"+createdPods[0].Name+"\x00", -1), + }, + expectedOut: []example.Pod{*createdPods[1], *createdPods[2]}, + expectContinue: true, + expectContinueExact: encodeContinueOrDie(createdPods[2].Namespace+"/"+createdPods[2].Name+"\x00", int64(continueRV+1)), + expectRV: currentRV, + expectedRemainingItemCount: utilpointer.Int64(2), + }, + { + name: "test List with continue from third pod, negative resource version gives consistent read", + prefix: "/pods/", + pred: storage.SelectionPredicate{ + Label: labels.Everything(), + Field: fields.Everything(), + Continue: encodeContinueOrDie(createdPods[2].Namespace+"/"+createdPods[2].Name+"\x00", -1), + }, + expectedOut: []example.Pod{*createdPods[3], *createdPods[4]}, + expectRV: currentRV, + }, + { + name: "test List with continue from empty fails", + prefix: "/pods/", + pred: storage.SelectionPredicate{ + Label: labels.Everything(), + Field: fields.Everything(), + Continue: encodeContinueOrDie("", int64(continueRV)), + }, + expectError: true, + }, + { + name: "test List with continue from first pod, empty resource version fails", + prefix: "/pods/", + pred: storage.SelectionPredicate{ + Label: labels.Everything(), + Field: fields.Everything(), + Continue: encodeContinueOrDie(createdPods[0].Namespace+"/"+createdPods[0].Name+"\x00", 0), + }, + expectError: true, + }, + { + name: "test List with negative rv fails", + prefix: "/pods/", + rv: "-1", + pred: storage.SelectionPredicate{ + Label: labels.Everything(), + Field: fields.Everything(), + }, + expectError: true, + }, } for _, tt := range tests {