k8s.io/apiserver@v0.31.1/pkg/storage/testing/store_tests.go (about)

     1  /*
     2  Copyright 2016 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package testing
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"math"
    24  	"reflect"
    25  	"sort"
    26  	"strconv"
    27  	"strings"
    28  	"sync"
    29  	"testing"
    30  
    31  	"github.com/google/go-cmp/cmp"
    32  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    33  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    34  	"k8s.io/apimachinery/pkg/fields"
    35  	"k8s.io/apimachinery/pkg/labels"
    36  	"k8s.io/apimachinery/pkg/runtime"
    37  	"k8s.io/apimachinery/pkg/watch"
    38  	"k8s.io/apiserver/pkg/apis/example"
    39  	"k8s.io/apiserver/pkg/storage"
    40  	"k8s.io/apiserver/pkg/storage/value"
    41  	utilpointer "k8s.io/utils/pointer"
    42  )
    43  
    44  type KeyValidation func(ctx context.Context, t *testing.T, key string)
    45  
    46  func RunTestCreate(ctx context.Context, t *testing.T, store storage.Interface, validation KeyValidation) {
    47  	tests := []struct {
    48  		name          string
    49  		inputObj      *example.Pod
    50  		expectedError error
    51  	}{{
    52  		name:     "successful create",
    53  		inputObj: &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}},
    54  	}, {
    55  		name:          "create with ResourceVersion set",
    56  		inputObj:      &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "test-ns", ResourceVersion: "1"}},
    57  		expectedError: storage.ErrResourceVersionSetOnCreate,
    58  	}}
    59  
    60  	for _, tt := range tests {
    61  		t.Run(tt.name, func(t *testing.T) {
    62  			out := &example.Pod{} // reset
    63  			// verify that kv pair is empty before set
    64  			key := computePodKey(tt.inputObj)
    65  			if err := store.Get(ctx, key, storage.GetOptions{}, out); !storage.IsNotFound(err) {
    66  				t.Fatalf("expecting empty result on key %s, got %v", key, err)
    67  			}
    68  
    69  			err := store.Create(ctx, key, tt.inputObj, out, 0)
    70  			if !errors.Is(err, tt.expectedError) {
    71  				t.Errorf("expecting error %v, but get: %v", tt.expectedError, err)
    72  			}
    73  			if err != nil {
    74  				return
    75  			}
    76  			// basic tests of the output
    77  			if tt.inputObj.ObjectMeta.Name != out.ObjectMeta.Name {
    78  				t.Errorf("pod name want=%s, get=%s", tt.inputObj.ObjectMeta.Name, out.ObjectMeta.Name)
    79  			}
    80  			if out.ResourceVersion == "" {
    81  				t.Errorf("output should have non-empty resource version")
    82  			}
    83  			validation(ctx, t, key)
    84  		})
    85  	}
    86  }
    87  
    88  func RunTestCreateWithTTL(ctx context.Context, t *testing.T, store storage.Interface) {
    89  	input := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}}
    90  	out := &example.Pod{}
    91  
    92  	key := computePodKey(input)
    93  	if err := store.Create(ctx, key, input, out, 1); err != nil {
    94  		t.Fatalf("Create failed: %v", err)
    95  	}
    96  
    97  	w, err := store.Watch(ctx, key, storage.ListOptions{ResourceVersion: out.ResourceVersion, Predicate: storage.Everything})
    98  	if err != nil {
    99  		t.Fatalf("Watch failed: %v", err)
   100  	}
   101  	testCheckEventType(t, w, watch.Deleted)
   102  }
   103  
   104  func RunTestCreateWithKeyExist(ctx context.Context, t *testing.T, store storage.Interface) {
   105  	obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}}
   106  	key, _ := testPropagateStore(ctx, t, store, obj)
   107  	out := &example.Pod{}
   108  
   109  	err := store.Create(ctx, key, obj, out, 0)
   110  	if err == nil || !storage.IsExist(err) {
   111  		t.Errorf("expecting key exists error, but get: %s", err)
   112  	}
   113  }
   114  
   115  func RunTestGet(ctx context.Context, t *testing.T, store storage.Interface) {
   116  	// create an object to test
   117  	key, createdObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}})
   118  	// update the object once to allow get by exact resource version to be tested
   119  	updateObj := createdObj.DeepCopy()
   120  	updateObj.Annotations = map[string]string{"test-annotation": "1"}
   121  	storedObj := &example.Pod{}
   122  	err := store.GuaranteedUpdate(ctx, key, storedObj, true, nil,
   123  		func(_ runtime.Object, _ storage.ResponseMeta) (runtime.Object, *uint64, error) {
   124  			ttl := uint64(1)
   125  			return updateObj, &ttl, nil
   126  		}, nil)
   127  	if err != nil {
   128  		t.Fatalf("Update failed: %v", err)
   129  	}
   130  	// create an additional object to increment the resource version for pods above the resource version of the foo object
   131  	secondObj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar", Namespace: "test-ns"}}
   132  	lastUpdatedObj := &example.Pod{}
   133  	if err := store.Create(ctx, computePodKey(secondObj), secondObj, lastUpdatedObj, 0); err != nil {
   134  		t.Fatalf("Set failed: %v", err)
   135  	}
   136  
   137  	currentRV, _ := strconv.Atoi(storedObj.ResourceVersion)
   138  	lastUpdatedCurrentRV, _ := strconv.Atoi(lastUpdatedObj.ResourceVersion)
   139  
   140  	// TODO(jpbetz): Add exact test cases
   141  	tests := []struct {
   142  		name                 string
   143  		key                  string
   144  		ignoreNotFound       bool
   145  		expectNotFoundErr    bool
   146  		expectRVTooLarge     bool
   147  		expectedOut          *example.Pod
   148  		expectedAlternatives []*example.Pod
   149  		rv                   string
   150  	}{{
   151  		name:              "get existing",
   152  		key:               key,
   153  		ignoreNotFound:    false,
   154  		expectNotFoundErr: false,
   155  		expectedOut:       storedObj,
   156  	}, {
   157  		// For RV=0 arbitrarily old version is allowed, including from the moment
   158  		// when the object didn't yet exist.
   159  		// As a result, we allow it by setting ignoreNotFound and allowing an empty
   160  		// object in expectedOut.
   161  		name:                 "resource version 0",
   162  		key:                  key,
   163  		ignoreNotFound:       true,
   164  		expectedAlternatives: []*example.Pod{{}, createdObj, storedObj},
   165  		rv:                   "0",
   166  	}, {
   167  		// Given that Get with set ResourceVersion is effectively always
   168  		// NotOlderThan semantic, both versions of object are allowed.
   169  		name:                 "object created resource version",
   170  		key:                  key,
   171  		expectedAlternatives: []*example.Pod{createdObj, storedObj},
   172  		rv:                   createdObj.ResourceVersion,
   173  	}, {
   174  		name:        "current object resource version, match=NotOlderThan",
   175  		key:         key,
   176  		expectedOut: storedObj,
   177  		rv:          fmt.Sprintf("%d", currentRV),
   178  	}, {
   179  		name:        "latest resource version",
   180  		key:         key,
   181  		expectedOut: storedObj,
   182  		rv:          fmt.Sprintf("%d", lastUpdatedCurrentRV),
   183  	}, {
   184  		name:             "too high resource version",
   185  		key:              key,
   186  		expectRVTooLarge: true,
   187  		rv:               strconv.FormatInt(math.MaxInt64, 10),
   188  	}, {
   189  		name:              "get non-existing",
   190  		key:               "/non-existing",
   191  		ignoreNotFound:    false,
   192  		expectNotFoundErr: true,
   193  	}, {
   194  		name:              "get non-existing, ignore not found",
   195  		key:               "/non-existing",
   196  		ignoreNotFound:    true,
   197  		expectNotFoundErr: false,
   198  		expectedOut:       &example.Pod{},
   199  	}}
   200  
   201  	for _, tt := range tests {
   202  		tt := tt
   203  		t.Run(tt.name, func(t *testing.T) {
   204  			// For some asynchronous implementations of storage interface (in particular watchcache),
   205  			// certain requests may impact result of further requests. As an example, if we first
   206  			// ensure that watchcache is synchronized up to ResourceVersion X (using Get/List requests
   207  			// with NotOlderThan semantic), the further requests (even specifying earlier resource
   208  			// version) will also return the result synchronized to at least ResourceVersion X.
   209  			// By parallelizing test cases we ensure that the order in which test cases are defined
   210  			// doesn't automatically preclude some scenarios from happening.
   211  			t.Parallel()
   212  
   213  			out := &example.Pod{}
   214  			err := store.Get(ctx, tt.key, storage.GetOptions{IgnoreNotFound: tt.ignoreNotFound, ResourceVersion: tt.rv}, out)
   215  			if tt.expectNotFoundErr {
   216  				if err == nil || !storage.IsNotFound(err) {
   217  					t.Errorf("expecting not found error, but get: %v", err)
   218  				}
   219  				return
   220  			}
   221  			if tt.expectRVTooLarge {
   222  				if err == nil || !storage.IsTooLargeResourceVersion(err) {
   223  					t.Errorf("expecting resource version too high error, but get: %v", err)
   224  				}
   225  				return
   226  			}
   227  			if err != nil {
   228  				t.Fatalf("Get failed: %v", err)
   229  			}
   230  
   231  			if tt.expectedAlternatives == nil {
   232  				expectNoDiff(t, fmt.Sprintf("%s: incorrect pod", tt.name), tt.expectedOut, out)
   233  			} else {
   234  				ExpectContains(t, fmt.Sprintf("%s: incorrect pod", tt.name), toInterfaceSlice(tt.expectedAlternatives), out)
   235  			}
   236  		})
   237  	}
   238  }
   239  
   240  func RunTestUnconditionalDelete(ctx context.Context, t *testing.T, store storage.Interface) {
   241  	key, storedObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}})
   242  
   243  	tests := []struct {
   244  		name              string
   245  		key               string
   246  		expectedObj       *example.Pod
   247  		expectNotFoundErr bool
   248  	}{{
   249  		name:              "existing key",
   250  		key:               key,
   251  		expectedObj:       storedObj,
   252  		expectNotFoundErr: false,
   253  	}, {
   254  		name:              "non-existing key",
   255  		key:               "/non-existing",
   256  		expectedObj:       nil,
   257  		expectNotFoundErr: true,
   258  	}}
   259  
   260  	for _, tt := range tests {
   261  		t.Run(tt.name, func(t *testing.T) {
   262  			out := &example.Pod{} // reset
   263  			err := store.Delete(ctx, tt.key, out, nil, storage.ValidateAllObjectFunc, nil)
   264  			if tt.expectNotFoundErr {
   265  				if err == nil || !storage.IsNotFound(err) {
   266  					t.Errorf("expecting not found error, but get: %s", err)
   267  				}
   268  				return
   269  			}
   270  			if err != nil {
   271  				t.Fatalf("Delete failed: %v", err)
   272  			}
   273  			// We expect the resource version of the returned object to be
   274  			// updated compared to the last existing object.
   275  			if storedObj.ResourceVersion == out.ResourceVersion {
   276  				t.Errorf("expecting resource version to be updated, but get: %s", out.ResourceVersion)
   277  			}
   278  			out.ResourceVersion = storedObj.ResourceVersion
   279  			expectNoDiff(t, "incorrect pod:", tt.expectedObj, out)
   280  		})
   281  	}
   282  }
   283  
   284  func RunTestConditionalDelete(ctx context.Context, t *testing.T, store storage.Interface) {
   285  	obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns", UID: "A"}}
   286  	key, storedObj := testPropagateStore(ctx, t, store, obj)
   287  
   288  	tests := []struct {
   289  		name                string
   290  		precondition        *storage.Preconditions
   291  		expectInvalidObjErr bool
   292  	}{{
   293  		name:                "UID match",
   294  		precondition:        storage.NewUIDPreconditions("A"),
   295  		expectInvalidObjErr: false,
   296  	}, {
   297  		name:                "UID mismatch",
   298  		precondition:        storage.NewUIDPreconditions("B"),
   299  		expectInvalidObjErr: true,
   300  	}}
   301  
   302  	for _, tt := range tests {
   303  		t.Run(tt.name, func(t *testing.T) {
   304  			out := &example.Pod{}
   305  			err := store.Delete(ctx, key, out, tt.precondition, storage.ValidateAllObjectFunc, nil)
   306  			if tt.expectInvalidObjErr {
   307  				if err == nil || !storage.IsInvalidObj(err) {
   308  					t.Errorf("expecting invalid UID error, but get: %s", err)
   309  				}
   310  				return
   311  			}
   312  			if err != nil {
   313  				t.Fatalf("Delete failed: %v", err)
   314  			}
   315  			// We expect the resource version of the returned object to be
   316  			// updated compared to the last existing object.
   317  			if storedObj.ResourceVersion == out.ResourceVersion {
   318  				t.Errorf("expecting resource version to be updated, but get: %s", out.ResourceVersion)
   319  			}
   320  			out.ResourceVersion = storedObj.ResourceVersion
   321  			expectNoDiff(t, "incorrect pod:", storedObj, out)
   322  			obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns", UID: "A"}}
   323  			key, storedObj = testPropagateStore(ctx, t, store, obj)
   324  		})
   325  	}
   326  }
   327  
   328  // The following set of Delete tests are testing the logic of adding `suggestion`
   329  // as a parameter with probably value of the current state.
   330  // Introducing it for GuaranteedUpdate cause a number of issues, so we're addressing
   331  // all of those upfront by adding appropriate tests:
   332  // - https://github.com/kubernetes/kubernetes/pull/35415
   333  //   [DONE] Lack of tests originally - added TestDeleteWithSuggestion.
   334  // - https://github.com/kubernetes/kubernetes/pull/40664
   335  //   [DONE] Irrelevant for delete, as Delete doesn't write data (nor compare it).
   336  // - https://github.com/kubernetes/kubernetes/pull/47703
   337  //   [DONE] Irrelevant for delete, because Delete doesn't persist data.
   338  // - https://github.com/kubernetes/kubernetes/pull/48394/
   339  //   [DONE] Irrelevant for delete, because Delete doesn't compare data.
   340  // - https://github.com/kubernetes/kubernetes/pull/43152
   341  //   [DONE] Added TestDeleteWithSuggestionAndConflict
   342  // - https://github.com/kubernetes/kubernetes/pull/54780
   343  //   [DONE] Irrelevant for delete, because Delete doesn't compare data.
   344  // - https://github.com/kubernetes/kubernetes/pull/58375
   345  //   [DONE] Irrelevant for delete, because Delete doesn't compare data.
   346  // - https://github.com/kubernetes/kubernetes/pull/77619
   347  //   [DONE] Added TestValidateDeletionWithSuggestion for corresponding delete checks.
   348  // - https://github.com/kubernetes/kubernetes/pull/78713
   349  //   [DONE] Bug was in getState function which is shared with the new code.
   350  // - https://github.com/kubernetes/kubernetes/pull/78713
   351  //   [DONE] Added TestPreconditionalDeleteWithSuggestion
   352  
   353  func RunTestDeleteWithSuggestion(ctx context.Context, t *testing.T, store storage.Interface) {
   354  	key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "test-ns"}})
   355  
   356  	out := &example.Pod{}
   357  	if err := store.Delete(ctx, key, out, nil, storage.ValidateAllObjectFunc, originalPod); err != nil {
   358  		t.Errorf("Unexpected failure during deletion: %v", err)
   359  	}
   360  
   361  	if err := store.Get(ctx, key, storage.GetOptions{}, &example.Pod{}); !storage.IsNotFound(err) {
   362  		t.Errorf("Unexpected error on reading object: %v", err)
   363  	}
   364  }
   365  
   366  func RunTestDeleteWithSuggestionAndConflict(ctx context.Context, t *testing.T, store storage.Interface) {
   367  	key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "test-ns"}})
   368  
   369  	// First update, so originalPod is outdated.
   370  	updatedPod := &example.Pod{}
   371  	if err := store.GuaranteedUpdate(ctx, key, updatedPod, false, nil,
   372  		storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) {
   373  			pod := obj.(*example.Pod)
   374  			pod.ObjectMeta.Labels = map[string]string{"foo": "bar"}
   375  			return pod, nil
   376  		}), nil); err != nil {
   377  		t.Errorf("Unexpected failure during updated: %v", err)
   378  	}
   379  
   380  	out := &example.Pod{}
   381  	if err := store.Delete(ctx, key, out, nil, storage.ValidateAllObjectFunc, originalPod); err != nil {
   382  		t.Errorf("Unexpected failure during deletion: %v", err)
   383  	}
   384  
   385  	if err := store.Get(ctx, key, storage.GetOptions{}, &example.Pod{}); !storage.IsNotFound(err) {
   386  		t.Errorf("Unexpected error on reading object: %v", err)
   387  	}
   388  	updatedPod.ObjectMeta.ResourceVersion = out.ObjectMeta.ResourceVersion
   389  	expectNoDiff(t, "incorrect pod:", updatedPod, out)
   390  }
   391  
   392  // RunTestDeleteWithConflict tests the case when another conflicting update happened before the delete completed.
   393  func RunTestDeleteWithConflict(ctx context.Context, t *testing.T, store storage.Interface) {
   394  	key, _ := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "test-ns"}})
   395  
   396  	// First update, so originalPod is outdated.
   397  	updatedPod := &example.Pod{}
   398  	validateCount := 0
   399  	updateCount := 0
   400  	// Simulate a conflicting update in the middle of delete.
   401  	validateAllWithUpdate := func(_ context.Context, _ runtime.Object) error {
   402  		validateCount++
   403  		if validateCount > 1 {
   404  			return nil
   405  		}
   406  		if err := store.GuaranteedUpdate(ctx, key, updatedPod, false, nil,
   407  			storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) {
   408  				pod := obj.(*example.Pod)
   409  				pod.ObjectMeta.Labels = map[string]string{"foo": "bar"}
   410  				return pod, nil
   411  			}), nil); err != nil {
   412  			t.Errorf("Unexpected failure during updated: %v", err)
   413  		}
   414  		updateCount++
   415  		return nil
   416  	}
   417  
   418  	out := &example.Pod{}
   419  	if err := store.Delete(ctx, key, out, nil, validateAllWithUpdate, nil); err != nil {
   420  		t.Errorf("Unexpected failure during deletion: %v", err)
   421  	}
   422  
   423  	if validateCount != 2 {
   424  		t.Errorf("Expect validateCount = %d, but got %d", 2, validateCount)
   425  	}
   426  	if updateCount != 1 {
   427  		t.Errorf("Expect updateCount = %d, but got %d", 1, updateCount)
   428  	}
   429  
   430  	if err := store.Get(ctx, key, storage.GetOptions{}, &example.Pod{}); !storage.IsNotFound(err) {
   431  		t.Errorf("Unexpected error on reading object: %v", err)
   432  	}
   433  	updatedPod.ObjectMeta.ResourceVersion = out.ObjectMeta.ResourceVersion
   434  	expectNoDiff(t, "incorrect pod:", updatedPod, out)
   435  }
   436  
   437  func RunTestDeleteWithSuggestionOfDeletedObject(ctx context.Context, t *testing.T, store storage.Interface) {
   438  	key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "test-ns"}})
   439  
   440  	// First delete, so originalPod is outdated.
   441  	deletedPod := &example.Pod{}
   442  	if err := store.Delete(ctx, key, deletedPod, nil, storage.ValidateAllObjectFunc, originalPod); err != nil {
   443  		t.Errorf("Unexpected failure during deletion: %v", err)
   444  	}
   445  
   446  	// Now try deleting with stale object.
   447  	out := &example.Pod{}
   448  	if err := store.Delete(ctx, key, out, nil, storage.ValidateAllObjectFunc, originalPod); !storage.IsNotFound(err) {
   449  		t.Errorf("Unexpected error during deletion: %v, expected not-found", err)
   450  	}
   451  }
   452  
   453  func RunTestValidateDeletionWithSuggestion(ctx context.Context, t *testing.T, store storage.Interface) {
   454  	key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "test-ns"}})
   455  
   456  	// Check that validaing fresh object fails is called once and fails.
   457  	validationCalls := 0
   458  	validationError := fmt.Errorf("validation error")
   459  	validateNothing := func(_ context.Context, _ runtime.Object) error {
   460  		validationCalls++
   461  		return validationError
   462  	}
   463  	out := &example.Pod{}
   464  	if err := store.Delete(ctx, key, out, nil, validateNothing, originalPod); err != validationError {
   465  		t.Errorf("Unexpected failure during deletion: %v", err)
   466  	}
   467  	if validationCalls != 1 {
   468  		t.Errorf("validate function should have been called once, called %d", validationCalls)
   469  	}
   470  
   471  	// First update, so originalPod is outdated.
   472  	updatedPod := &example.Pod{}
   473  	if err := store.GuaranteedUpdate(ctx, key, updatedPod, false, nil,
   474  		storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) {
   475  			pod := obj.(*example.Pod)
   476  			pod.ObjectMeta.Labels = map[string]string{"foo": "bar"}
   477  			return pod, nil
   478  		}), nil); err != nil {
   479  		t.Errorf("Unexpected failure during updated: %v", err)
   480  	}
   481  
   482  	calls := 0
   483  	validateFresh := func(_ context.Context, obj runtime.Object) error {
   484  		calls++
   485  		pod := obj.(*example.Pod)
   486  		if pod.ObjectMeta.Labels == nil || pod.ObjectMeta.Labels["foo"] != "bar" {
   487  			return fmt.Errorf("stale object")
   488  		}
   489  		return nil
   490  	}
   491  
   492  	if err := store.Delete(ctx, key, out, nil, validateFresh, originalPod); err != nil {
   493  		t.Errorf("Unexpected failure during deletion: %v", err)
   494  	}
   495  
   496  	// Implementations of the storage interface are allowed to ignore the suggestion,
   497  	// in which case just one validation call is possible.
   498  	if calls > 2 {
   499  		t.Errorf("validate function should have been called at most twice, called %d", calls)
   500  	}
   501  
   502  	if err := store.Get(ctx, key, storage.GetOptions{}, &example.Pod{}); !storage.IsNotFound(err) {
   503  		t.Errorf("Unexpected error on reading object: %v", err)
   504  	}
   505  }
   506  
   507  // RunTestValidateDeletionWithOnlySuggestionValid tests the case of delete with validateDeletion function,
   508  // when the suggested cachedExistingObject passes the validate function while the current version does not pass the validate function.
   509  func RunTestValidateDeletionWithOnlySuggestionValid(ctx context.Context, t *testing.T, store storage.Interface) {
   510  	key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "test-ns", Labels: map[string]string{"foo": "bar"}}})
   511  
   512  	// Check that validaing fresh object fails is called once and fails.
   513  	validationCalls := 0
   514  	validationError := fmt.Errorf("validation error")
   515  	validateNothing := func(_ context.Context, _ runtime.Object) error {
   516  		validationCalls++
   517  		return validationError
   518  	}
   519  	out := &example.Pod{}
   520  	if err := store.Delete(ctx, key, out, nil, validateNothing, originalPod); err != validationError {
   521  		t.Errorf("Unexpected failure during deletion: %v", err)
   522  	}
   523  	if validationCalls != 1 {
   524  		t.Errorf("validate function should have been called once, called %d", validationCalls)
   525  	}
   526  
   527  	// First update, so originalPod is outdated.
   528  	updatedPod := &example.Pod{}
   529  	if err := store.GuaranteedUpdate(ctx, key, updatedPod, false, nil,
   530  		storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) {
   531  			pod := obj.(*example.Pod)
   532  			pod.ObjectMeta.Labels = map[string]string{"foo": "barbar"}
   533  			return pod, nil
   534  		}), nil); err != nil {
   535  		t.Errorf("Unexpected failure during updated: %v", err)
   536  	}
   537  
   538  	calls := 0
   539  	validateFresh := func(_ context.Context, obj runtime.Object) error {
   540  		calls++
   541  		pod := obj.(*example.Pod)
   542  		if pod.ObjectMeta.Labels == nil || pod.ObjectMeta.Labels["foo"] != "bar" {
   543  			return fmt.Errorf("stale object")
   544  		}
   545  		return nil
   546  	}
   547  
   548  	err := store.Delete(ctx, key, out, nil, validateFresh, originalPod)
   549  	if err == nil || err.Error() != "stale object" {
   550  		t.Errorf("expecting stale object error, but get: %s", err)
   551  	}
   552  
   553  	// Implementations of the storage interface are allowed to ignore the suggestion,
   554  	// in which case just one validation call is possible.
   555  	if calls > 2 {
   556  		t.Errorf("validate function should have been called at most twice, called %d", calls)
   557  	}
   558  
   559  	if err = store.Get(ctx, key, storage.GetOptions{}, out); err != nil {
   560  		t.Errorf("Unexpected error on reading object: %v", err)
   561  	}
   562  	expectNoDiff(t, "incorrect pod:", updatedPod, out)
   563  }
   564  
   565  func RunTestPreconditionalDeleteWithSuggestion(ctx context.Context, t *testing.T, store storage.Interface) {
   566  	key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "test-ns"}})
   567  
   568  	// First update, so originalPod is outdated.
   569  	updatedPod := &example.Pod{}
   570  	if err := store.GuaranteedUpdate(ctx, key, updatedPod, false, nil,
   571  		storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) {
   572  			pod := obj.(*example.Pod)
   573  			pod.ObjectMeta.UID = "myUID"
   574  			return pod, nil
   575  		}), nil); err != nil {
   576  		t.Errorf("Unexpected failure during updated: %v", err)
   577  	}
   578  
   579  	prec := storage.NewUIDPreconditions("myUID")
   580  
   581  	out := &example.Pod{}
   582  	if err := store.Delete(ctx, key, out, prec, storage.ValidateAllObjectFunc, originalPod); err != nil {
   583  		t.Errorf("Unexpected failure during deletion: %v", err)
   584  	}
   585  
   586  	if err := store.Get(ctx, key, storage.GetOptions{}, &example.Pod{}); !storage.IsNotFound(err) {
   587  		t.Errorf("Unexpected error on reading object: %v", err)
   588  	}
   589  }
   590  
   591  // RunTestPreconditionalDeleteWithOnlySuggestionPass tests the case of delete with preconditions,
   592  // when the suggested cachedExistingObject passes the preconditions while the current version does not pass the preconditions.
   593  func RunTestPreconditionalDeleteWithOnlySuggestionPass(ctx context.Context, t *testing.T, store storage.Interface) {
   594  	key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "test-ns", UID: "myUID"}})
   595  
   596  	// First update, so originalPod is outdated.
   597  	updatedPod := &example.Pod{}
   598  	if err := store.GuaranteedUpdate(ctx, key, updatedPod, false, nil,
   599  		storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) {
   600  			pod := obj.(*example.Pod)
   601  			pod.ObjectMeta.UID = "otherUID"
   602  			return pod, nil
   603  		}), nil); err != nil {
   604  		t.Errorf("Unexpected failure during updated: %v", err)
   605  	}
   606  
   607  	prec := storage.NewUIDPreconditions("myUID")
   608  	// Although originalPod passes the precondition, its delete would fail due to conflict.
   609  	// The 2nd try with updatedPod would fail the precondition.
   610  	out := &example.Pod{}
   611  	err := store.Delete(ctx, key, out, prec, storage.ValidateAllObjectFunc, originalPod)
   612  	if err == nil || !storage.IsInvalidObj(err) {
   613  		t.Errorf("expecting invalid UID error, but get: %s", err)
   614  	}
   615  
   616  	if err = store.Get(ctx, key, storage.GetOptions{}, out); err != nil {
   617  		t.Errorf("Unexpected error on reading object: %v", err)
   618  	}
   619  	expectNoDiff(t, "incorrect pod:", updatedPod, out)
   620  }
   621  
   622  func RunTestList(ctx context.Context, t *testing.T, store storage.Interface, compaction Compaction, ignoreWatchCacheTests bool) {
   623  	initialRV, preset, err := seedMultiLevelData(ctx, store)
   624  	if err != nil {
   625  		t.Fatal(err)
   626  	}
   627  
   628  	list := &example.PodList{}
   629  	storageOpts := storage.ListOptions{
   630  		// Ensure we're listing from "now".
   631  		ResourceVersion: "",
   632  		Predicate:       storage.Everything,
   633  		Recursive:       true,
   634  	}
   635  	if err := store.GetList(ctx, "/second", storageOpts, list); err != nil {
   636  		t.Errorf("Unexpected error: %v", err)
   637  	}
   638  	continueRV, _ := strconv.Atoi(list.ResourceVersion)
   639  	secondContinuation, err := storage.EncodeContinue("/second/foo", "/second/", int64(continueRV))
   640  	if err != nil {
   641  		t.Fatal(err)
   642  	}
   643  
   644  	getAttrs := func(obj runtime.Object) (labels.Set, fields.Set, error) {
   645  		pod := obj.(*example.Pod)
   646  		return nil, fields.Set{"metadata.name": pod.Name, "spec.nodeName": pod.Spec.NodeName}, nil
   647  	}
   648  	// Use compact to increase etcd global revision without changes to any resources.
   649  	// The increase in resources version comes from Kubernetes compaction updating hidden key.
   650  	// Used to test consistent List to confirm it returns latest etcd revision.
   651  	compaction(ctx, t, initialRV)
   652  	currentRV := fmt.Sprintf("%d", continueRV+1)
   653  
   654  	tests := []struct {
   655  		name                       string
   656  		rv                         string
   657  		rvMatch                    metav1.ResourceVersionMatch
   658  		prefix                     string
   659  		pred                       storage.SelectionPredicate
   660  		ignoreForWatchCache        bool
   661  		expectedOut                []example.Pod
   662  		expectedAlternatives       [][]example.Pod
   663  		expectContinue             bool
   664  		expectedRemainingItemCount *int64
   665  		expectError                bool
   666  		expectRVTooLarge           bool
   667  		expectRV                   string
   668  		expectRVFunc               func(string) error
   669  	}{
   670  		{
   671  			name:        "rejects invalid resource version",
   672  			prefix:      "/pods",
   673  			pred:        storage.Everything,
   674  			rv:          "abc",
   675  			expectError: true,
   676  		},
   677  		{
   678  			name:   "rejects resource version and continue token",
   679  			prefix: "/pods",
   680  			pred: storage.SelectionPredicate{
   681  				Label:    labels.Everything(),
   682  				Field:    fields.Everything(),
   683  				Limit:    1,
   684  				Continue: secondContinuation,
   685  			},
   686  			rv:          "1",
   687  			expectError: true,
   688  		},
   689  		{
   690  			name:             "rejects resource version set too high",
   691  			prefix:           "/pods",
   692  			rv:               strconv.FormatInt(math.MaxInt64, 10),
   693  			expectRVTooLarge: true,
   694  		},
   695  		{
   696  			name:        "test List on existing key",
   697  			prefix:      "/pods/first/",
   698  			pred:        storage.Everything,
   699  			expectedOut: []example.Pod{*preset[0]},
   700  		},
   701  		{
   702  			name:                 "test List on existing key with resource version set to 0",
   703  			prefix:               "/pods/first/",
   704  			pred:                 storage.Everything,
   705  			expectedAlternatives: [][]example.Pod{{}, {*preset[0]}},
   706  			rv:                   "0",
   707  		},
   708  		{
   709  			name:        "test List on existing key with resource version set before first write, match=Exact",
   710  			prefix:      "/pods/first/",
   711  			pred:        storage.Everything,
   712  			expectedOut: []example.Pod{},
   713  			rv:          initialRV,
   714  			rvMatch:     metav1.ResourceVersionMatchExact,
   715  			expectRV:    initialRV,
   716  		},
   717  		{
   718  			name:                 "test List on existing key with resource version set to 0, match=NotOlderThan",
   719  			prefix:               "/pods/first/",
   720  			pred:                 storage.Everything,
   721  			expectedAlternatives: [][]example.Pod{{}, {*preset[0]}},
   722  			rv:                   "0",
   723  			rvMatch:              metav1.ResourceVersionMatchNotOlderThan,
   724  		},
   725  		{
   726  			name:        "test List on existing key with resource version set to 0, match=Invalid",
   727  			prefix:      "/pods/first/",
   728  			pred:        storage.Everything,
   729  			rv:          "0",
   730  			rvMatch:     "Invalid",
   731  			expectError: true,
   732  		},
   733  		{
   734  			name:                 "test List on existing key with resource version set before first write, match=NotOlderThan",
   735  			prefix:               "/pods/first/",
   736  			pred:                 storage.Everything,
   737  			expectedAlternatives: [][]example.Pod{{}, {*preset[0]}},
   738  			rv:                   initialRV,
   739  			rvMatch:              metav1.ResourceVersionMatchNotOlderThan,
   740  		},
   741  		{
   742  			name:        "test List on existing key with resource version set before first write, match=Invalid",
   743  			prefix:      "/pods/first/",
   744  			pred:        storage.Everything,
   745  			rv:          initialRV,
   746  			rvMatch:     "Invalid",
   747  			expectError: true,
   748  		},
   749  		{
   750  			name:        "test List on existing key with resource version set to current resource version",
   751  			prefix:      "/pods/first/",
   752  			pred:        storage.Everything,
   753  			expectedOut: []example.Pod{*preset[0]},
   754  			rv:          list.ResourceVersion,
   755  		},
   756  		{
   757  			name:        "test List on existing key with resource version set to current resource version, match=Exact",
   758  			prefix:      "/pods/first/",
   759  			pred:        storage.Everything,
   760  			expectedOut: []example.Pod{*preset[0]},
   761  			rv:          list.ResourceVersion,
   762  			rvMatch:     metav1.ResourceVersionMatchExact,
   763  			expectRV:    list.ResourceVersion,
   764  		},
   765  		{
   766  			name:        "test List on existing key with resource version set to current resource version, match=NotOlderThan",
   767  			prefix:      "/pods/first/",
   768  			pred:        storage.Everything,
   769  			expectedOut: []example.Pod{*preset[0]},
   770  			rv:          list.ResourceVersion,
   771  			rvMatch:     metav1.ResourceVersionMatchNotOlderThan,
   772  		},
   773  		{
   774  			name:        "test List on non-existing key",
   775  			prefix:      "/pods/non-existing/",
   776  			pred:        storage.Everything,
   777  			expectedOut: []example.Pod{},
   778  		},
   779  		{
   780  			name:   "test List with pod name matching",
   781  			prefix: "/pods/first/",
   782  			pred: storage.SelectionPredicate{
   783  				Label: labels.Everything(),
   784  				Field: fields.ParseSelectorOrDie("metadata.name!=bar"),
   785  			},
   786  			expectedOut: []example.Pod{},
   787  		},
   788  		{
   789  			name:   "test List with pod name matching with resource version set to current resource version, match=NotOlderThan",
   790  			prefix: "/pods/first/",
   791  			pred: storage.SelectionPredicate{
   792  				Label: labels.Everything(),
   793  				Field: fields.ParseSelectorOrDie("metadata.name!=bar"),
   794  			},
   795  			expectedOut: []example.Pod{},
   796  			rv:          list.ResourceVersion,
   797  			rvMatch:     metav1.ResourceVersionMatchNotOlderThan,
   798  		},
   799  		{
   800  			name:   "test List with limit",
   801  			prefix: "/pods/second/",
   802  			pred: storage.SelectionPredicate{
   803  				Label: labels.Everything(),
   804  				Field: fields.Everything(),
   805  				Limit: 1,
   806  			},
   807  			expectedOut:                []example.Pod{*preset[1]},
   808  			expectContinue:             true,
   809  			expectedRemainingItemCount: utilpointer.Int64(1),
   810  		},
   811  		{
   812  			name:   "test List with limit at current resource version",
   813  			prefix: "/pods/second/",
   814  			pred: storage.SelectionPredicate{
   815  				Label: labels.Everything(),
   816  				Field: fields.Everything(),
   817  				Limit: 1,
   818  			},
   819  			expectedOut:                []example.Pod{*preset[1]},
   820  			expectContinue:             true,
   821  			expectedRemainingItemCount: utilpointer.Int64(1),
   822  			rv:                         list.ResourceVersion,
   823  			expectRV:                   list.ResourceVersion,
   824  		},
   825  		{
   826  			name:   "test List with limit at current resource version and match=Exact",
   827  			prefix: "/pods/second/",
   828  			pred: storage.SelectionPredicate{
   829  				Label: labels.Everything(),
   830  				Field: fields.Everything(),
   831  				Limit: 1,
   832  			},
   833  			expectedOut:                []example.Pod{*preset[1]},
   834  			expectContinue:             true,
   835  			expectedRemainingItemCount: utilpointer.Int64(1),
   836  			rv:                         list.ResourceVersion,
   837  			rvMatch:                    metav1.ResourceVersionMatchExact,
   838  			expectRV:                   list.ResourceVersion,
   839  		},
   840  		{
   841  			name:   "test List with limit at current resource version and match=NotOlderThan",
   842  			prefix: "/pods/second/",
   843  			pred: storage.SelectionPredicate{
   844  				Label: labels.Everything(),
   845  				Field: fields.Everything(),
   846  				Limit: 1,
   847  			},
   848  			expectedOut:                []example.Pod{*preset[1]},
   849  			expectContinue:             true,
   850  			expectedRemainingItemCount: utilpointer.Int64(1),
   851  			rv:                         list.ResourceVersion,
   852  			rvMatch:                    metav1.ResourceVersionMatchNotOlderThan,
   853  			expectRVFunc:               resourceVersionNotOlderThan(list.ResourceVersion),
   854  		},
   855  		{
   856  			name:   "test List with limit at resource version 0",
   857  			prefix: "/pods/second/",
   858  			pred: storage.SelectionPredicate{
   859  				Label: labels.Everything(),
   860  				Field: fields.Everything(),
   861  				Limit: 1,
   862  			},
   863  			// TODO(#108003): As of now, watchcache is deliberately ignoring
   864  			// limit if RV=0 is specified, returning whole list of objects.
   865  			// While this should eventually get fixed, for now we're explicitly
   866  			// ignoring this testcase for watchcache.
   867  			ignoreForWatchCache:        true,
   868  			expectedOut:                []example.Pod{*preset[1]},
   869  			expectContinue:             true,
   870  			expectedRemainingItemCount: utilpointer.Int64(1),
   871  			rv:                         "0",
   872  			expectRVFunc:               resourceVersionNotOlderThan(list.ResourceVersion),
   873  		},
   874  		{
   875  			name:   "test List with limit at resource version 0 match=NotOlderThan",
   876  			prefix: "/pods/second/",
   877  			pred: storage.SelectionPredicate{
   878  				Label: labels.Everything(),
   879  				Field: fields.Everything(),
   880  				Limit: 1,
   881  			},
   882  			// TODO(#108003): As of now, watchcache is deliberately ignoring
   883  			// limit if RV=0 is specified, returning whole list of objects.
   884  			// While this should eventually get fixed, for now we're explicitly
   885  			// ignoring this testcase for watchcache.
   886  			ignoreForWatchCache:        true,
   887  			expectedOut:                []example.Pod{*preset[1]},
   888  			expectContinue:             true,
   889  			expectedRemainingItemCount: utilpointer.Int64(1),
   890  			rv:                         "0",
   891  			rvMatch:                    metav1.ResourceVersionMatchNotOlderThan,
   892  			expectRVFunc:               resourceVersionNotOlderThan(list.ResourceVersion),
   893  		},
   894  		{
   895  			name:   "test List with limit at resource version before first write and match=Exact",
   896  			prefix: "/pods/second/",
   897  			pred: storage.SelectionPredicate{
   898  				Label: labels.Everything(),
   899  				Field: fields.Everything(),
   900  				Limit: 1,
   901  			},
   902  			expectedOut:    []example.Pod{},
   903  			expectContinue: false,
   904  			rv:             initialRV,
   905  			rvMatch:        metav1.ResourceVersionMatchExact,
   906  			expectRV:       initialRV,
   907  		},
   908  		{
   909  			name:   "test List with pregenerated continue token",
   910  			prefix: "/pods/second/",
   911  			pred: storage.SelectionPredicate{
   912  				Label:    labels.Everything(),
   913  				Field:    fields.Everything(),
   914  				Limit:    1,
   915  				Continue: secondContinuation,
   916  			},
   917  			expectedOut: []example.Pod{*preset[2]},
   918  		},
   919  		{
   920  			name:   "ignores resource version 0 for List with pregenerated continue token",
   921  			prefix: "/pods/second/",
   922  			pred: storage.SelectionPredicate{
   923  				Label:    labels.Everything(),
   924  				Field:    fields.Everything(),
   925  				Limit:    1,
   926  				Continue: secondContinuation,
   927  			},
   928  			rv:          "0",
   929  			expectedOut: []example.Pod{*preset[2]},
   930  		},
   931  		{
   932  			name:        "test List with multiple levels of directories and expect flattened result",
   933  			prefix:      "/pods/second/",
   934  			pred:        storage.Everything,
   935  			expectedOut: []example.Pod{*preset[1], *preset[2]},
   936  		},
   937  		{
   938  			name:        "test List with multiple levels of directories and expect flattened result with current resource version and match=NotOlderThan",
   939  			prefix:      "/pods/second/",
   940  			pred:        storage.Everything,
   941  			rv:          list.ResourceVersion,
   942  			rvMatch:     metav1.ResourceVersionMatchNotOlderThan,
   943  			expectedOut: []example.Pod{*preset[1], *preset[2]},
   944  		},
   945  		{
   946  			name:   "test List with filter returning only one item, ensure only a single page returned",
   947  			prefix: "/pods",
   948  			pred: storage.SelectionPredicate{
   949  				Field: fields.OneTermEqualSelector("metadata.name", "barfoo"),
   950  				Label: labels.Everything(),
   951  				Limit: 1,
   952  			},
   953  			expectedOut:    []example.Pod{*preset[3]},
   954  			expectContinue: true,
   955  		},
   956  		{
   957  			name:   "test List with filter returning only one item, ensure only a single page returned with current resource version and match=NotOlderThan",
   958  			prefix: "/pods",
   959  			pred: storage.SelectionPredicate{
   960  				Field: fields.OneTermEqualSelector("metadata.name", "barfoo"),
   961  				Label: labels.Everything(),
   962  				Limit: 1,
   963  			},
   964  			rv:             list.ResourceVersion,
   965  			rvMatch:        metav1.ResourceVersionMatchNotOlderThan,
   966  			expectedOut:    []example.Pod{*preset[3]},
   967  			expectContinue: true,
   968  		},
   969  		{
   970  			name:   "test List with filter returning only one item, covers the entire list",
   971  			prefix: "/pods",
   972  			pred: storage.SelectionPredicate{
   973  				Field: fields.OneTermEqualSelector("metadata.name", "barfoo"),
   974  				Label: labels.Everything(),
   975  				Limit: 2,
   976  			},
   977  			expectedOut:    []example.Pod{*preset[3]},
   978  			expectContinue: false,
   979  		},
   980  		{
   981  			name:   "test List with filter returning only one item, covers the entire list with current resource version and match=NotOlderThan",
   982  			prefix: "/pods",
   983  			pred: storage.SelectionPredicate{
   984  				Field: fields.OneTermEqualSelector("metadata.name", "barfoo"),
   985  				Label: labels.Everything(),
   986  				Limit: 2,
   987  			},
   988  			rv:             list.ResourceVersion,
   989  			rvMatch:        metav1.ResourceVersionMatchNotOlderThan,
   990  			expectedOut:    []example.Pod{*preset[3]},
   991  			expectContinue: false,
   992  		},
   993  		{
   994  			name:   "test List with filter returning only one item, covers the entire list, with resource version 0",
   995  			prefix: "/pods",
   996  			pred: storage.SelectionPredicate{
   997  				Field: fields.OneTermEqualSelector("metadata.name", "barfoo"),
   998  				Label: labels.Everything(),
   999  				Limit: 2,
  1000  			},
  1001  			rv:                   "0",
  1002  			expectedAlternatives: [][]example.Pod{{}, {*preset[3]}},
  1003  			expectContinue:       false,
  1004  		},
  1005  		{
  1006  			name:   "test List with filter returning two items, more pages possible",
  1007  			prefix: "/pods",
  1008  			pred: storage.SelectionPredicate{
  1009  				Field: fields.OneTermEqualSelector("metadata.name", "bar"),
  1010  				Label: labels.Everything(),
  1011  				Limit: 2,
  1012  			},
  1013  			expectContinue: true,
  1014  			expectedOut:    []example.Pod{*preset[0], *preset[1]},
  1015  		},
  1016  		{
  1017  			name:   "test List with filter returning two items, more pages possible with current resource version and match=NotOlderThan",
  1018  			prefix: "/pods",
  1019  			pred: storage.SelectionPredicate{
  1020  				Field: fields.OneTermEqualSelector("metadata.name", "bar"),
  1021  				Label: labels.Everything(),
  1022  				Limit: 2,
  1023  			},
  1024  			rv:             list.ResourceVersion,
  1025  			rvMatch:        metav1.ResourceVersionMatchNotOlderThan,
  1026  			expectContinue: true,
  1027  			expectedOut:    []example.Pod{*preset[0], *preset[1]},
  1028  		},
  1029  		{
  1030  			name:   "filter returns two items split across multiple pages",
  1031  			prefix: "/pods",
  1032  			pred: storage.SelectionPredicate{
  1033  				Field: fields.OneTermEqualSelector("metadata.name", "foo"),
  1034  				Label: labels.Everything(),
  1035  				Limit: 2,
  1036  			},
  1037  			expectedOut: []example.Pod{*preset[2], *preset[4]},
  1038  		},
  1039  		{
  1040  			name:   "filter returns two items split across multiple pages with current resource version and match=NotOlderThan",
  1041  			prefix: "/pods",
  1042  			pred: storage.SelectionPredicate{
  1043  				Field: fields.OneTermEqualSelector("metadata.name", "foo"),
  1044  				Label: labels.Everything(),
  1045  				Limit: 2,
  1046  			},
  1047  			rv:          list.ResourceVersion,
  1048  			rvMatch:     metav1.ResourceVersionMatchNotOlderThan,
  1049  			expectedOut: []example.Pod{*preset[2], *preset[4]},
  1050  		},
  1051  		{
  1052  			name:   "filter returns one item for last page, ends on last item, not full",
  1053  			prefix: "/pods",
  1054  			pred: storage.SelectionPredicate{
  1055  				Field:    fields.OneTermEqualSelector("metadata.name", "foo"),
  1056  				Label:    labels.Everything(),
  1057  				Limit:    2,
  1058  				Continue: encodeContinueOrDie("third/barfoo", int64(continueRV)),
  1059  			},
  1060  			expectedOut: []example.Pod{*preset[4]},
  1061  		},
  1062  		{
  1063  			name:   "filter returns one item for last page, starts on last item, full",
  1064  			prefix: "/pods",
  1065  			pred: storage.SelectionPredicate{
  1066  				Field:    fields.OneTermEqualSelector("metadata.name", "foo"),
  1067  				Label:    labels.Everything(),
  1068  				Limit:    1,
  1069  				Continue: encodeContinueOrDie("third/barfoo", int64(continueRV)),
  1070  			},
  1071  			expectedOut: []example.Pod{*preset[4]},
  1072  		},
  1073  		{
  1074  			name:   "filter returns one item for last page, starts on last item, partial page",
  1075  			prefix: "/pods",
  1076  			pred: storage.SelectionPredicate{
  1077  				Field:    fields.OneTermEqualSelector("metadata.name", "foo"),
  1078  				Label:    labels.Everything(),
  1079  				Limit:    2,
  1080  				Continue: encodeContinueOrDie("third/barfoo", int64(continueRV)),
  1081  			},
  1082  			expectedOut: []example.Pod{*preset[4]},
  1083  		},
  1084  		{
  1085  			name:   "filter returns two items, page size equal to total list size",
  1086  			prefix: "/pods",
  1087  			pred: storage.SelectionPredicate{
  1088  				Field: fields.OneTermEqualSelector("metadata.name", "foo"),
  1089  				Label: labels.Everything(),
  1090  				Limit: 5,
  1091  			},
  1092  			expectedOut: []example.Pod{*preset[2], *preset[4]},
  1093  		},
  1094  		{
  1095  			name:   "filter returns two items, page size equal to total list size with current resource version and match=NotOlderThan",
  1096  			prefix: "/pods",
  1097  			pred: storage.SelectionPredicate{
  1098  				Field: fields.OneTermEqualSelector("metadata.name", "foo"),
  1099  				Label: labels.Everything(),
  1100  				Limit: 5,
  1101  			},
  1102  			rv:          list.ResourceVersion,
  1103  			rvMatch:     metav1.ResourceVersionMatchNotOlderThan,
  1104  			expectedOut: []example.Pod{*preset[2], *preset[4]},
  1105  		},
  1106  		{
  1107  			name:   "filter returns one item, page size equal to total list size",
  1108  			prefix: "/pods",
  1109  			pred: storage.SelectionPredicate{
  1110  				Field: fields.OneTermEqualSelector("metadata.name", "barfoo"),
  1111  				Label: labels.Everything(),
  1112  				Limit: 5,
  1113  			},
  1114  			expectedOut: []example.Pod{*preset[3]},
  1115  		},
  1116  		{
  1117  			name:   "filter returns one item, page size equal to total list size with current resource version and match=NotOlderThan",
  1118  			prefix: "/pods",
  1119  			pred: storage.SelectionPredicate{
  1120  				Field: fields.OneTermEqualSelector("metadata.name", "barfoo"),
  1121  				Label: labels.Everything(),
  1122  				Limit: 5,
  1123  			},
  1124  			rv:          list.ResourceVersion,
  1125  			rvMatch:     metav1.ResourceVersionMatchNotOlderThan,
  1126  			expectedOut: []example.Pod{*preset[3]},
  1127  		},
  1128  		{
  1129  			name:        "list all items",
  1130  			prefix:      "/pods",
  1131  			pred:        storage.Everything,
  1132  			expectedOut: []example.Pod{*preset[0], *preset[1], *preset[2], *preset[3], *preset[4]},
  1133  		},
  1134  		{
  1135  			name:        "list all items with current resource version and match=NotOlderThan",
  1136  			prefix:      "/pods",
  1137  			pred:        storage.Everything,
  1138  			rv:          list.ResourceVersion,
  1139  			rvMatch:     metav1.ResourceVersionMatchNotOlderThan,
  1140  			expectedOut: []example.Pod{*preset[0], *preset[1], *preset[2], *preset[3], *preset[4]},
  1141  		},
  1142  		{
  1143  			name:   "verify list returns updated version of object; filter returns one item, page size equal to total list size with current resource version and match=NotOlderThan",
  1144  			prefix: "/pods",
  1145  			pred: storage.SelectionPredicate{
  1146  				Field: fields.OneTermEqualSelector("spec.nodeName", "fakeNode"),
  1147  				Label: labels.Everything(),
  1148  				Limit: 5,
  1149  			},
  1150  			rv:          list.ResourceVersion,
  1151  			rvMatch:     metav1.ResourceVersionMatchNotOlderThan,
  1152  			expectedOut: []example.Pod{*preset[0]},
  1153  		},
  1154  		{
  1155  			name:   "verify list does not return deleted object; filter for deleted object, page size equal to total list size with current resource version and match=NotOlderThan",
  1156  			prefix: "/pods",
  1157  			pred: storage.SelectionPredicate{
  1158  				Field: fields.OneTermEqualSelector("metadata.name", "baz"),
  1159  				Label: labels.Everything(),
  1160  				Limit: 5,
  1161  			},
  1162  			rv:          list.ResourceVersion,
  1163  			rvMatch:     metav1.ResourceVersionMatchNotOlderThan,
  1164  			expectedOut: []example.Pod{},
  1165  		},
  1166  		{
  1167  			name:        "test consistent List",
  1168  			prefix:      "/pods/empty",
  1169  			pred:        storage.Everything,
  1170  			rv:          "",
  1171  			expectRV:    currentRV,
  1172  			expectedOut: []example.Pod{},
  1173  		},
  1174  		{
  1175  			name:         "test non-consistent List",
  1176  			prefix:       "/pods/empty",
  1177  			pred:         storage.Everything,
  1178  			rv:           "0",
  1179  			expectRVFunc: resourceVersionNotOlderThan(list.ResourceVersion),
  1180  			expectedOut:  []example.Pod{},
  1181  		},
  1182  	}
  1183  
  1184  	for _, tt := range tests {
  1185  		tt := tt
  1186  		t.Run(tt.name, func(t *testing.T) {
  1187  			// For some asynchronous implementations of storage interface (in particular watchcache),
  1188  			// certain requests may impact result of further requests. As an example, if we first
  1189  			// ensure that watchcache is synchronized up to ResourceVersion X (using Get/List requests
  1190  			// with NotOlderThan semantic), the further requests (even specifying earlier resource
  1191  			// version) will also return the result synchronized to at least ResourceVersion X.
  1192  			// By parallelizing test cases we ensure that the order in which test cases are defined
  1193  			// doesn't automatically preclude some scenarios from happening.
  1194  			t.Parallel()
  1195  
  1196  			if ignoreWatchCacheTests && tt.ignoreForWatchCache {
  1197  				t.Skip()
  1198  			}
  1199  
  1200  			if tt.pred.GetAttrs == nil {
  1201  				tt.pred.GetAttrs = getAttrs
  1202  			}
  1203  
  1204  			out := &example.PodList{}
  1205  			storageOpts := storage.ListOptions{
  1206  				ResourceVersion:      tt.rv,
  1207  				ResourceVersionMatch: tt.rvMatch,
  1208  				Predicate:            tt.pred,
  1209  				Recursive:            true,
  1210  			}
  1211  			err := store.GetList(ctx, tt.prefix, storageOpts, out)
  1212  			if tt.expectRVTooLarge {
  1213  				if err == nil || !apierrors.IsTimeout(err) || !storage.IsTooLargeResourceVersion(err) {
  1214  					t.Fatalf("expecting resource version too high error, but get: %s", err)
  1215  				}
  1216  				return
  1217  			}
  1218  
  1219  			if err != nil {
  1220  				if !tt.expectError {
  1221  					t.Fatalf("GetList failed: %v", err)
  1222  				}
  1223  				return
  1224  			}
  1225  			if tt.expectError {
  1226  				t.Fatalf("expected error but got none")
  1227  			}
  1228  			if (len(out.Continue) > 0) != tt.expectContinue {
  1229  				t.Errorf("unexpected continue token: %q", out.Continue)
  1230  			}
  1231  
  1232  			// If a client requests an exact resource version, it must be echoed back to them.
  1233  			if tt.expectRV != "" {
  1234  				if tt.expectRV != out.ResourceVersion {
  1235  					t.Errorf("resourceVersion in list response want=%s, got=%s", tt.expectRV, out.ResourceVersion)
  1236  				}
  1237  			}
  1238  			if tt.expectRVFunc != nil {
  1239  				if err := tt.expectRVFunc(out.ResourceVersion); err != nil {
  1240  					t.Errorf("resourceVersion in list response invalid: %v", err)
  1241  				}
  1242  			}
  1243  
  1244  			if tt.expectedAlternatives == nil {
  1245  				sort.Sort(sortablePodList(tt.expectedOut))
  1246  				expectNoDiff(t, "incorrect list pods", tt.expectedOut, out.Items)
  1247  			} else {
  1248  				ExpectContains(t, "incorrect list pods", toInterfaceSlice(tt.expectedAlternatives), out.Items)
  1249  			}
  1250  			if !cmp.Equal(tt.expectedRemainingItemCount, out.RemainingItemCount) {
  1251  				t.Fatalf("unexpected remainingItemCount, diff: %s", cmp.Diff(tt.expectedRemainingItemCount, out.RemainingItemCount))
  1252  			}
  1253  		})
  1254  	}
  1255  }
  1256  
  1257  func RunTestConsistentList(ctx context.Context, t *testing.T, store storage.Interface, compaction Compaction, cacheEnabled, consistentReadsSupported bool) {
  1258  	outPod := &example.Pod{}
  1259  	inPod := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "default", Name: "foo"}}
  1260  	err := store.Create(ctx, computePodKey(inPod), inPod, outPod, 0)
  1261  	if err != nil {
  1262  		t.Errorf("Unexpected error: %v", err)
  1263  	}
  1264  	lastObjecRV := outPod.ResourceVersion
  1265  	compaction(ctx, t, outPod.ResourceVersion)
  1266  	parsedRV, _ := strconv.Atoi(outPod.ResourceVersion)
  1267  	currentRV := fmt.Sprintf("%d", parsedRV+1)
  1268  
  1269  	firstNonConsistentReadRV := lastObjecRV
  1270  	if consistentReadsSupported && !cacheEnabled {
  1271  		firstNonConsistentReadRV = currentRV
  1272  	}
  1273  
  1274  	secondNonConsistentReadRV := lastObjecRV
  1275  	if consistentReadsSupported {
  1276  		secondNonConsistentReadRV = currentRV
  1277  	}
  1278  
  1279  	tcs := []struct {
  1280  		name             string
  1281  		requestRV        string
  1282  		expectResponseRV string
  1283  	}{
  1284  		{
  1285  			name:             "Non-consistent list before sync",
  1286  			requestRV:        "0",
  1287  			expectResponseRV: firstNonConsistentReadRV,
  1288  		},
  1289  		{
  1290  			name:             "Consistent request returns currentRV",
  1291  			requestRV:        "",
  1292  			expectResponseRV: currentRV,
  1293  		},
  1294  		{
  1295  			name:             "Non-consistent request after sync returns currentRV",
  1296  			requestRV:        "0",
  1297  			expectResponseRV: secondNonConsistentReadRV,
  1298  		},
  1299  	}
  1300  	for _, tc := range tcs {
  1301  		t.Run(tc.name, func(t *testing.T) {
  1302  			out := &example.PodList{}
  1303  			opts := storage.ListOptions{
  1304  				ResourceVersion: tc.requestRV,
  1305  				Predicate:       storage.Everything,
  1306  			}
  1307  			err = store.GetList(ctx, "/pods/empty", opts, out)
  1308  			if err != nil {
  1309  				t.Fatalf("GetList failed: %v", err)
  1310  			}
  1311  			if out.ResourceVersion != tc.expectResponseRV {
  1312  				t.Errorf("resourceVersion in list response want=%s, got=%s", tc.expectResponseRV, out.ResourceVersion)
  1313  			}
  1314  		})
  1315  	}
  1316  }
  1317  
  1318  // seedMultiLevelData creates a set of keys with a multi-level structure, returning a resourceVersion
  1319  // from before any were created along with the full set of objects that were persisted
  1320  func seedMultiLevelData(ctx context.Context, store storage.Interface) (string, []*example.Pod, error) {
  1321  	// Setup storage with the following structure:
  1322  	//  /
  1323  	//   - first/
  1324  	//  |         - bar
  1325  	//  |
  1326  	//   - second/
  1327  	//  |         - bar
  1328  	//  |         - foo
  1329  	//  |         - [deleted] baz
  1330  	//  |
  1331  	//   - third/
  1332  	//  |         - barfoo
  1333  	//  |         - foo
  1334  	barFirst := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "first", Name: "bar"}}
  1335  	barSecond := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "second", Name: "bar"}}
  1336  	fooSecond := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "second", Name: "foo"}}
  1337  	bazSecond := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "second", Name: "baz"}}
  1338  	barfooThird := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "third", Name: "barfoo"}}
  1339  	fooThird := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "third", Name: "foo"}}
  1340  
  1341  	preset := []struct {
  1342  		key       string
  1343  		obj       *example.Pod
  1344  		storedObj *example.Pod
  1345  	}{
  1346  		{
  1347  			key: computePodKey(barFirst),
  1348  			obj: barFirst,
  1349  		},
  1350  		{
  1351  			key: computePodKey(barSecond),
  1352  			obj: barSecond,
  1353  		},
  1354  		{
  1355  			key: computePodKey(fooSecond),
  1356  			obj: fooSecond,
  1357  		},
  1358  		{
  1359  			key: computePodKey(barfooThird),
  1360  			obj: barfooThird,
  1361  		},
  1362  		{
  1363  			key: computePodKey(fooThird),
  1364  			obj: fooThird,
  1365  		},
  1366  		{
  1367  			key: computePodKey(bazSecond),
  1368  			obj: bazSecond,
  1369  		},
  1370  	}
  1371  
  1372  	// we want to figure out the resourceVersion before we create anything
  1373  	initialList := &example.PodList{}
  1374  	if err := store.GetList(ctx, "/pods", storage.ListOptions{Predicate: storage.Everything, Recursive: true}, initialList); err != nil {
  1375  		return "", nil, fmt.Errorf("failed to determine starting resourceVersion: %w", err)
  1376  	}
  1377  	initialRV := initialList.ResourceVersion
  1378  
  1379  	for i, ps := range preset {
  1380  		preset[i].storedObj = &example.Pod{}
  1381  		err := store.Create(ctx, ps.key, ps.obj, preset[i].storedObj, 0)
  1382  		if err != nil {
  1383  			return "", nil, fmt.Errorf("failed to create object: %w", err)
  1384  		}
  1385  	}
  1386  
  1387  	// For barFirst, we first create it with key /pods/first/bar and then we update
  1388  	// it by changing its spec.nodeName. The point of doing this is to be able to
  1389  	// test that if a pod with key /pods/first/bar is in fact returned, the returned
  1390  	// pod is the updated one (i.e. with spec.nodeName changed).
  1391  	preset[0].storedObj = &example.Pod{}
  1392  	if err := store.GuaranteedUpdate(ctx, computePodKey(barFirst), preset[0].storedObj, true, nil,
  1393  		func(input runtime.Object, _ storage.ResponseMeta) (output runtime.Object, ttl *uint64, err error) {
  1394  			pod := input.(*example.Pod).DeepCopy()
  1395  			pod.Spec.NodeName = "fakeNode"
  1396  			return pod, nil, nil
  1397  		}, nil); err != nil {
  1398  		return "", nil, fmt.Errorf("failed to update object: %w", err)
  1399  	}
  1400  
  1401  	// We now delete bazSecond provided it has been created first. We do this to enable
  1402  	// testing cases that had an object exist initially and then was deleted and how this
  1403  	// would be reflected in responses of different calls.
  1404  	if err := store.Delete(ctx, computePodKey(bazSecond), preset[len(preset)-1].storedObj, nil, storage.ValidateAllObjectFunc, nil); err != nil {
  1405  		return "", nil, fmt.Errorf("failed to delete object: %w", err)
  1406  	}
  1407  
  1408  	// Since we deleted bazSecond (last element of preset), we remove it from preset.
  1409  	preset = preset[:len(preset)-1]
  1410  	var created []*example.Pod
  1411  	for _, item := range preset {
  1412  		created = append(created, item.storedObj)
  1413  	}
  1414  	return initialRV, created, nil
  1415  }
  1416  
  1417  func RunTestGetListNonRecursive(ctx context.Context, t *testing.T, compaction Compaction, store storage.Interface) {
  1418  	key, prevStoredObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}})
  1419  	prevRV, _ := strconv.Atoi(prevStoredObj.ResourceVersion)
  1420  
  1421  	storedObj := &example.Pod{}
  1422  	if err := store.GuaranteedUpdate(ctx, key, storedObj, false, nil,
  1423  		func(_ runtime.Object, _ storage.ResponseMeta) (runtime.Object, *uint64, error) {
  1424  			newPod := prevStoredObj.DeepCopy()
  1425  			newPod.Annotations = map[string]string{"version": "second"}
  1426  			return newPod, nil, nil
  1427  		}, nil); err != nil {
  1428  		t.Fatalf("update failed: %v", err)
  1429  	}
  1430  	objRV, _ := strconv.Atoi(storedObj.ResourceVersion)
  1431  	// Use compact to increase etcd global revision without changes to any resources.
  1432  	// The increase in resources version comes from Kubernetes compaction updating hidden key.
  1433  	// Used to test consistent List to confirm it returns latest etcd revision.
  1434  	compaction(ctx, t, prevStoredObj.ResourceVersion)
  1435  
  1436  	tests := []struct {
  1437  		name                 string
  1438  		key                  string
  1439  		pred                 storage.SelectionPredicate
  1440  		expectedOut          []example.Pod
  1441  		expectedAlternatives [][]example.Pod
  1442  		rv                   string
  1443  		rvMatch              metav1.ResourceVersionMatch
  1444  		expectRVTooLarge     bool
  1445  	}{{
  1446  		name:        "existing key",
  1447  		key:         key,
  1448  		pred:        storage.Everything,
  1449  		expectedOut: []example.Pod{*storedObj},
  1450  	}, {
  1451  		name:                 "existing key, resourceVersion=0",
  1452  		key:                  key,
  1453  		pred:                 storage.Everything,
  1454  		expectedAlternatives: [][]example.Pod{{}, {*prevStoredObj}, {*storedObj}},
  1455  		rv:                   "0",
  1456  	}, {
  1457  		name:                 "existing key, resourceVersion=0, resourceVersionMatch=notOlderThan",
  1458  		key:                  key,
  1459  		pred:                 storage.Everything,
  1460  		expectedAlternatives: [][]example.Pod{{}, {*prevStoredObj}, {*storedObj}},
  1461  		rv:                   "0",
  1462  		rvMatch:              metav1.ResourceVersionMatchNotOlderThan,
  1463  	}, {
  1464  		name:        "existing key, resourceVersion=current",
  1465  		key:         key,
  1466  		pred:        storage.Everything,
  1467  		expectedOut: []example.Pod{*storedObj},
  1468  		rv:          fmt.Sprintf("%d", objRV),
  1469  	}, {
  1470  		name:        "existing key, resourceVersion=current, resourceVersionMatch=notOlderThan",
  1471  		key:         key,
  1472  		pred:        storage.Everything,
  1473  		expectedOut: []example.Pod{*storedObj},
  1474  		rv:          fmt.Sprintf("%d", objRV),
  1475  		rvMatch:     metav1.ResourceVersionMatchNotOlderThan,
  1476  	}, {
  1477  		name:                 "existing key, resourceVersion=previous, resourceVersionMatch=notOlderThan",
  1478  		key:                  key,
  1479  		pred:                 storage.Everything,
  1480  		expectedAlternatives: [][]example.Pod{{*prevStoredObj}, {*storedObj}},
  1481  		rv:                   fmt.Sprintf("%d", prevRV),
  1482  		rvMatch:              metav1.ResourceVersionMatchNotOlderThan,
  1483  	}, {
  1484  		name:        "existing key, resourceVersion=current, resourceVersionMatch=exact",
  1485  		key:         key,
  1486  		pred:        storage.Everything,
  1487  		expectedOut: []example.Pod{*storedObj},
  1488  		rv:          fmt.Sprintf("%d", objRV),
  1489  		rvMatch:     metav1.ResourceVersionMatchExact,
  1490  	}, {
  1491  		name:        "existing key, resourceVersion=previous, resourceVersionMatch=exact",
  1492  		key:         key,
  1493  		pred:        storage.Everything,
  1494  		expectedOut: []example.Pod{*prevStoredObj},
  1495  		rv:          fmt.Sprintf("%d", prevRV),
  1496  		rvMatch:     metav1.ResourceVersionMatchExact,
  1497  	}, {
  1498  		name:             "existing key, resourceVersion=too high",
  1499  		key:              key,
  1500  		pred:             storage.Everything,
  1501  		expectedOut:      []example.Pod{*storedObj},
  1502  		rv:               strconv.FormatInt(math.MaxInt64, 10),
  1503  		expectRVTooLarge: true,
  1504  	}, {
  1505  		name:        "non-existing key",
  1506  		key:         "/non-existing",
  1507  		pred:        storage.Everything,
  1508  		expectedOut: []example.Pod{},
  1509  	}, {
  1510  		name: "with matching pod name",
  1511  		key:  "/non-existing",
  1512  		pred: storage.SelectionPredicate{
  1513  			Label: labels.Everything(),
  1514  			Field: fields.ParseSelectorOrDie("metadata.name!=" + storedObj.Name),
  1515  			GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
  1516  				pod := obj.(*example.Pod)
  1517  				return nil, fields.Set{"metadata.name": pod.Name}, nil
  1518  			},
  1519  		},
  1520  		expectedOut: []example.Pod{},
  1521  	}, {
  1522  		name: "existing key, resourceVersion=current, with not matching pod name",
  1523  		key:  key,
  1524  		pred: storage.SelectionPredicate{
  1525  			Label: labels.Everything(),
  1526  			Field: fields.ParseSelectorOrDie("metadata.name!=" + storedObj.Name),
  1527  			GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
  1528  				pod := obj.(*example.Pod)
  1529  				return nil, fields.Set{"metadata.name": pod.Name}, nil
  1530  			},
  1531  		},
  1532  		expectedOut: []example.Pod{},
  1533  		rv:          fmt.Sprintf("%d", objRV),
  1534  	}}
  1535  
  1536  	for _, tt := range tests {
  1537  		tt := tt
  1538  		t.Run(tt.name, func(t *testing.T) {
  1539  			// For some asynchronous implementations of storage interface (in particular watchcache),
  1540  			// certain requests may impact result of further requests. As an example, if we first
  1541  			// ensure that watchcache is synchronized up to ResourceVersion X (using Get/List requests
  1542  			// with NotOlderThan semantic), the further requests (even specifying earlier resource
  1543  			// version) will also return the result synchronized to at least ResourceVersion X.
  1544  			// By parallelizing test cases we ensure that the order in which test cases are defined
  1545  			// doesn't automatically preclude some scenarios from happening.
  1546  			t.Parallel()
  1547  
  1548  			out := &example.PodList{}
  1549  			storageOpts := storage.ListOptions{
  1550  				ResourceVersion:      tt.rv,
  1551  				ResourceVersionMatch: tt.rvMatch,
  1552  				Predicate:            tt.pred,
  1553  				Recursive:            false,
  1554  			}
  1555  			err := store.GetList(ctx, tt.key, storageOpts, out)
  1556  
  1557  			if tt.expectRVTooLarge {
  1558  				if err == nil || !storage.IsTooLargeResourceVersion(err) {
  1559  					t.Errorf("%s: expecting resource version too high error, but get: %s", tt.name, err)
  1560  				}
  1561  				return
  1562  			}
  1563  
  1564  			if err != nil {
  1565  				t.Fatalf("GetList failed: %v", err)
  1566  			}
  1567  			if len(out.ResourceVersion) == 0 {
  1568  				t.Errorf("%s: unset resourceVersion", tt.name)
  1569  			}
  1570  
  1571  			if tt.expectedAlternatives == nil {
  1572  				expectNoDiff(t, "incorrect list pods", tt.expectedOut, out.Items)
  1573  			} else {
  1574  				ExpectContains(t, "incorrect list pods", toInterfaceSlice(tt.expectedAlternatives), out.Items)
  1575  			}
  1576  		})
  1577  	}
  1578  }
  1579  
  1580  // RunTestGetListRecursivePrefix tests how recursive parameter works for object keys that are prefixes of each other.
  1581  func RunTestGetListRecursivePrefix(ctx context.Context, t *testing.T, store storage.Interface) {
  1582  	fooKey, fooObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}})
  1583  	fooBarKey, fooBarObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foobar", Namespace: "test-ns"}})
  1584  	_, otherNamespaceObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "baz", Namespace: "test-ns2"}})
  1585  	lastRev := otherNamespaceObj.ResourceVersion
  1586  
  1587  	tests := []struct {
  1588  		name        string
  1589  		key         string
  1590  		recursive   bool
  1591  		expectedOut []example.Pod
  1592  	}{
  1593  		{
  1594  			name:        "NonRecursive on resource prefix doesn't return any objects",
  1595  			key:         "/pods/",
  1596  			recursive:   false,
  1597  			expectedOut: []example.Pod{},
  1598  		},
  1599  		{
  1600  			name:        "Recursive on resource prefix returns all objects",
  1601  			key:         "/pods/",
  1602  			recursive:   true,
  1603  			expectedOut: []example.Pod{*fooObj, *fooBarObj, *otherNamespaceObj},
  1604  		},
  1605  		{
  1606  			name:        "NonRecursive on namespace prefix doesn't return any objects",
  1607  			key:         "/pods/test-ns/",
  1608  			recursive:   false,
  1609  			expectedOut: []example.Pod{},
  1610  		},
  1611  		{
  1612  			name:        "Recursive on resource prefix returns objects in the namespace",
  1613  			key:         "/pods/test-ns/",
  1614  			recursive:   true,
  1615  			expectedOut: []example.Pod{*fooObj, *fooBarObj},
  1616  		},
  1617  		{
  1618  			name:        "NonRecursive on object key (prefix) returns object and no other objects with the same prefix",
  1619  			key:         fooKey,
  1620  			recursive:   false,
  1621  			expectedOut: []example.Pod{*fooObj},
  1622  		},
  1623  		{
  1624  			name:        "Recursive on object key (prefix) doesn't return anything",
  1625  			key:         fooKey,
  1626  			recursive:   true,
  1627  			expectedOut: []example.Pod{},
  1628  		},
  1629  		{
  1630  			name:        "NonRecursive on object key (no-prefix) return object",
  1631  			key:         fooBarKey,
  1632  			recursive:   false,
  1633  			expectedOut: []example.Pod{*fooBarObj},
  1634  		},
  1635  		{
  1636  			name:        "Recursive on object key (no-prefix) doesn't return anything",
  1637  			key:         fooBarKey,
  1638  			recursive:   true,
  1639  			expectedOut: []example.Pod{},
  1640  		},
  1641  	}
  1642  
  1643  	listTypes := []struct {
  1644  		name            string
  1645  		ResourceVersion string
  1646  		Match           metav1.ResourceVersionMatch
  1647  	}{
  1648  		{
  1649  			name:            "Exact",
  1650  			ResourceVersion: lastRev,
  1651  			Match:           metav1.ResourceVersionMatchExact,
  1652  		},
  1653  		{
  1654  			name:            "Consistent",
  1655  			ResourceVersion: "",
  1656  		},
  1657  		{
  1658  			name:            "NotOlderThan",
  1659  			ResourceVersion: "0",
  1660  			Match:           metav1.ResourceVersionMatchNotOlderThan,
  1661  		},
  1662  	}
  1663  
  1664  	for _, listType := range listTypes {
  1665  		listType := listType
  1666  		t.Run(listType.name, func(t *testing.T) {
  1667  			for _, tt := range tests {
  1668  				tt := tt
  1669  				t.Run(tt.name, func(t *testing.T) {
  1670  					out := &example.PodList{}
  1671  					storageOpts := storage.ListOptions{
  1672  						ResourceVersion:      listType.ResourceVersion,
  1673  						ResourceVersionMatch: listType.Match,
  1674  						Recursive:            tt.recursive,
  1675  						Predicate:            storage.Everything,
  1676  					}
  1677  					err := store.GetList(ctx, tt.key, storageOpts, out)
  1678  					if err != nil {
  1679  						t.Fatalf("GetList failed: %v", err)
  1680  					}
  1681  					expectNoDiff(t, "incorrect list pods", tt.expectedOut, out.Items)
  1682  				})
  1683  			}
  1684  		})
  1685  	}
  1686  }
  1687  
  1688  type CallsValidation func(t *testing.T, pageSize, estimatedProcessedObjects uint64)
  1689  
  1690  func RunTestListContinuation(ctx context.Context, t *testing.T, store storage.Interface, validation CallsValidation) {
  1691  	// Setup storage with the following structure:
  1692  	//  /
  1693  	//   - first/
  1694  	//  |         - bar
  1695  	//  |
  1696  	//   - second/
  1697  	//  |         - bar
  1698  	//  |         - foo
  1699  	barFirst := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "first", Name: "bar"}}
  1700  	barSecond := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "second", Name: "bar"}}
  1701  	fooSecond := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "second", Name: "foo"}}
  1702  	preset := []struct {
  1703  		key       string
  1704  		obj       *example.Pod
  1705  		storedObj *example.Pod
  1706  	}{
  1707  		{
  1708  			key: computePodKey(barFirst),
  1709  			obj: barFirst,
  1710  		},
  1711  		{
  1712  			key: computePodKey(barSecond),
  1713  			obj: barSecond,
  1714  		},
  1715  		{
  1716  			key: computePodKey(fooSecond),
  1717  			obj: fooSecond,
  1718  		},
  1719  	}
  1720  
  1721  	var currentRV string
  1722  	for i, ps := range preset {
  1723  		preset[i].storedObj = &example.Pod{}
  1724  		err := store.Create(ctx, ps.key, ps.obj, preset[i].storedObj, 0)
  1725  		if err != nil {
  1726  			t.Fatalf("Set failed: %v", err)
  1727  		}
  1728  		currentRV = preset[i].storedObj.ResourceVersion
  1729  	}
  1730  
  1731  	// test continuations
  1732  	out := &example.PodList{}
  1733  	pred := func(limit int64, continueValue string) storage.SelectionPredicate {
  1734  		return storage.SelectionPredicate{
  1735  			Limit:    limit,
  1736  			Continue: continueValue,
  1737  			Label:    labels.Everything(),
  1738  			Field:    fields.Everything(),
  1739  			GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
  1740  				pod := obj.(*example.Pod)
  1741  				return nil, fields.Set{"metadata.name": pod.Name}, nil
  1742  			},
  1743  		}
  1744  	}
  1745  	options := storage.ListOptions{
  1746  		// Limit is ignored when ResourceVersion is set to 0.
  1747  		// Set it to consistent read.
  1748  		ResourceVersion: "",
  1749  		Predicate:       pred(1, ""),
  1750  		Recursive:       true,
  1751  	}
  1752  	if err := store.GetList(ctx, "/pods", options, out); err != nil {
  1753  		t.Fatalf("Unable to get initial list: %v", err)
  1754  	}
  1755  	if len(out.Continue) == 0 {
  1756  		t.Fatalf("No continuation token set")
  1757  	}
  1758  	expectNoDiff(t, "incorrect first page", []example.Pod{*preset[0].storedObj}, out.Items)
  1759  	if out.ResourceVersion != currentRV {
  1760  		t.Errorf("Expect output.ResourceVersion = %s, but got %s", currentRV, out.ResourceVersion)
  1761  	}
  1762  	if validation != nil {
  1763  		validation(t, 1, 1)
  1764  	}
  1765  
  1766  	continueFromSecondItem := out.Continue
  1767  
  1768  	// no limit, should get two items
  1769  	out = &example.PodList{}
  1770  	options = storage.ListOptions{
  1771  		// ResourceVersion should be unset when setting continuation token.
  1772  		ResourceVersion: "",
  1773  		Predicate:       pred(0, continueFromSecondItem),
  1774  		Recursive:       true,
  1775  	}
  1776  	if err := store.GetList(ctx, "/pods", options, out); err != nil {
  1777  		t.Fatalf("Unable to get second page: %v", err)
  1778  	}
  1779  	if len(out.Continue) != 0 {
  1780  		t.Fatalf("Unexpected continuation token set")
  1781  	}
  1782  	key, rv, err := storage.DecodeContinue(continueFromSecondItem, "/pods")
  1783  	t.Logf("continue token was %d %s %v", rv, key, err)
  1784  	expectNoDiff(t, "incorrect second page", []example.Pod{*preset[1].storedObj, *preset[2].storedObj}, out.Items)
  1785  	if out.ResourceVersion != currentRV {
  1786  		t.Errorf("Expect output.ResourceVersion = %s, but got %s", currentRV, out.ResourceVersion)
  1787  	}
  1788  	if validation != nil {
  1789  		validation(t, 0, 2)
  1790  	}
  1791  
  1792  	// limit, should get two more pages
  1793  	out = &example.PodList{}
  1794  	options = storage.ListOptions{
  1795  		// ResourceVersion should be unset when setting continuation token.
  1796  		ResourceVersion: "",
  1797  		Predicate:       pred(1, continueFromSecondItem),
  1798  		Recursive:       true,
  1799  	}
  1800  	if err := store.GetList(ctx, "/pods", options, out); err != nil {
  1801  		t.Fatalf("Unable to get second page: %v", err)
  1802  	}
  1803  	if len(out.Continue) == 0 {
  1804  		t.Fatalf("No continuation token set")
  1805  	}
  1806  	expectNoDiff(t, "incorrect second page", []example.Pod{*preset[1].storedObj}, out.Items)
  1807  	if out.ResourceVersion != currentRV {
  1808  		t.Errorf("Expect output.ResourceVersion = %s, but got %s", currentRV, out.ResourceVersion)
  1809  	}
  1810  	if validation != nil {
  1811  		validation(t, 1, 1)
  1812  	}
  1813  
  1814  	continueFromThirdItem := out.Continue
  1815  
  1816  	out = &example.PodList{}
  1817  	options = storage.ListOptions{
  1818  		// ResourceVersion should be unset when setting continuation token.
  1819  		ResourceVersion: "",
  1820  		Predicate:       pred(1, continueFromThirdItem),
  1821  		Recursive:       true,
  1822  	}
  1823  	if err := store.GetList(ctx, "/pods", options, out); err != nil {
  1824  		t.Fatalf("Unable to get second page: %v", err)
  1825  	}
  1826  	if len(out.Continue) != 0 {
  1827  		t.Fatalf("Unexpected continuation token set")
  1828  	}
  1829  	expectNoDiff(t, "incorrect third page", []example.Pod{*preset[2].storedObj}, out.Items)
  1830  	if out.ResourceVersion != currentRV {
  1831  		t.Errorf("Expect output.ResourceVersion = %s, but got %s", currentRV, out.ResourceVersion)
  1832  	}
  1833  	if validation != nil {
  1834  		validation(t, 1, 1)
  1835  	}
  1836  }
  1837  
  1838  func RunTestListPaginationRareObject(ctx context.Context, t *testing.T, store storage.Interface, validation CallsValidation) {
  1839  	podCount := 1000
  1840  	var pods []*example.Pod
  1841  	for i := 0; i < podCount; i++ {
  1842  		obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("pod-%d", i)}}
  1843  		key := computePodKey(obj)
  1844  		storedObj := &example.Pod{}
  1845  		err := store.Create(ctx, key, obj, storedObj, 0)
  1846  		if err != nil {
  1847  			t.Fatalf("Set failed: %v", err)
  1848  		}
  1849  		pods = append(pods, storedObj)
  1850  	}
  1851  
  1852  	out := &example.PodList{}
  1853  	options := storage.ListOptions{
  1854  		Predicate: storage.SelectionPredicate{
  1855  			Limit: 1,
  1856  			Label: labels.Everything(),
  1857  			Field: fields.OneTermEqualSelector("metadata.name", "pod-999"),
  1858  			GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
  1859  				pod := obj.(*example.Pod)
  1860  				return nil, fields.Set{"metadata.name": pod.Name}, nil
  1861  			},
  1862  		},
  1863  		Recursive: true,
  1864  	}
  1865  	if err := store.GetList(ctx, "/pods", options, out); err != nil {
  1866  		t.Fatalf("Unable to get initial list: %v", err)
  1867  	}
  1868  	if len(out.Continue) != 0 {
  1869  		t.Errorf("Unexpected continuation token set")
  1870  	}
  1871  	if len(out.Items) != 1 || !reflect.DeepEqual(&out.Items[0], pods[999]) {
  1872  		t.Fatalf("Unexpected first page: %#v", out.Items)
  1873  	}
  1874  	if validation != nil {
  1875  		validation(t, 1, uint64(podCount))
  1876  	}
  1877  }
  1878  
  1879  func RunTestListContinuationWithFilter(ctx context.Context, t *testing.T, store storage.Interface, validation CallsValidation) {
  1880  	foo1 := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "1", Name: "foo"}}
  1881  	bar2 := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "2", Name: "bar"}} // this should not match
  1882  	foo3 := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "3", Name: "foo"}}
  1883  	foo4 := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "4", Name: "foo"}}
  1884  	preset := []struct {
  1885  		key       string
  1886  		obj       *example.Pod
  1887  		storedObj *example.Pod
  1888  	}{
  1889  		{
  1890  			key: computePodKey(foo1),
  1891  			obj: foo1,
  1892  		},
  1893  		{
  1894  			key: computePodKey(bar2),
  1895  			obj: bar2,
  1896  		},
  1897  		{
  1898  			key: computePodKey(foo3),
  1899  			obj: foo3,
  1900  		},
  1901  		{
  1902  			key: computePodKey(foo4),
  1903  			obj: foo4,
  1904  		},
  1905  	}
  1906  
  1907  	var currentRV string
  1908  	for i, ps := range preset {
  1909  		preset[i].storedObj = &example.Pod{}
  1910  		err := store.Create(ctx, ps.key, ps.obj, preset[i].storedObj, 0)
  1911  		if err != nil {
  1912  			t.Fatalf("Set failed: %v", err)
  1913  		}
  1914  		currentRV = preset[i].storedObj.ResourceVersion
  1915  	}
  1916  
  1917  	// the first list call should try to get 2 items from etcd (and only those items should be returned)
  1918  	// the field selector should result in it reading 3 items via the transformer
  1919  	// the chunking should result in 2 etcd Gets
  1920  	// there should be a continueValue because there is more data
  1921  	out := &example.PodList{}
  1922  	pred := func(limit int64, continueValue string) storage.SelectionPredicate {
  1923  		return storage.SelectionPredicate{
  1924  			Limit:    limit,
  1925  			Continue: continueValue,
  1926  			Label:    labels.Everything(),
  1927  			Field:    fields.OneTermNotEqualSelector("metadata.name", "bar"),
  1928  			GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
  1929  				pod := obj.(*example.Pod)
  1930  				return nil, fields.Set{"metadata.name": pod.Name}, nil
  1931  			},
  1932  		}
  1933  	}
  1934  	options := storage.ListOptions{
  1935  		// Limit is ignored when ResourceVersion is set to 0.
  1936  		// Set it to consistent read.
  1937  		ResourceVersion: "",
  1938  		Predicate:       pred(2, ""),
  1939  		Recursive:       true,
  1940  	}
  1941  	if err := store.GetList(ctx, "/pods", options, out); err != nil {
  1942  		t.Errorf("Unable to get initial list: %v", err)
  1943  	}
  1944  	if len(out.Continue) == 0 {
  1945  		t.Errorf("No continuation token set")
  1946  	}
  1947  	expectNoDiff(t, "incorrect first page", []example.Pod{*preset[0].storedObj, *preset[2].storedObj}, out.Items)
  1948  	if out.ResourceVersion != currentRV {
  1949  		t.Errorf("Expect output.ResourceVersion = %s, but got %s", currentRV, out.ResourceVersion)
  1950  	}
  1951  	if validation != nil {
  1952  		validation(t, 2, 3)
  1953  	}
  1954  
  1955  	// the rest of the test does not make sense if the previous call failed
  1956  	if t.Failed() {
  1957  		return
  1958  	}
  1959  
  1960  	cont := out.Continue
  1961  
  1962  	// the second list call should try to get 2 more items from etcd
  1963  	// but since there is only one item left, that is all we should get with no continueValue
  1964  	// both read counters should be incremented for the singular calls they make in this case
  1965  	out = &example.PodList{}
  1966  	options = storage.ListOptions{
  1967  		// ResourceVersion should be unset when setting continuation token.
  1968  		ResourceVersion: "",
  1969  		Predicate:       pred(2, cont),
  1970  		Recursive:       true,
  1971  	}
  1972  	if err := store.GetList(ctx, "/pods", options, out); err != nil {
  1973  		t.Errorf("Unable to get second page: %v", err)
  1974  	}
  1975  	if len(out.Continue) != 0 {
  1976  		t.Errorf("Unexpected continuation token set")
  1977  	}
  1978  	expectNoDiff(t, "incorrect second page", []example.Pod{*preset[3].storedObj}, out.Items)
  1979  	if out.ResourceVersion != currentRV {
  1980  		t.Errorf("Expect output.ResourceVersion = %s, but got %s", currentRV, out.ResourceVersion)
  1981  	}
  1982  	if validation != nil {
  1983  		validation(t, 2, 1)
  1984  	}
  1985  }
  1986  
  1987  type Compaction func(ctx context.Context, t *testing.T, resourceVersion string)
  1988  
  1989  func RunTestListInconsistentContinuation(ctx context.Context, t *testing.T, store storage.Interface, compaction Compaction) {
  1990  	if compaction == nil {
  1991  		t.Skipf("compaction callback not provided")
  1992  	}
  1993  
  1994  	// Setup storage with the following structure:
  1995  	//  /
  1996  	//   - first/
  1997  	//  |         - bar
  1998  	//  |
  1999  	//   - second/
  2000  	//  |         - bar
  2001  	//  |         - foo
  2002  	barFirst := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "first", Name: "bar"}}
  2003  	barSecond := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "second", Name: "bar"}}
  2004  	fooSecond := &example.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "second", Name: "foo"}}
  2005  	preset := []struct {
  2006  		key       string
  2007  		obj       *example.Pod
  2008  		storedObj *example.Pod
  2009  	}{
  2010  		{
  2011  			key: computePodKey(barFirst),
  2012  			obj: barFirst,
  2013  		},
  2014  		{
  2015  			key: computePodKey(barSecond),
  2016  			obj: barSecond,
  2017  		},
  2018  		{
  2019  			key: computePodKey(fooSecond),
  2020  			obj: fooSecond,
  2021  		},
  2022  	}
  2023  	for i, ps := range preset {
  2024  		preset[i].storedObj = &example.Pod{}
  2025  		err := store.Create(ctx, ps.key, ps.obj, preset[i].storedObj, 0)
  2026  		if err != nil {
  2027  			t.Fatalf("Set failed: %v", err)
  2028  		}
  2029  	}
  2030  
  2031  	pred := func(limit int64, continueValue string) storage.SelectionPredicate {
  2032  		return storage.SelectionPredicate{
  2033  			Limit:    limit,
  2034  			Continue: continueValue,
  2035  			Label:    labels.Everything(),
  2036  			Field:    fields.Everything(),
  2037  			GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
  2038  				pod := obj.(*example.Pod)
  2039  				return nil, fields.Set{"metadata.name": pod.Name}, nil
  2040  			},
  2041  		}
  2042  	}
  2043  
  2044  	out := &example.PodList{}
  2045  	options := storage.ListOptions{
  2046  		ResourceVersion: "0",
  2047  		Predicate:       pred(1, ""),
  2048  		Recursive:       true,
  2049  	}
  2050  	if err := store.GetList(ctx, "/pods", options, out); err != nil {
  2051  		t.Fatalf("Unable to get initial list: %v", err)
  2052  	}
  2053  	if len(out.Continue) == 0 {
  2054  		t.Fatalf("No continuation token set")
  2055  	}
  2056  	expectNoDiff(t, "incorrect first page", []example.Pod{*preset[0].storedObj}, out.Items)
  2057  
  2058  	continueFromSecondItem := out.Continue
  2059  
  2060  	// update /second/bar
  2061  	oldName := preset[2].obj.Name
  2062  	newPod := &example.Pod{
  2063  		ObjectMeta: metav1.ObjectMeta{
  2064  			Name: oldName,
  2065  			Labels: map[string]string{
  2066  				"state": "new",
  2067  			},
  2068  		},
  2069  	}
  2070  	if err := store.GuaranteedUpdate(ctx, preset[2].key, preset[2].storedObj, false, nil,
  2071  		func(_ runtime.Object, _ storage.ResponseMeta) (runtime.Object, *uint64, error) {
  2072  			return newPod, nil, nil
  2073  		}, newPod); err != nil {
  2074  		t.Fatalf("update failed: %v", err)
  2075  	}
  2076  
  2077  	// compact to latest revision.
  2078  	lastRVString := preset[2].storedObj.ResourceVersion
  2079  	compaction(ctx, t, lastRVString)
  2080  
  2081  	// The old continue token should have expired
  2082  	options = storage.ListOptions{
  2083  		ResourceVersion: "0",
  2084  		Predicate:       pred(0, continueFromSecondItem),
  2085  		Recursive:       true,
  2086  	}
  2087  	err := store.GetList(ctx, "/pods", options, out)
  2088  	if err == nil {
  2089  		t.Fatalf("unexpected no error")
  2090  	}
  2091  	if !strings.Contains(err.Error(), "The provided continue parameter is too old ") {
  2092  		t.Fatalf("unexpected error message %v", err)
  2093  	}
  2094  	status, ok := err.(apierrors.APIStatus)
  2095  	if !ok {
  2096  		t.Fatalf("expect error of implements the APIStatus interface, got %v", reflect.TypeOf(err))
  2097  	}
  2098  	inconsistentContinueFromSecondItem := status.Status().ListMeta.Continue
  2099  	if len(inconsistentContinueFromSecondItem) == 0 {
  2100  		t.Fatalf("expect non-empty continue token")
  2101  	}
  2102  
  2103  	out = &example.PodList{}
  2104  	options = storage.ListOptions{
  2105  		ResourceVersion: "0",
  2106  		Predicate:       pred(1, inconsistentContinueFromSecondItem),
  2107  		Recursive:       true,
  2108  	}
  2109  	if err := store.GetList(ctx, "/pods", options, out); err != nil {
  2110  		t.Fatalf("Unable to get second page: %v", err)
  2111  	}
  2112  	if len(out.Continue) == 0 {
  2113  		t.Fatalf("No continuation token set")
  2114  	}
  2115  	validateResourceVersion := resourceVersionNotOlderThan(lastRVString)
  2116  	expectNoDiff(t, "incorrect second page", []example.Pod{*preset[1].storedObj}, out.Items)
  2117  	if err := validateResourceVersion(out.ResourceVersion); err != nil {
  2118  		t.Fatal(err)
  2119  	}
  2120  	continueFromThirdItem := out.Continue
  2121  	resolvedResourceVersionFromThirdItem := out.ResourceVersion
  2122  	out = &example.PodList{}
  2123  	options = storage.ListOptions{
  2124  		ResourceVersion: "0",
  2125  		Predicate:       pred(1, continueFromThirdItem),
  2126  		Recursive:       true,
  2127  	}
  2128  	if err := store.GetList(ctx, "/pods", options, out); err != nil {
  2129  		t.Fatalf("Unable to get second page: %v", err)
  2130  	}
  2131  	if len(out.Continue) != 0 {
  2132  		t.Fatalf("Unexpected continuation token set")
  2133  	}
  2134  	expectNoDiff(t, "incorrect third page", []example.Pod{*preset[2].storedObj}, out.Items)
  2135  	if out.ResourceVersion != resolvedResourceVersionFromThirdItem {
  2136  		t.Fatalf("Expected list resource version to be %s, got %s", resolvedResourceVersionFromThirdItem, out.ResourceVersion)
  2137  	}
  2138  }
  2139  
  2140  type PrefixTransformerModifier func(*PrefixTransformer) value.Transformer
  2141  
  2142  type InterfaceWithPrefixTransformer interface {
  2143  	storage.Interface
  2144  
  2145  	UpdatePrefixTransformer(PrefixTransformerModifier) func()
  2146  }
  2147  
  2148  func RunTestListResourceVersionMatch(ctx context.Context, t *testing.T, store InterfaceWithPrefixTransformer) {
  2149  	nextPod := func(index uint32) (string, *example.Pod) {
  2150  		obj := &example.Pod{
  2151  			ObjectMeta: metav1.ObjectMeta{
  2152  				Name: fmt.Sprintf("pod-%d", index),
  2153  				Labels: map[string]string{
  2154  					"even": strconv.FormatBool(index%2 == 0),
  2155  				},
  2156  			},
  2157  		}
  2158  		return computePodKey(obj), obj
  2159  	}
  2160  
  2161  	transformer := &reproducingTransformer{
  2162  		store:      store,
  2163  		nextObject: nextPod,
  2164  	}
  2165  
  2166  	revertTransformer := store.UpdatePrefixTransformer(
  2167  		func(previousTransformer *PrefixTransformer) value.Transformer {
  2168  			transformer.wrapped = previousTransformer
  2169  			return transformer
  2170  		})
  2171  	defer revertTransformer()
  2172  
  2173  	for i := 0; i < 5; i++ {
  2174  		if err := transformer.createObject(ctx); err != nil {
  2175  			t.Fatalf("failed to create object: %v", err)
  2176  		}
  2177  	}
  2178  
  2179  	getAttrs := func(obj runtime.Object) (labels.Set, fields.Set, error) {
  2180  		pod, ok := obj.(*example.Pod)
  2181  		if !ok {
  2182  			return nil, nil, fmt.Errorf("invalid object")
  2183  		}
  2184  		return labels.Set(pod.Labels), nil, nil
  2185  	}
  2186  	predicate := storage.SelectionPredicate{
  2187  		Label:    labels.Set{"even": "true"}.AsSelector(),
  2188  		GetAttrs: getAttrs,
  2189  		Limit:    4,
  2190  	}
  2191  
  2192  	result1 := example.PodList{}
  2193  	options := storage.ListOptions{
  2194  		Predicate: predicate,
  2195  		Recursive: true,
  2196  	}
  2197  	if err := store.GetList(ctx, "/pods", options, &result1); err != nil {
  2198  		t.Fatalf("failed to list objects: %v", err)
  2199  	}
  2200  
  2201  	// List objects from the returned resource version.
  2202  	options = storage.ListOptions{
  2203  		Predicate:            predicate,
  2204  		ResourceVersion:      result1.ResourceVersion,
  2205  		ResourceVersionMatch: metav1.ResourceVersionMatchExact,
  2206  		Recursive:            true,
  2207  	}
  2208  
  2209  	result2 := example.PodList{}
  2210  	if err := store.GetList(ctx, "/pods", options, &result2); err != nil {
  2211  		t.Fatalf("failed to list objects: %v", err)
  2212  	}
  2213  
  2214  	expectNoDiff(t, "incorrect lists", result1, result2)
  2215  
  2216  	// Now also verify the  ResourceVersionMatchNotOlderThan.
  2217  	options.ResourceVersionMatch = metav1.ResourceVersionMatchNotOlderThan
  2218  
  2219  	result3 := example.PodList{}
  2220  	if err := store.GetList(ctx, "/pods", options, &result3); err != nil {
  2221  		t.Fatalf("failed to list objects: %v", err)
  2222  	}
  2223  
  2224  	options.ResourceVersion = result3.ResourceVersion
  2225  	options.ResourceVersionMatch = metav1.ResourceVersionMatchExact
  2226  
  2227  	result4 := example.PodList{}
  2228  	if err := store.GetList(ctx, "/pods", options, &result4); err != nil {
  2229  		t.Fatalf("failed to list objects: %v", err)
  2230  	}
  2231  
  2232  	expectNoDiff(t, "incorrect lists", result3, result4)
  2233  }
  2234  
  2235  func RunTestGuaranteedUpdate(ctx context.Context, t *testing.T, store InterfaceWithPrefixTransformer, validation KeyValidation) {
  2236  	inputObj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns", UID: "A"}}
  2237  	key := computePodKey(inputObj)
  2238  
  2239  	tests := []struct {
  2240  		name                string
  2241  		key                 string
  2242  		ignoreNotFound      bool
  2243  		precondition        *storage.Preconditions
  2244  		expectNotFoundErr   bool
  2245  		expectInvalidObjErr bool
  2246  		expectNoUpdate      bool
  2247  		transformStale      bool
  2248  		hasSelfLink         bool
  2249  	}{{
  2250  		name:                "non-existing key, ignoreNotFound=false",
  2251  		key:                 "/non-existing",
  2252  		ignoreNotFound:      false,
  2253  		precondition:        nil,
  2254  		expectNotFoundErr:   true,
  2255  		expectInvalidObjErr: false,
  2256  		expectNoUpdate:      false,
  2257  	}, {
  2258  		name:                "non-existing key, ignoreNotFound=true",
  2259  		key:                 "/non-existing",
  2260  		ignoreNotFound:      true,
  2261  		precondition:        nil,
  2262  		expectNotFoundErr:   false,
  2263  		expectInvalidObjErr: false,
  2264  		expectNoUpdate:      false,
  2265  	}, {
  2266  		name:                "existing key",
  2267  		key:                 key,
  2268  		ignoreNotFound:      false,
  2269  		precondition:        nil,
  2270  		expectNotFoundErr:   false,
  2271  		expectInvalidObjErr: false,
  2272  		expectNoUpdate:      false,
  2273  	}, {
  2274  		name:                "same data",
  2275  		key:                 key,
  2276  		ignoreNotFound:      false,
  2277  		precondition:        nil,
  2278  		expectNotFoundErr:   false,
  2279  		expectInvalidObjErr: false,
  2280  		expectNoUpdate:      true,
  2281  	}, {
  2282  		name:                "same data, a selfLink",
  2283  		key:                 key,
  2284  		ignoreNotFound:      false,
  2285  		precondition:        nil,
  2286  		expectNotFoundErr:   false,
  2287  		expectInvalidObjErr: false,
  2288  		expectNoUpdate:      true,
  2289  		hasSelfLink:         true,
  2290  	}, {
  2291  		name:                "same data, stale",
  2292  		key:                 key,
  2293  		ignoreNotFound:      false,
  2294  		precondition:        nil,
  2295  		expectNotFoundErr:   false,
  2296  		expectInvalidObjErr: false,
  2297  		expectNoUpdate:      false,
  2298  		transformStale:      true,
  2299  	}, {
  2300  		name:                "UID match",
  2301  		key:                 key,
  2302  		ignoreNotFound:      false,
  2303  		precondition:        storage.NewUIDPreconditions("A"),
  2304  		expectNotFoundErr:   false,
  2305  		expectInvalidObjErr: false,
  2306  		expectNoUpdate:      true,
  2307  	}, {
  2308  		name:                "UID mismatch",
  2309  		key:                 key,
  2310  		ignoreNotFound:      false,
  2311  		precondition:        storage.NewUIDPreconditions("B"),
  2312  		expectNotFoundErr:   false,
  2313  		expectInvalidObjErr: true,
  2314  		expectNoUpdate:      true,
  2315  	}}
  2316  
  2317  	for i, tt := range tests {
  2318  		t.Run(tt.name, func(t *testing.T) {
  2319  			key, storeObj := testPropagateStore(ctx, t, store, inputObj)
  2320  
  2321  			out := &example.Pod{}
  2322  			annotations := map[string]string{"version": fmt.Sprintf("%d", i)}
  2323  			if tt.expectNoUpdate {
  2324  				annotations = nil
  2325  			}
  2326  
  2327  			if tt.transformStale {
  2328  				revertTransformer := store.UpdatePrefixTransformer(
  2329  					func(transformer *PrefixTransformer) value.Transformer {
  2330  						transformer.stale = true
  2331  						return transformer
  2332  					})
  2333  				defer revertTransformer()
  2334  			}
  2335  
  2336  			version := storeObj.ResourceVersion
  2337  			err := store.GuaranteedUpdate(ctx, tt.key, out, tt.ignoreNotFound, tt.precondition,
  2338  				storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) {
  2339  					if tt.expectNotFoundErr && tt.ignoreNotFound {
  2340  						if pod := obj.(*example.Pod); pod.Name != "" {
  2341  							t.Errorf("%s: expecting zero value, but get=%#v", tt.name, pod)
  2342  						}
  2343  					}
  2344  					pod := *storeObj
  2345  					if tt.hasSelfLink {
  2346  						pod.SelfLink = "testlink"
  2347  					}
  2348  					pod.Annotations = annotations
  2349  					return &pod, nil
  2350  				}), nil)
  2351  
  2352  			if tt.expectNotFoundErr {
  2353  				if err == nil || !storage.IsNotFound(err) {
  2354  					t.Errorf("%s: expecting not found error, but get: %v", tt.name, err)
  2355  				}
  2356  				return
  2357  			}
  2358  			if tt.expectInvalidObjErr {
  2359  				if err == nil || !storage.IsInvalidObj(err) {
  2360  					t.Errorf("%s: expecting invalid UID error, but get: %s", tt.name, err)
  2361  				}
  2362  				return
  2363  			}
  2364  			if err != nil {
  2365  				t.Fatalf("%s: GuaranteedUpdate failed: %v", tt.name, err)
  2366  			}
  2367  			if !reflect.DeepEqual(out.ObjectMeta.Annotations, annotations) {
  2368  				t.Errorf("%s: pod annotations want=%s, get=%s", tt.name, annotations, out.ObjectMeta.Annotations)
  2369  			}
  2370  			if out.SelfLink != "" {
  2371  				t.Errorf("%s: selfLink should not be set", tt.name)
  2372  			}
  2373  
  2374  			// verify that kv pair is not empty after set and that the underlying data matches expectations
  2375  			validation(ctx, t, key)
  2376  
  2377  			switch tt.expectNoUpdate {
  2378  			case true:
  2379  				if version != out.ResourceVersion {
  2380  					t.Errorf("%s: expect no version change, before=%s, after=%s", tt.name, version, out.ResourceVersion)
  2381  				}
  2382  			case false:
  2383  				if version == out.ResourceVersion {
  2384  					t.Errorf("%s: expect version change, but get the same version=%s", tt.name, version)
  2385  				}
  2386  			}
  2387  		})
  2388  	}
  2389  }
  2390  
  2391  func RunTestGuaranteedUpdateWithTTL(ctx context.Context, t *testing.T, store storage.Interface) {
  2392  	input := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}}
  2393  	key := computePodKey(input)
  2394  
  2395  	out := &example.Pod{}
  2396  	err := store.GuaranteedUpdate(ctx, key, out, true, nil,
  2397  		func(_ runtime.Object, _ storage.ResponseMeta) (runtime.Object, *uint64, error) {
  2398  			ttl := uint64(1)
  2399  			return input, &ttl, nil
  2400  		}, nil)
  2401  	if err != nil {
  2402  		t.Fatalf("Create failed: %v", err)
  2403  	}
  2404  
  2405  	w, err := store.Watch(ctx, key, storage.ListOptions{ResourceVersion: out.ResourceVersion, Predicate: storage.Everything})
  2406  	if err != nil {
  2407  		t.Fatalf("Watch failed: %v", err)
  2408  	}
  2409  	testCheckEventType(t, w, watch.Deleted)
  2410  }
  2411  
  2412  func RunTestGuaranteedUpdateChecksStoredData(ctx context.Context, t *testing.T, store InterfaceWithPrefixTransformer) {
  2413  	input := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}}
  2414  	key := computePodKey(input)
  2415  
  2416  	// serialize input into etcd with data that would be normalized by a write -
  2417  	// in this case, leading whitespace
  2418  	revertTransformer := store.UpdatePrefixTransformer(
  2419  		func(transformer *PrefixTransformer) value.Transformer {
  2420  			transformer.prefix = []byte(string(transformer.prefix) + " ")
  2421  			return transformer
  2422  		})
  2423  	_, initial := testPropagateStore(ctx, t, store, input)
  2424  	revertTransformer()
  2425  
  2426  	// this update should write the canonical value to etcd because the new serialization differs
  2427  	// from the stored serialization
  2428  	input.ResourceVersion = initial.ResourceVersion
  2429  	out := &example.Pod{}
  2430  	err := store.GuaranteedUpdate(ctx, key, out, true, nil,
  2431  		func(_ runtime.Object, _ storage.ResponseMeta) (runtime.Object, *uint64, error) {
  2432  			return input, nil, nil
  2433  		}, input)
  2434  	if err != nil {
  2435  		t.Fatalf("Update failed: %v", err)
  2436  	}
  2437  	if out.ResourceVersion == initial.ResourceVersion {
  2438  		t.Errorf("guaranteed update should have updated the serialized data, got %#v", out)
  2439  	}
  2440  
  2441  	lastVersion := out.ResourceVersion
  2442  
  2443  	// this update should not write to etcd because the input matches the stored data
  2444  	input = out
  2445  	out = &example.Pod{}
  2446  	err = store.GuaranteedUpdate(ctx, key, out, true, nil,
  2447  		func(_ runtime.Object, _ storage.ResponseMeta) (runtime.Object, *uint64, error) {
  2448  			return input, nil, nil
  2449  		}, input)
  2450  	if err != nil {
  2451  		t.Fatalf("Update failed: %v", err)
  2452  	}
  2453  	if out.ResourceVersion != lastVersion {
  2454  		t.Errorf("guaranteed update should have short-circuited write, got %#v", out)
  2455  	}
  2456  
  2457  	revertTransformer = store.UpdatePrefixTransformer(
  2458  		func(transformer *PrefixTransformer) value.Transformer {
  2459  			transformer.stale = true
  2460  			return transformer
  2461  		})
  2462  	defer revertTransformer()
  2463  
  2464  	// this update should write to etcd because the transformer reported stale
  2465  	err = store.GuaranteedUpdate(ctx, key, out, true, nil,
  2466  		func(_ runtime.Object, _ storage.ResponseMeta) (runtime.Object, *uint64, error) {
  2467  			return input, nil, nil
  2468  		}, input)
  2469  	if err != nil {
  2470  		t.Fatalf("Update failed: %v", err)
  2471  	}
  2472  	if out.ResourceVersion == lastVersion {
  2473  		t.Errorf("guaranteed update should have written to etcd when transformer reported stale, got %#v", out)
  2474  	}
  2475  }
  2476  
  2477  func RunTestGuaranteedUpdateWithConflict(ctx context.Context, t *testing.T, store storage.Interface) {
  2478  	key, _ := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}})
  2479  
  2480  	errChan := make(chan error, 1)
  2481  	var firstToFinish sync.WaitGroup
  2482  	var secondToEnter sync.WaitGroup
  2483  	firstToFinish.Add(1)
  2484  	secondToEnter.Add(1)
  2485  
  2486  	go func() {
  2487  		err := store.GuaranteedUpdate(ctx, key, &example.Pod{}, false, nil,
  2488  			storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) {
  2489  				pod := obj.(*example.Pod)
  2490  				pod.Name = "foo-1"
  2491  				secondToEnter.Wait()
  2492  				return pod, nil
  2493  			}), nil)
  2494  		firstToFinish.Done()
  2495  		errChan <- err
  2496  	}()
  2497  
  2498  	updateCount := 0
  2499  	err := store.GuaranteedUpdate(ctx, key, &example.Pod{}, false, nil,
  2500  		storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) {
  2501  			if updateCount == 0 {
  2502  				secondToEnter.Done()
  2503  				firstToFinish.Wait()
  2504  			}
  2505  			updateCount++
  2506  			pod := obj.(*example.Pod)
  2507  			pod.Name = "foo-2"
  2508  			return pod, nil
  2509  		}), nil)
  2510  	if err != nil {
  2511  		t.Fatalf("Second GuaranteedUpdate error %#v", err)
  2512  	}
  2513  	if err := <-errChan; err != nil {
  2514  		t.Fatalf("First GuaranteedUpdate error %#v", err)
  2515  	}
  2516  
  2517  	if updateCount != 2 {
  2518  		t.Errorf("Should have conflict and called update func twice")
  2519  	}
  2520  }
  2521  
  2522  func RunTestGuaranteedUpdateWithSuggestionAndConflict(ctx context.Context, t *testing.T, store storage.Interface) {
  2523  	key, originalPod := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}})
  2524  
  2525  	// First, update without a suggestion so originalPod is outdated
  2526  	updatedPod := &example.Pod{}
  2527  	err := store.GuaranteedUpdate(ctx, key, updatedPod, false, nil,
  2528  		storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) {
  2529  			pod := obj.(*example.Pod)
  2530  			pod.Generation = 2
  2531  			return pod, nil
  2532  		}),
  2533  		nil,
  2534  	)
  2535  	if err != nil {
  2536  		t.Fatalf("unexpected error: %v", err)
  2537  	}
  2538  
  2539  	// Second, update using the outdated originalPod as the suggestion. Return a conflict error when
  2540  	// passed originalPod, and make sure that SimpleUpdate is called a second time after a live lookup
  2541  	// with the value of updatedPod.
  2542  	sawConflict := false
  2543  	updatedPod2 := &example.Pod{}
  2544  	err = store.GuaranteedUpdate(ctx, key, updatedPod2, false, nil,
  2545  		storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) {
  2546  			pod := obj.(*example.Pod)
  2547  			if pod.Generation != 2 {
  2548  				if sawConflict {
  2549  					t.Fatalf("unexpected second conflict")
  2550  				}
  2551  				sawConflict = true
  2552  				// simulated stale object - return a conflict
  2553  				return nil, apierrors.NewConflict(example.SchemeGroupVersion.WithResource("pods").GroupResource(), "name", errors.New("foo"))
  2554  			}
  2555  			pod.Generation = 3
  2556  			return pod, nil
  2557  		}),
  2558  		originalPod,
  2559  	)
  2560  	if err != nil {
  2561  		t.Fatalf("unexpected error: %v", err)
  2562  	}
  2563  	if updatedPod2.Generation != 3 {
  2564  		t.Errorf("unexpected pod generation: %q", updatedPod2.Generation)
  2565  	}
  2566  
  2567  	// Third, update using a current version as the suggestion.
  2568  	// Return an error and make sure that SimpleUpdate is NOT called a second time,
  2569  	// since the live lookup shows the suggestion was already up to date.
  2570  	attempts := 0
  2571  	updatedPod3 := &example.Pod{}
  2572  	err = store.GuaranteedUpdate(ctx, key, updatedPod3, false, nil,
  2573  		storage.SimpleUpdate(func(obj runtime.Object) (runtime.Object, error) {
  2574  			pod := obj.(*example.Pod)
  2575  			if pod.Generation != updatedPod2.Generation || pod.ResourceVersion != updatedPod2.ResourceVersion {
  2576  				t.Logf("stale object (rv=%s), expected rv=%s", pod.ResourceVersion, updatedPod2.ResourceVersion)
  2577  			}
  2578  			attempts++
  2579  			return nil, fmt.Errorf("validation or admission error")
  2580  		}),
  2581  		updatedPod2,
  2582  	)
  2583  	if err == nil {
  2584  		t.Fatalf("expected error, got none")
  2585  	}
  2586  	// Implementations of the storage interface are allowed to ignore the suggestion,
  2587  	// in which case two attempts are possible.
  2588  	if attempts > 2 {
  2589  		t.Errorf("update function should have been called at most twice, called %d", attempts)
  2590  	}
  2591  }
  2592  
  2593  func RunTestTransformationFailure(ctx context.Context, t *testing.T, store InterfaceWithPrefixTransformer) {
  2594  	barFirst := &example.Pod{
  2595  		ObjectMeta: metav1.ObjectMeta{Namespace: "first", Name: "bar"},
  2596  		Spec:       DeepEqualSafePodSpec(),
  2597  	}
  2598  	bazSecond := &example.Pod{
  2599  		ObjectMeta: metav1.ObjectMeta{Namespace: "second", Name: "baz"},
  2600  		Spec:       DeepEqualSafePodSpec(),
  2601  	}
  2602  
  2603  	preset := []struct {
  2604  		key       string
  2605  		obj       *example.Pod
  2606  		storedObj *example.Pod
  2607  	}{{
  2608  		key: computePodKey(barFirst),
  2609  		obj: barFirst,
  2610  	}, {
  2611  		key: computePodKey(bazSecond),
  2612  		obj: bazSecond,
  2613  	}}
  2614  	for i, ps := range preset[:1] {
  2615  		preset[i].storedObj = &example.Pod{}
  2616  		err := store.Create(ctx, ps.key, ps.obj, preset[:1][i].storedObj, 0)
  2617  		if err != nil {
  2618  			t.Fatalf("Set failed: %v", err)
  2619  		}
  2620  	}
  2621  
  2622  	// create a second resource with an invalid prefix
  2623  	revertTransformer := store.UpdatePrefixTransformer(
  2624  		func(transformer *PrefixTransformer) value.Transformer {
  2625  			return NewPrefixTransformer([]byte("otherprefix!"), false)
  2626  		})
  2627  	for i, ps := range preset[1:] {
  2628  		preset[1:][i].storedObj = &example.Pod{}
  2629  		err := store.Create(ctx, ps.key, ps.obj, preset[1:][i].storedObj, 0)
  2630  		if err != nil {
  2631  			t.Fatalf("Set failed: %v", err)
  2632  		}
  2633  	}
  2634  	revertTransformer()
  2635  
  2636  	// List should fail
  2637  	var got example.PodList
  2638  	storageOpts := storage.ListOptions{
  2639  		Predicate: storage.Everything,
  2640  		Recursive: true,
  2641  	}
  2642  	if err := store.GetList(ctx, "/pods", storageOpts, &got); !storage.IsInternalError(err) {
  2643  		t.Errorf("Unexpected error %v", err)
  2644  	}
  2645  
  2646  	// Get should fail
  2647  	if err := store.Get(ctx, preset[1].key, storage.GetOptions{}, &example.Pod{}); !storage.IsInternalError(err) {
  2648  		t.Errorf("Unexpected error: %v", err)
  2649  	}
  2650  
  2651  	updateFunc := func(input runtime.Object, res storage.ResponseMeta) (runtime.Object, *uint64, error) {
  2652  		return input, nil, nil
  2653  	}
  2654  	// GuaranteedUpdate without suggestion should return an error
  2655  	if err := store.GuaranteedUpdate(ctx, preset[1].key, &example.Pod{}, false, nil, updateFunc, nil); !storage.IsInternalError(err) {
  2656  		t.Errorf("Unexpected error: %v", err)
  2657  	}
  2658  	// GuaranteedUpdate with suggestion should return an error if we don't change the object
  2659  	if err := store.GuaranteedUpdate(ctx, preset[1].key, &example.Pod{}, false, nil, updateFunc, preset[1].obj); err == nil {
  2660  		t.Errorf("Unexpected error: %v", err)
  2661  	}
  2662  
  2663  	// Delete fails with internal error.
  2664  	if err := store.Delete(ctx, preset[1].key, &example.Pod{}, nil, storage.ValidateAllObjectFunc, nil); !storage.IsInternalError(err) {
  2665  		t.Errorf("Unexpected error: %v", err)
  2666  	}
  2667  	if err := store.Get(ctx, preset[1].key, storage.GetOptions{}, &example.Pod{}); !storage.IsInternalError(err) {
  2668  		t.Errorf("Unexpected error: %v", err)
  2669  	}
  2670  }
  2671  
  2672  func RunTestCount(ctx context.Context, t *testing.T, store storage.Interface) {
  2673  	resourceA := "/foo.bar.io/abc"
  2674  
  2675  	// resourceA is intentionally a prefix of resourceB to ensure that the count
  2676  	// for resourceA does not include any objects from resourceB.
  2677  	resourceB := fmt.Sprintf("%sdef", resourceA)
  2678  
  2679  	resourceACountExpected := 5
  2680  	for i := 1; i <= resourceACountExpected; i++ {
  2681  		obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("foo-%d", i)}}
  2682  
  2683  		key := fmt.Sprintf("%s/%d", resourceA, i)
  2684  		if err := store.Create(ctx, key, obj, nil, 0); err != nil {
  2685  			t.Fatalf("Create failed: %v", err)
  2686  		}
  2687  	}
  2688  
  2689  	resourceBCount := 4
  2690  	for i := 1; i <= resourceBCount; i++ {
  2691  		obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("foo-%d", i)}}
  2692  
  2693  		key := fmt.Sprintf("%s/%d", resourceB, i)
  2694  		if err := store.Create(ctx, key, obj, nil, 0); err != nil {
  2695  			t.Fatalf("Create failed: %v", err)
  2696  		}
  2697  	}
  2698  
  2699  	resourceACountGot, err := store.Count(resourceA)
  2700  	if err != nil {
  2701  		t.Fatalf("store.Count failed: %v", err)
  2702  	}
  2703  
  2704  	// count for resourceA should not include the objects for resourceB
  2705  	// even though resourceA is a prefix of resourceB.
  2706  	if int64(resourceACountExpected) != resourceACountGot {
  2707  		t.Fatalf("store.Count for resource %s: expected %d but got %d", resourceA, resourceACountExpected, resourceACountGot)
  2708  	}
  2709  }