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