k8s.io/apiserver@v0.31.1/pkg/storage/testing/watcher_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  	"fmt"
    22  	"net/http"
    23  	"sync"
    24  	"testing"
    25  	"time"
    26  
    27  	"github.com/stretchr/testify/require"
    28  
    29  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    30  	"k8s.io/apimachinery/pkg/api/errors"
    31  	"k8s.io/apimachinery/pkg/api/meta"
    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/util/wait"
    37  	"k8s.io/apimachinery/pkg/watch"
    38  	"k8s.io/apiserver/pkg/apis/example"
    39  	genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
    40  	"k8s.io/apiserver/pkg/features"
    41  	"k8s.io/apiserver/pkg/storage"
    42  	"k8s.io/apiserver/pkg/storage/value"
    43  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    44  	utilflowcontrol "k8s.io/apiserver/pkg/util/flowcontrol"
    45  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    46  	"k8s.io/utils/pointer"
    47  )
    48  
    49  func RunTestWatch(ctx context.Context, t *testing.T, store storage.Interface) {
    50  	testWatch(ctx, t, store, false)
    51  	testWatch(ctx, t, store, true)
    52  }
    53  
    54  // It tests that
    55  // - first occurrence of objects should notify Add event
    56  // - update should trigger Modified event
    57  // - update that gets filtered should trigger Deleted event
    58  func testWatch(ctx context.Context, t *testing.T, store storage.Interface, recursive bool) {
    59  	basePod := &example.Pod{
    60  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
    61  		Spec:       example.PodSpec{NodeName: ""},
    62  	}
    63  	basePodAssigned := &example.Pod{
    64  		ObjectMeta: metav1.ObjectMeta{Name: "foo"},
    65  		Spec:       example.PodSpec{NodeName: "bar"},
    66  	}
    67  
    68  	selectedPod := func(pod *example.Pod) *example.Pod {
    69  		result := pod.DeepCopy()
    70  		result.Labels = map[string]string{"select": "true"}
    71  		return result
    72  	}
    73  
    74  	tests := []struct {
    75  		name       string
    76  		namespace  string
    77  		key        string
    78  		pred       storage.SelectionPredicate
    79  		watchTests []*testWatchStruct
    80  	}{{
    81  		name:       "create a key",
    82  		namespace:  fmt.Sprintf("test-ns-1-%t", recursive),
    83  		watchTests: []*testWatchStruct{{basePod, true, watch.Added}},
    84  		pred:       storage.Everything,
    85  	}, {
    86  		name:       "key updated to match predicate",
    87  		namespace:  fmt.Sprintf("test-ns-2-%t", recursive),
    88  		watchTests: []*testWatchStruct{{basePod, false, ""}, {basePodAssigned, true, watch.Added}},
    89  		pred: storage.SelectionPredicate{
    90  			Label: labels.Everything(),
    91  			Field: fields.ParseSelectorOrDie("spec.nodeName=bar"),
    92  			GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
    93  				pod := obj.(*example.Pod)
    94  				return nil, fields.Set{"spec.nodeName": pod.Spec.NodeName}, nil
    95  			},
    96  		},
    97  	}, {
    98  		name:       "update",
    99  		namespace:  fmt.Sprintf("test-ns-3-%t", recursive),
   100  		watchTests: []*testWatchStruct{{basePod, true, watch.Added}, {basePodAssigned, true, watch.Modified}},
   101  		pred:       storage.Everything,
   102  	}, {
   103  		name:       "delete because of being filtered",
   104  		namespace:  fmt.Sprintf("test-ns-4-%t", recursive),
   105  		watchTests: []*testWatchStruct{{basePod, true, watch.Added}, {basePodAssigned, true, watch.Deleted}},
   106  		pred: storage.SelectionPredicate{
   107  			Label: labels.Everything(),
   108  			Field: fields.ParseSelectorOrDie("spec.nodeName!=bar"),
   109  			GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
   110  				pod := obj.(*example.Pod)
   111  				return nil, fields.Set{"spec.nodeName": pod.Spec.NodeName}, nil
   112  			},
   113  		},
   114  	}, {
   115  		name:      "filtering",
   116  		namespace: fmt.Sprintf("test-ns-5-%t", recursive),
   117  		watchTests: []*testWatchStruct{
   118  			{selectedPod(basePod), true, watch.Added},
   119  			{basePod, true, watch.Deleted},
   120  			{selectedPod(basePod), true, watch.Added},
   121  			{selectedPod(basePodAssigned), true, watch.Modified},
   122  			{nil, true, watch.Deleted},
   123  		},
   124  		pred: storage.SelectionPredicate{
   125  			Label: labels.SelectorFromSet(labels.Set{"select": "true"}),
   126  			Field: fields.Everything(),
   127  			GetAttrs: func(obj runtime.Object) (labels.Set, fields.Set, error) {
   128  				pod := obj.(*example.Pod)
   129  				return labels.Set(pod.Labels), nil, nil
   130  			},
   131  		},
   132  	}}
   133  	for _, tt := range tests {
   134  		t.Run(tt.name, func(t *testing.T) {
   135  			watchKey := fmt.Sprintf("/pods/%s", tt.namespace)
   136  			key := watchKey + "/foo"
   137  			if !recursive {
   138  				watchKey = key
   139  			}
   140  
   141  			// Get the current RV from which we can start watching.
   142  			out := &example.PodList{}
   143  			if err := store.GetList(ctx, watchKey, storage.ListOptions{ResourceVersion: "", Predicate: tt.pred, Recursive: recursive}, out); err != nil {
   144  				t.Fatalf("List failed: %v", err)
   145  			}
   146  
   147  			w, err := store.Watch(ctx, watchKey, storage.ListOptions{ResourceVersion: out.ResourceVersion, Predicate: tt.pred, Recursive: recursive})
   148  			if err != nil {
   149  				t.Fatalf("Watch failed: %v", err)
   150  			}
   151  
   152  			// Create a pod in a different namespace first to ensure
   153  			// that its corresponding event will not be propagated.
   154  			badKey := fmt.Sprintf("/pods/%s-bad/foo", tt.namespace)
   155  			badOut := &example.Pod{}
   156  			err = store.GuaranteedUpdate(ctx, badKey, badOut, true, nil, storage.SimpleUpdate(
   157  				func(runtime.Object) (runtime.Object, error) {
   158  					obj := basePod.DeepCopy()
   159  					obj.Namespace = fmt.Sprintf("%s-bad", tt.namespace)
   160  					return obj, nil
   161  				}), nil)
   162  			if err != nil {
   163  				t.Fatalf("GuaranteedUpdate of bad pod failed: %v", err)
   164  			}
   165  
   166  			var prevObj *example.Pod
   167  			for _, watchTest := range tt.watchTests {
   168  				out := &example.Pod{}
   169  				if watchTest.obj != nil {
   170  					err := store.GuaranteedUpdate(ctx, key, out, true, nil, storage.SimpleUpdate(
   171  						func(runtime.Object) (runtime.Object, error) {
   172  							obj := watchTest.obj.DeepCopy()
   173  							obj.Namespace = tt.namespace
   174  							return obj, nil
   175  						}), nil)
   176  					if err != nil {
   177  						t.Fatalf("GuaranteedUpdate failed: %v", err)
   178  					}
   179  				} else {
   180  					err := store.Delete(ctx, key, out, nil, storage.ValidateAllObjectFunc, nil)
   181  					if err != nil {
   182  						t.Fatalf("Delete failed: %v", err)
   183  					}
   184  				}
   185  				if watchTest.expectEvent {
   186  					expectObj := out
   187  					if watchTest.watchType == watch.Deleted {
   188  						expectObj = prevObj
   189  						expectObj.ResourceVersion = out.ResourceVersion
   190  					}
   191  					testCheckResult(t, w, watch.Event{Type: watchTest.watchType, Object: expectObj})
   192  				}
   193  				prevObj = out
   194  			}
   195  			w.Stop()
   196  			testCheckStop(t, w)
   197  		})
   198  	}
   199  }
   200  
   201  // RunTestWatchFromZero tests that
   202  //   - watch from 0 should sync up and grab the object added before
   203  //   - For testing with etcd, watch from 0 is able to return events for objects
   204  //     whose previous version has been compacted. If testing with cacher, we
   205  //     expect compaction to be nil.
   206  func RunTestWatchFromZero(ctx context.Context, t *testing.T, store storage.Interface, compaction Compaction) {
   207  	key, storedObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}})
   208  
   209  	w, err := store.Watch(ctx, key, storage.ListOptions{ResourceVersion: "0", Predicate: storage.Everything})
   210  	if err != nil {
   211  		t.Fatalf("Watch failed: %v", err)
   212  	}
   213  	testCheckResult(t, w, watch.Event{Type: watch.Added, Object: storedObj})
   214  
   215  	// Update
   216  	out := &example.Pod{}
   217  	err = store.GuaranteedUpdate(ctx, key, out, true, nil, storage.SimpleUpdate(
   218  		func(runtime.Object) (runtime.Object, error) {
   219  			return &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns", Annotations: map[string]string{"a": "1"}}}, nil
   220  		}), nil)
   221  	if err != nil {
   222  		t.Fatalf("GuaranteedUpdate failed: %v", err)
   223  	}
   224  
   225  	// Check that we receive a modified watch event. This check also
   226  	// indirectly ensures that the cache is synced. This is important
   227  	// when testing with the Cacher since we may have to allow for slow
   228  	// processing by allowing updates to propagate to the watch cache.
   229  	// This allows for that.
   230  	testCheckResult(t, w, watch.Event{Type: watch.Modified, Object: out})
   231  	w.Stop()
   232  
   233  	// Make sure when we watch from 0 we receive an ADDED event
   234  	w, err = store.Watch(ctx, key, storage.ListOptions{ResourceVersion: "0", Predicate: storage.Everything})
   235  	if err != nil {
   236  		t.Fatalf("Watch failed: %v", err)
   237  	}
   238  
   239  	testCheckResult(t, w, watch.Event{Type: watch.Added, Object: out})
   240  	w.Stop()
   241  
   242  	// Compact previous versions
   243  	if compaction == nil {
   244  		t.Skip("compaction callback not provided")
   245  	}
   246  
   247  	// Update again
   248  	newOut := &example.Pod{}
   249  	err = store.GuaranteedUpdate(ctx, key, newOut, true, nil, storage.SimpleUpdate(
   250  		func(runtime.Object) (runtime.Object, error) {
   251  			return &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}}, nil
   252  		}), nil)
   253  	if err != nil {
   254  		t.Fatalf("GuaranteedUpdate failed: %v", err)
   255  	}
   256  
   257  	// Compact previous versions
   258  	compaction(ctx, t, newOut.ResourceVersion)
   259  
   260  	// Make sure we can still watch from 0 and receive an ADDED event
   261  	w, err = store.Watch(ctx, key, storage.ListOptions{ResourceVersion: "0", Predicate: storage.Everything})
   262  	defer w.Stop()
   263  	if err != nil {
   264  		t.Fatalf("Watch failed: %v", err)
   265  	}
   266  	testCheckResult(t, w, watch.Event{Type: watch.Added, Object: newOut})
   267  
   268  	// Make sure we can't watch from older resource versions anymoer and get a "Gone" error.
   269  	tooOldWatcher, err := store.Watch(ctx, key, storage.ListOptions{ResourceVersion: out.ResourceVersion, Predicate: storage.Everything})
   270  	if err != nil {
   271  		t.Fatalf("Watch failed: %v", err)
   272  	}
   273  	defer tooOldWatcher.Stop()
   274  	expiredError := errors.NewResourceExpired("").ErrStatus
   275  	// TODO(wojtek-t): It seems that etcd is currently returning a different error,
   276  	// being an Internal error of "etcd event received with PrevKv=nil".
   277  	// We temporary allow both but we should unify here.
   278  	internalError := metav1.Status{
   279  		Status: metav1.StatusFailure,
   280  		Code:   http.StatusInternalServerError,
   281  		Reason: metav1.StatusReasonInternalError,
   282  	}
   283  	testCheckResultFunc(t, tooOldWatcher, func(actualEvent watch.Event) {
   284  		expectNoDiff(t, "incorrect event type", watch.Error, actualEvent.Type)
   285  		if !apiequality.Semantic.DeepDerivative(&expiredError, actualEvent.Object) && !apiequality.Semantic.DeepDerivative(&internalError, actualEvent.Object) {
   286  			t.Errorf("expected: %#v; got %#v", &expiredError, actualEvent.Object)
   287  		}
   288  	})
   289  }
   290  
   291  func RunTestDeleteTriggerWatch(ctx context.Context, t *testing.T, store storage.Interface) {
   292  	key, storedObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}})
   293  	w, err := store.Watch(ctx, key, storage.ListOptions{ResourceVersion: storedObj.ResourceVersion, Predicate: storage.Everything})
   294  	if err != nil {
   295  		t.Fatalf("Watch failed: %v", err)
   296  	}
   297  	if err := store.Delete(ctx, key, &example.Pod{}, nil, storage.ValidateAllObjectFunc, nil); err != nil {
   298  		t.Fatalf("Delete failed: %v", err)
   299  	}
   300  	testCheckEventType(t, w, watch.Deleted)
   301  }
   302  
   303  func RunTestWatchFromNonZero(ctx context.Context, t *testing.T, store storage.Interface) {
   304  	key, storedObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}})
   305  
   306  	w, err := store.Watch(ctx, key, storage.ListOptions{ResourceVersion: storedObj.ResourceVersion, Predicate: storage.Everything})
   307  	if err != nil {
   308  		t.Fatalf("Watch failed: %v", err)
   309  	}
   310  	out := &example.Pod{}
   311  	store.GuaranteedUpdate(ctx, key, out, true, nil, storage.SimpleUpdate(
   312  		func(runtime.Object) (runtime.Object, error) {
   313  			newObj := storedObj.DeepCopy()
   314  			newObj.Annotations = map[string]string{"version": "2"}
   315  			return newObj, nil
   316  		}), nil)
   317  	testCheckResult(t, w, watch.Event{Type: watch.Modified, Object: out})
   318  }
   319  
   320  func RunTestDelayedWatchDelivery(ctx context.Context, t *testing.T, store storage.Interface) {
   321  	_, storedObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}})
   322  	startRV := storedObj.ResourceVersion
   323  
   324  	watcher, err := store.Watch(ctx, "/pods/test-ns", storage.ListOptions{ResourceVersion: startRV, Predicate: storage.Everything, Recursive: true})
   325  	if err != nil {
   326  		t.Fatalf("Unexpected error: %v", err)
   327  	}
   328  
   329  	// Depending on the implementation, different number of events that
   330  	// should be delivered to the watcher can be created before it will
   331  	// block the implementation and as a result force the watcher to be
   332  	// closed (as otherwise events would have to be dropped).
   333  	// For now, this number is smallest for Cacher and it equals 21 for it.
   334  	totalPods := 21
   335  	for i := 0; i < totalPods; i++ {
   336  		out := &example.Pod{}
   337  		pod := &example.Pod{
   338  			ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("foo-%d", i), Namespace: "test-ns"},
   339  		}
   340  		err := store.GuaranteedUpdate(ctx, computePodKey(pod), out, true, nil, storage.SimpleUpdate(
   341  			func(runtime.Object) (runtime.Object, error) {
   342  				return pod, nil
   343  			}), nil)
   344  		if err != nil {
   345  			t.Errorf("GuaranteedUpdate failed: %v", err)
   346  		}
   347  	}
   348  
   349  	// Now stop the watcher and check if the consecutive events are being delivered.
   350  	watcher.Stop()
   351  
   352  	watched := 0
   353  	for {
   354  		event, ok := <-watcher.ResultChan()
   355  		if !ok {
   356  			break
   357  		}
   358  		object := event.Object
   359  		if co, ok := object.(runtime.CacheableObject); ok {
   360  			object = co.GetObject()
   361  		}
   362  		if a, e := object.(*example.Pod).Name, fmt.Sprintf("foo-%d", watched); e != a {
   363  			t.Errorf("Unexpected object watched: %s, expected %s", a, e)
   364  		}
   365  		watched++
   366  	}
   367  	// We expect at least N events to be delivered, depending on the implementation.
   368  	// For now, this number is smallest for Cacher and it equals 10 (size of the out buffer).
   369  	if watched < 10 {
   370  		t.Errorf("Unexpected number of events: %v, expected: %v", watched, totalPods)
   371  	}
   372  }
   373  
   374  func RunTestWatchError(ctx context.Context, t *testing.T, store InterfaceWithPrefixTransformer) {
   375  	obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}}
   376  	key := computePodKey(obj)
   377  
   378  	// Compute the initial resource version from which we can start watching later.
   379  	list := &example.PodList{}
   380  	storageOpts := storage.ListOptions{
   381  		ResourceVersion: "0",
   382  		Predicate:       storage.Everything,
   383  		Recursive:       true,
   384  	}
   385  	if err := store.GetList(ctx, "/pods", storageOpts, list); err != nil {
   386  		t.Errorf("Unexpected error: %v", err)
   387  	}
   388  
   389  	if err := store.GuaranteedUpdate(ctx, key, &example.Pod{}, true, nil, storage.SimpleUpdate(
   390  		func(runtime.Object) (runtime.Object, error) {
   391  			return obj, nil
   392  		}), nil); err != nil {
   393  		t.Fatalf("GuaranteedUpdate failed: %v", err)
   394  	}
   395  
   396  	// Now trigger watch error by injecting failing transformer.
   397  	revertTransformer := store.UpdatePrefixTransformer(
   398  		func(previousTransformer *PrefixTransformer) value.Transformer {
   399  			return &failingTransformer{}
   400  		})
   401  	defer revertTransformer()
   402  
   403  	w, err := store.Watch(ctx, key, storage.ListOptions{ResourceVersion: list.ResourceVersion, Predicate: storage.Everything})
   404  	if err != nil {
   405  		t.Fatalf("Watch failed: %v", err)
   406  	}
   407  	testCheckEventType(t, w, watch.Error)
   408  }
   409  
   410  func RunTestWatchContextCancel(ctx context.Context, t *testing.T, store storage.Interface) {
   411  	canceledCtx, cancel := context.WithCancel(ctx)
   412  	cancel()
   413  	// When we watch with a canceled context, we should detect that it's context canceled.
   414  	// We won't take it as error and also close the watcher.
   415  	w, err := store.Watch(canceledCtx, "/pods/not-existing", storage.ListOptions{
   416  		ResourceVersion: "0",
   417  		Predicate:       storage.Everything,
   418  	})
   419  	if err != nil {
   420  		t.Fatal(err)
   421  	}
   422  
   423  	select {
   424  	case _, ok := <-w.ResultChan():
   425  		if ok {
   426  			t.Error("ResultChan() should be closed")
   427  		}
   428  	case <-time.After(wait.ForeverTestTimeout):
   429  		t.Errorf("timeout after %v", wait.ForeverTestTimeout)
   430  	}
   431  }
   432  
   433  func RunTestWatcherTimeout(ctx context.Context, t *testing.T, store storage.Interface) {
   434  	// initialRV is used to initate the watcher at the beginning of the world.
   435  	podList := example.PodList{}
   436  	options := storage.ListOptions{
   437  		Predicate: storage.Everything,
   438  		Recursive: true,
   439  	}
   440  	if err := store.GetList(ctx, "/pods", options, &podList); err != nil {
   441  		t.Fatalf("Failed to list pods: %v", err)
   442  	}
   443  	initialRV := podList.ResourceVersion
   444  
   445  	options = storage.ListOptions{
   446  		ResourceVersion: initialRV,
   447  		Predicate:       storage.Everything,
   448  		Recursive:       true,
   449  	}
   450  
   451  	// Create a number of watchers that will not be reading any result.
   452  	nonReadingWatchers := 50
   453  	for i := 0; i < nonReadingWatchers; i++ {
   454  		watcher, err := store.Watch(ctx, "/pods/test-ns", options)
   455  		if err != nil {
   456  			t.Fatalf("Unexpected error: %v", err)
   457  		}
   458  		defer watcher.Stop()
   459  	}
   460  
   461  	// Create a second watcher that will be reading result.
   462  	readingWatcher, err := store.Watch(ctx, "/pods/test-ns", options)
   463  	if err != nil {
   464  		t.Fatalf("Unexpected error: %v", err)
   465  	}
   466  	defer readingWatcher.Stop()
   467  
   468  	// Depending on the implementation, different number of events that
   469  	// should be delivered to the watcher can be created before it will
   470  	// block the implementation and as a result force the watcher to be
   471  	// closed (as otherwise events would have to be dropped).
   472  	// For now, this number is smallest for Cacher and it equals 21 for it.
   473  	//
   474  	// Create more events to ensure that we're not blocking other watchers
   475  	// forever.
   476  	startTime := time.Now()
   477  	for i := 0; i < 22; i++ {
   478  		out := &example.Pod{}
   479  		pod := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("foo-%d", i), Namespace: "test-ns"}}
   480  		if err := store.Create(ctx, computePodKey(pod), pod, out, 0); err != nil {
   481  			t.Fatalf("Create failed: %v", err)
   482  		}
   483  		testCheckResult(t, readingWatcher, watch.Event{Type: watch.Added, Object: out})
   484  	}
   485  	if time.Since(startTime) > time.Duration(250*nonReadingWatchers)*time.Millisecond {
   486  		t.Errorf("waiting for events took too long: %v", time.Since(startTime))
   487  	}
   488  }
   489  
   490  func RunTestWatchDeleteEventObjectHaveLatestRV(ctx context.Context, t *testing.T, store storage.Interface) {
   491  	key, storedObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}})
   492  
   493  	watchCtx, cancel := context.WithTimeout(ctx, wait.ForeverTestTimeout)
   494  	t.Cleanup(cancel)
   495  	w, err := store.Watch(watchCtx, key, storage.ListOptions{ResourceVersion: storedObj.ResourceVersion, Predicate: storage.Everything})
   496  	if err != nil {
   497  		t.Fatalf("Watch failed: %v", err)
   498  	}
   499  
   500  	deletedObj := &example.Pod{}
   501  	if err := store.Delete(ctx, key, deletedObj, &storage.Preconditions{}, storage.ValidateAllObjectFunc, nil); err != nil {
   502  		t.Fatalf("Delete failed: %v", err)
   503  	}
   504  
   505  	// Verify that ResourceVersion has changed on deletion.
   506  	if storedObj.ResourceVersion == deletedObj.ResourceVersion {
   507  		t.Fatalf("ResourceVersion didn't changed on deletion: %s", deletedObj.ResourceVersion)
   508  	}
   509  
   510  	testCheckResult(t, w, watch.Event{Type: watch.Deleted, Object: deletedObj})
   511  }
   512  
   513  func RunTestWatchInitializationSignal(ctx context.Context, t *testing.T, store storage.Interface) {
   514  	ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
   515  	t.Cleanup(cancel)
   516  	initSignal := utilflowcontrol.NewInitializationSignal()
   517  	ctx = utilflowcontrol.WithInitializationSignal(ctx, initSignal)
   518  
   519  	key, storedObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}})
   520  	_, err := store.Watch(ctx, key, storage.ListOptions{ResourceVersion: storedObj.ResourceVersion, Predicate: storage.Everything})
   521  	if err != nil {
   522  		t.Fatalf("Watch failed: %v", err)
   523  	}
   524  
   525  	initSignal.Wait()
   526  }
   527  
   528  // RunOptionalTestProgressNotify tests ProgressNotify feature of ListOptions.
   529  // Given this feature is currently not explicitly used by higher layers of Kubernetes
   530  // (it rather is used by wrappers of storage.Interface to implement its functionalities)
   531  // this test is currently considered optional.
   532  func RunOptionalTestProgressNotify(ctx context.Context, t *testing.T, store storage.Interface) {
   533  	input := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "test-ns"}}
   534  	key := computePodKey(input)
   535  	out := &example.Pod{}
   536  	if err := store.Create(ctx, key, input, out, 0); err != nil {
   537  		t.Fatalf("Create failed: %v", err)
   538  	}
   539  	validateResourceVersion := resourceVersionNotOlderThan(out.ResourceVersion)
   540  
   541  	opts := storage.ListOptions{
   542  		ResourceVersion: out.ResourceVersion,
   543  		Predicate:       storage.Everything,
   544  		ProgressNotify:  true,
   545  	}
   546  	w, err := store.Watch(ctx, key, opts)
   547  	if err != nil {
   548  		t.Fatalf("Watch failed: %v", err)
   549  	}
   550  
   551  	// when we send a bookmark event, the client expects the event to contain an
   552  	// object of the correct type, but with no fields set other than the resourceVersion
   553  	testCheckResultFunc(t, w, func(actualEvent watch.Event) {
   554  		expectNoDiff(t, "incorrect event type", watch.Bookmark, actualEvent.Type)
   555  		// first, check that we have the correct resource version
   556  		obj, ok := actualEvent.Object.(metav1.Object)
   557  		if !ok {
   558  			t.Fatalf("got %T, not metav1.Object", actualEvent.Object)
   559  		}
   560  		if err := validateResourceVersion(obj.GetResourceVersion()); err != nil {
   561  			t.Fatal(err)
   562  		}
   563  
   564  		// then, check that we have the right type and content
   565  		pod, ok := actualEvent.Object.(*example.Pod)
   566  		if !ok {
   567  			t.Fatalf("got %T, not *example.Pod", actualEvent.Object)
   568  		}
   569  		pod.ResourceVersion = ""
   570  		expectNoDiff(t, "bookmark event should contain an object with no fields set other than resourceVersion", &example.Pod{}, pod)
   571  	})
   572  }
   573  
   574  // It tests watches of cluster-scoped resources.
   575  func RunTestClusterScopedWatch(ctx context.Context, t *testing.T, store storage.Interface) {
   576  	tests := []struct {
   577  		name string
   578  		// For watch request, the name of object is specified with field selector
   579  		// "metadata.name=objectName". So in this watch tests, we should set the
   580  		// requestedName and field selector "metadata.name=requestedName" at the
   581  		// same time or set neighter of them.
   582  		requestedName string
   583  		recursive     bool
   584  		fieldSelector fields.Selector
   585  		indexFields   []string
   586  		watchTests    []*testWatchStruct
   587  	}{
   588  		{
   589  			name:          "cluster-wide watch, request without name, without field selector",
   590  			recursive:     true,
   591  			fieldSelector: fields.Everything(),
   592  			watchTests: []*testWatchStruct{
   593  				{basePod("t1-foo1"), true, watch.Added},
   594  				{basePodUpdated("t1-foo1"), true, watch.Modified},
   595  				{basePodAssigned("t1-foo2", "t1-bar1"), true, watch.Added},
   596  			},
   597  		},
   598  		{
   599  			name:          "cluster-wide watch, request without name, field selector with spec.nodeName",
   600  			recursive:     true,
   601  			fieldSelector: fields.ParseSelectorOrDie("spec.nodeName=t2-bar1"),
   602  			indexFields:   []string{"spec.nodeName"},
   603  			watchTests: []*testWatchStruct{
   604  				{basePod("t2-foo1"), false, ""},
   605  				{basePodAssigned("t2-foo1", "t2-bar1"), true, watch.Added},
   606  			},
   607  		},
   608  		{
   609  			name:          "cluster-wide watch, request without name, field selector with spec.nodeName to filter out watch",
   610  			recursive:     true,
   611  			fieldSelector: fields.ParseSelectorOrDie("spec.nodeName!=t3-bar1"),
   612  			indexFields:   []string{"spec.nodeName"},
   613  			watchTests: []*testWatchStruct{
   614  				{basePod("t3-foo1"), true, watch.Added},
   615  				{basePod("t3-foo2"), true, watch.Added},
   616  				{basePodUpdated("t3-foo1"), true, watch.Modified},
   617  				{basePodAssigned("t3-foo1", "t3-bar1"), true, watch.Deleted},
   618  			},
   619  		},
   620  		{
   621  			name:          "cluster-wide watch, request with name, field selector with metadata.name",
   622  			requestedName: "t4-foo1",
   623  			fieldSelector: fields.ParseSelectorOrDie("metadata.name=t4-foo1"),
   624  			watchTests: []*testWatchStruct{
   625  				{basePod("t4-foo1"), true, watch.Added},
   626  				{basePod("t4-foo2"), false, ""},
   627  				{basePodUpdated("t4-foo1"), true, watch.Modified},
   628  				{basePodUpdated("t4-foo2"), false, ""},
   629  			},
   630  		},
   631  		{
   632  			name:          "cluster-wide watch, request with name, field selector with metadata.name and spec.nodeName",
   633  			requestedName: "t5-foo1",
   634  			fieldSelector: fields.SelectorFromSet(fields.Set{
   635  				"metadata.name": "t5-foo1",
   636  				"spec.nodeName": "t5-bar1",
   637  			}),
   638  			indexFields: []string{"spec.nodeName"},
   639  			watchTests: []*testWatchStruct{
   640  				{basePod("t5-foo1"), false, ""},
   641  				{basePod("t5-foo2"), false, ""},
   642  				{basePodUpdated("t5-foo1"), false, ""},
   643  				{basePodUpdated("t5-foo2"), false, ""},
   644  				{basePodAssigned("t5-foo1", "t5-bar1"), true, watch.Added},
   645  			},
   646  		},
   647  		{
   648  			name:          "cluster-wide watch, request with name, field selector with metadata.name, and with spec.nodeName to filter out watch",
   649  			requestedName: "t6-foo1",
   650  			fieldSelector: fields.AndSelectors(
   651  				fields.ParseSelectorOrDie("spec.nodeName!=t6-bar1"),
   652  				fields.SelectorFromSet(fields.Set{"metadata.name": "t6-foo1"}),
   653  			),
   654  			indexFields: []string{"spec.nodeName"},
   655  			watchTests: []*testWatchStruct{
   656  				{basePod("t6-foo1"), true, watch.Added},
   657  				{basePod("t6-foo2"), false, ""},
   658  				{basePodUpdated("t6-foo1"), true, watch.Modified},
   659  				{basePodAssigned("t6-foo1", "t6-bar1"), true, watch.Deleted},
   660  				{basePodAssigned("t6-foo2", "t6-bar1"), false, ""},
   661  			},
   662  		},
   663  	}
   664  	for _, tt := range tests {
   665  		t.Run(tt.name, func(t *testing.T) {
   666  			requestInfo := &genericapirequest.RequestInfo{}
   667  			requestInfo.Name = tt.requestedName
   668  			requestInfo.Namespace = ""
   669  			ctx = genericapirequest.WithRequestInfo(ctx, requestInfo)
   670  			ctx = genericapirequest.WithNamespace(ctx, "")
   671  
   672  			watchKey := "/pods"
   673  			if tt.requestedName != "" {
   674  				watchKey += "/" + tt.requestedName
   675  			}
   676  
   677  			predicate := createPodPredicate(tt.fieldSelector, false, tt.indexFields)
   678  
   679  			list := &example.PodList{}
   680  			opts := storage.ListOptions{
   681  				ResourceVersion: "",
   682  				Predicate:       predicate,
   683  				Recursive:       true,
   684  			}
   685  			if err := store.GetList(ctx, "/pods", opts, list); err != nil {
   686  				t.Errorf("Unexpected error: %v", err)
   687  			}
   688  
   689  			opts.ResourceVersion = list.ResourceVersion
   690  			opts.Recursive = tt.recursive
   691  
   692  			w, err := store.Watch(ctx, watchKey, opts)
   693  			if err != nil {
   694  				t.Fatalf("Watch failed: %v", err)
   695  			}
   696  
   697  			currentObjs := map[string]*example.Pod{}
   698  			for _, watchTest := range tt.watchTests {
   699  				out := &example.Pod{}
   700  				key := "pods/" + watchTest.obj.Name
   701  				err := store.GuaranteedUpdate(ctx, key, out, true, nil, storage.SimpleUpdate(
   702  					func(runtime.Object) (runtime.Object, error) {
   703  						obj := watchTest.obj.DeepCopy()
   704  						return obj, nil
   705  					}), nil)
   706  				if err != nil {
   707  					t.Fatalf("GuaranteedUpdate failed: %v", err)
   708  				}
   709  
   710  				expectObj := out
   711  				if watchTest.watchType == watch.Deleted {
   712  					expectObj = currentObjs[watchTest.obj.Name]
   713  					expectObj.ResourceVersion = out.ResourceVersion
   714  					delete(currentObjs, watchTest.obj.Name)
   715  				} else {
   716  					currentObjs[watchTest.obj.Name] = out
   717  				}
   718  				if watchTest.expectEvent {
   719  					testCheckResult(t, w, watch.Event{Type: watchTest.watchType, Object: expectObj})
   720  				}
   721  			}
   722  			w.Stop()
   723  			testCheckStop(t, w)
   724  		})
   725  	}
   726  }
   727  
   728  // It tests watch of namespace-scoped resources.
   729  func RunTestNamespaceScopedWatch(ctx context.Context, t *testing.T, store storage.Interface) {
   730  	tests := []struct {
   731  		name string
   732  		// For watch request, the name of object is specified with field selector
   733  		// "metadata.name=objectName". So in this watch tests, we should set the
   734  		// requestedName and field selector "metadata.name=requestedName" at the
   735  		// same time or set neighter of them.
   736  		requestedName      string
   737  		requestedNamespace string
   738  		recursive          bool
   739  		fieldSelector      fields.Selector
   740  		indexFields        []string
   741  		watchTests         []*testWatchStruct
   742  	}{
   743  		{
   744  			name:          "namespaced watch, request without name, request without namespace, without field selector",
   745  			recursive:     true,
   746  			fieldSelector: fields.Everything(),
   747  			watchTests: []*testWatchStruct{
   748  				{baseNamespacedPod("t1-foo1", "t1-ns1"), true, watch.Added},
   749  				{baseNamespacedPod("t1-foo2", "t1-ns2"), true, watch.Added},
   750  				{baseNamespacedPodUpdated("t1-foo1", "t1-ns1"), true, watch.Modified},
   751  				{baseNamespacedPodUpdated("t1-foo2", "t1-ns2"), true, watch.Modified},
   752  			},
   753  		},
   754  		{
   755  			name:          "namespaced watch, request without name, request without namespace, field selector with metadata.namespace",
   756  			recursive:     true,
   757  			fieldSelector: fields.ParseSelectorOrDie("metadata.namespace=t2-ns1"),
   758  			watchTests: []*testWatchStruct{
   759  				{baseNamespacedPod("t2-foo1", "t2-ns1"), true, watch.Added},
   760  				{baseNamespacedPod("t2-foo1", "t2-ns2"), false, ""},
   761  				{baseNamespacedPodUpdated("t2-foo1", "t2-ns1"), true, watch.Modified},
   762  				{baseNamespacedPodUpdated("t2-foo1", "t2-ns2"), false, ""},
   763  			},
   764  		},
   765  		{
   766  			name:          "namespaced watch, request without name, request without namespace, field selector with spec.nodename",
   767  			recursive:     true,
   768  			fieldSelector: fields.ParseSelectorOrDie("spec.nodeName=t3-bar1"),
   769  			indexFields:   []string{"spec.nodeName"},
   770  			watchTests: []*testWatchStruct{
   771  				{baseNamespacedPod("t3-foo1", "t3-ns1"), false, ""},
   772  				{baseNamespacedPod("t3-foo2", "t3-ns2"), false, ""},
   773  				{baseNamespacedPodAssigned("t3-foo1", "t3-ns1", "t3-bar1"), true, watch.Added},
   774  				{baseNamespacedPodAssigned("t3-foo2", "t3-ns2", "t3-bar1"), true, watch.Added},
   775  			},
   776  		},
   777  		{
   778  			name:          "namespaced watch, request without name, request without namespace, field selector with spec.nodename to filter out watch",
   779  			recursive:     true,
   780  			fieldSelector: fields.ParseSelectorOrDie("spec.nodeName!=t4-bar1"),
   781  			indexFields:   []string{"spec.nodeName"},
   782  			watchTests: []*testWatchStruct{
   783  				{baseNamespacedPod("t4-foo1", "t4-ns1"), true, watch.Added},
   784  				{baseNamespacedPod("t4-foo2", "t4-ns1"), true, watch.Added},
   785  				{baseNamespacedPodUpdated("t4-foo1", "t4-ns1"), true, watch.Modified},
   786  				{baseNamespacedPodAssigned("t4-foo1", "t4-ns1", "t4-bar1"), true, watch.Deleted},
   787  			},
   788  		},
   789  		{
   790  			name:               "namespaced watch, request without name, request with namespace, without field selector",
   791  			requestedNamespace: "t5-ns1",
   792  			recursive:          true,
   793  			fieldSelector:      fields.Everything(),
   794  			watchTests: []*testWatchStruct{
   795  				{baseNamespacedPod("t5-foo1", "t5-ns1"), true, watch.Added},
   796  				{baseNamespacedPod("t5-foo1", "t5-ns2"), false, ""},
   797  				{baseNamespacedPod("t5-foo2", "t5-ns1"), true, watch.Added},
   798  				{baseNamespacedPodUpdated("t5-foo1", "t5-ns1"), true, watch.Modified},
   799  				{baseNamespacedPodUpdated("t5-foo1", "t5-ns2"), false, ""},
   800  			},
   801  		},
   802  		{
   803  			name:               "namespaced watch, request without name, request with namespace, field selector with matched metadata.namespace",
   804  			requestedNamespace: "t6-ns1",
   805  			recursive:          true,
   806  			fieldSelector:      fields.ParseSelectorOrDie("metadata.namespace=t6-ns1"),
   807  			watchTests: []*testWatchStruct{
   808  				{baseNamespacedPod("t6-foo1", "t6-ns1"), true, watch.Added},
   809  				{baseNamespacedPod("t6-foo1", "t6-ns2"), false, ""},
   810  				{baseNamespacedPodUpdated("t6-foo1", "t6-ns1"), true, watch.Modified},
   811  			},
   812  		},
   813  		{
   814  			name:               "namespaced watch, request without name, request with namespace, field selector with non-matched metadata.namespace",
   815  			requestedNamespace: "t7-ns1",
   816  			recursive:          true,
   817  			fieldSelector:      fields.ParseSelectorOrDie("metadata.namespace=t7-ns2"),
   818  			watchTests: []*testWatchStruct{
   819  				{baseNamespacedPod("t7-foo1", "t7-ns1"), false, ""},
   820  				{baseNamespacedPod("t7-foo1", "t7-ns2"), false, ""},
   821  				{baseNamespacedPodUpdated("t7-foo1", "t7-ns1"), false, ""},
   822  				{baseNamespacedPodUpdated("t7-foo1", "t7-ns2"), false, ""},
   823  			},
   824  		},
   825  		{
   826  			name:               "namespaced watch, request without name, request with namespace, field selector with spec.nodename",
   827  			requestedNamespace: "t8-ns1",
   828  			recursive:          true,
   829  			fieldSelector:      fields.ParseSelectorOrDie("spec.nodeName=t8-bar2"),
   830  			indexFields:        []string{"spec.nodeName"},
   831  			watchTests: []*testWatchStruct{
   832  				{baseNamespacedPod("t8-foo1", "t8-ns1"), false, ""},
   833  				{baseNamespacedPodAssigned("t8-foo1", "t8-ns1", "t8-bar1"), false, ""},
   834  				{baseNamespacedPodAssigned("t8-foo1", "t8-ns2", "t8-bar2"), false, ""},
   835  				{baseNamespacedPodAssigned("t8-foo1", "t8-ns1", "t8-bar2"), true, watch.Added},
   836  			},
   837  		},
   838  		{
   839  			name:               "namespaced watch, request without name, request with namespace, field selector with spec.nodename to filter out watch",
   840  			requestedNamespace: "t9-ns2",
   841  			recursive:          true,
   842  			fieldSelector:      fields.ParseSelectorOrDie("spec.nodeName!=t9-bar1"),
   843  			indexFields:        []string{"spec.nodeName"},
   844  			watchTests: []*testWatchStruct{
   845  				{baseNamespacedPod("t9-foo1", "t9-ns1"), false, ""},
   846  				{baseNamespacedPod("t9-foo1", "t9-ns2"), true, watch.Added},
   847  				{baseNamespacedPodAssigned("t9-foo1", "t9-ns2", "t9-bar1"), true, watch.Deleted},
   848  				{baseNamespacedPodAssigned("t9-foo1", "t9-ns2", "t9-bar2"), true, watch.Added},
   849  			},
   850  		},
   851  		{
   852  			name:          "namespaced watch, request with name, request without namespace, field selector with metadata.name",
   853  			requestedName: "t10-foo1",
   854  			recursive:     true,
   855  			fieldSelector: fields.ParseSelectorOrDie("metadata.name=t10-foo1"),
   856  			watchTests: []*testWatchStruct{
   857  				{baseNamespacedPod("t10-foo1", "t10-ns1"), true, watch.Added},
   858  				{baseNamespacedPod("t10-foo1", "t10-ns2"), true, watch.Added},
   859  				{baseNamespacedPod("t10-foo2", "t10-ns1"), false, ""},
   860  				{baseNamespacedPodUpdated("t10-foo1", "t10-ns1"), true, watch.Modified},
   861  				{baseNamespacedPodAssigned("t10-foo1", "t10-ns1", "t10-bar1"), true, watch.Modified},
   862  			},
   863  		},
   864  		{
   865  			name:          "namespaced watch, request with name, request without namespace, field selector with metadata.name and metadata.namespace",
   866  			requestedName: "t11-foo1",
   867  			recursive:     true,
   868  			fieldSelector: fields.SelectorFromSet(fields.Set{
   869  				"metadata.name":      "t11-foo1",
   870  				"metadata.namespace": "t11-ns1",
   871  			}),
   872  			watchTests: []*testWatchStruct{
   873  				{baseNamespacedPod("t11-foo1", "t11-ns1"), true, watch.Added},
   874  				{baseNamespacedPod("t11-foo2", "t11-ns1"), false, ""},
   875  				{baseNamespacedPod("t11-foo1", "t11-ns2"), false, ""},
   876  				{baseNamespacedPodUpdated("t11-foo1", "t11-ns1"), true, watch.Modified},
   877  				{baseNamespacedPodAssigned("t11-foo1", "t11-ns1", "t11-bar1"), true, watch.Modified},
   878  			},
   879  		},
   880  		{
   881  			name:          "namespaced watch, request with name, request without namespace, field selector with metadata.name and spec.nodeName",
   882  			requestedName: "t12-foo1",
   883  			recursive:     true,
   884  			fieldSelector: fields.SelectorFromSet(fields.Set{
   885  				"metadata.name": "t12-foo1",
   886  				"spec.nodeName": "t12-bar1",
   887  			}),
   888  			indexFields: []string{"spec.nodeName"},
   889  			watchTests: []*testWatchStruct{
   890  				{baseNamespacedPod("t12-foo1", "t12-ns1"), false, ""},
   891  				{baseNamespacedPodUpdated("t12-foo1", "t12-ns1"), false, ""},
   892  				{baseNamespacedPodAssigned("t12-foo1", "t12-ns1", "t12-bar1"), true, watch.Added},
   893  			},
   894  		},
   895  		{
   896  			name:          "namespaced watch, request with name, request without namespace, field selector with metadata.name, and with spec.nodeName to filter out watch",
   897  			requestedName: "t15-foo1",
   898  			recursive:     true,
   899  			fieldSelector: fields.AndSelectors(
   900  				fields.ParseSelectorOrDie("spec.nodeName!=t15-bar1"),
   901  				fields.SelectorFromSet(fields.Set{"metadata.name": "t15-foo1"}),
   902  			),
   903  			indexFields: []string{"spec.nodeName"},
   904  			watchTests: []*testWatchStruct{
   905  				{baseNamespacedPod("t15-foo1", "t15-ns1"), true, watch.Added},
   906  				{baseNamespacedPod("t15-foo2", "t15-ns1"), false, ""},
   907  				{baseNamespacedPodUpdated("t15-foo1", "t15-ns1"), true, watch.Modified},
   908  				{baseNamespacedPodAssigned("t15-foo1", "t15-ns1", "t15-bar1"), true, watch.Deleted},
   909  				{baseNamespacedPodAssigned("t15-foo1", "t15-ns1", "t15-bar2"), true, watch.Added},
   910  			},
   911  		},
   912  		{
   913  			name:               "namespaced watch, request with name, request with namespace, with field selector metadata.name",
   914  			requestedName:      "t16-foo1",
   915  			requestedNamespace: "t16-ns1",
   916  			fieldSelector:      fields.ParseSelectorOrDie("metadata.name=t16-foo1"),
   917  			watchTests: []*testWatchStruct{
   918  				{baseNamespacedPod("t16-foo1", "t16-ns1"), true, watch.Added},
   919  				{baseNamespacedPod("t16-foo2", "t16-ns1"), false, ""},
   920  				{baseNamespacedPodUpdated("t16-foo1", "t16-ns1"), true, watch.Modified},
   921  				{baseNamespacedPodAssigned("t16-foo1", "t16-ns1", "t16-bar1"), true, watch.Modified},
   922  			},
   923  		},
   924  		{
   925  			name:               "namespaced watch, request with name, request with namespace, with field selector metadata.name and metadata.namespace",
   926  			requestedName:      "t17-foo2",
   927  			requestedNamespace: "t17-ns1",
   928  			fieldSelector: fields.SelectorFromSet(fields.Set{
   929  				"metadata.name":      "t17-foo2",
   930  				"metadata.namespace": "t17-ns1",
   931  			}),
   932  			watchTests: []*testWatchStruct{
   933  				{baseNamespacedPod("t17-foo1", "t17-ns1"), false, ""},
   934  				{baseNamespacedPod("t17-foo2", "t17-ns1"), true, watch.Added},
   935  				{baseNamespacedPodUpdated("t17-foo1", "t17-ns1"), false, ""},
   936  				{baseNamespacedPodAssigned("t17-foo2", "t17-ns1", "t17-bar1"), true, watch.Modified},
   937  			},
   938  		},
   939  		{
   940  			name:               "namespaced watch, request with name, request with namespace, with field selector metadata.name, metadata.namespace and spec.nodename",
   941  			requestedName:      "t18-foo1",
   942  			requestedNamespace: "t18-ns1",
   943  			fieldSelector: fields.SelectorFromSet(fields.Set{
   944  				"metadata.name":      "t18-foo1",
   945  				"metadata.namespace": "t18-ns1",
   946  				"spec.nodeName":      "t18-bar1",
   947  			}),
   948  			indexFields: []string{"spec.nodeName"},
   949  			watchTests: []*testWatchStruct{
   950  				{baseNamespacedPod("t18-foo1", "t18-ns1"), false, ""},
   951  				{baseNamespacedPod("t18-foo2", "t18-ns1"), false, ""},
   952  				{baseNamespacedPod("t18-foo1", "t18-ns2"), false, ""},
   953  				{baseNamespacedPodUpdated("t18-foo1", "t18-ns1"), false, ""},
   954  				{baseNamespacedPodAssigned("t18-foo1", "t18-ns1", "t18-bar1"), true, watch.Added},
   955  			},
   956  		},
   957  		{
   958  			name:               "namespaced watch, request with name, request with namespace, with field selector metadata.name, metadata.namespace, and with spec.nodename to filter out watch",
   959  			requestedName:      "t19-foo2",
   960  			requestedNamespace: "t19-ns1",
   961  			fieldSelector: fields.AndSelectors(
   962  				fields.ParseSelectorOrDie("spec.nodeName!=t19-bar1"),
   963  				fields.SelectorFromSet(fields.Set{"metadata.name": "t19-foo2", "metadata.namespace": "t19-ns1"}),
   964  			),
   965  			indexFields: []string{"spec.nodeName"},
   966  			watchTests: []*testWatchStruct{
   967  				{baseNamespacedPod("t19-foo1", "t19-ns1"), false, ""},
   968  				{baseNamespacedPod("t19-foo2", "t19-ns2"), false, ""},
   969  				{baseNamespacedPod("t19-foo2", "t19-ns1"), true, watch.Added},
   970  				{baseNamespacedPodUpdated("t19-foo2", "t19-ns1"), true, watch.Modified},
   971  				{baseNamespacedPodAssigned("t19-foo2", "t19-ns1", "t19-bar1"), true, watch.Deleted},
   972  			},
   973  		},
   974  	}
   975  	for _, tt := range tests {
   976  		t.Run(tt.name, func(t *testing.T) {
   977  			requestInfo := &genericapirequest.RequestInfo{}
   978  			requestInfo.Name = tt.requestedName
   979  			requestInfo.Namespace = tt.requestedNamespace
   980  			ctx = genericapirequest.WithRequestInfo(ctx, requestInfo)
   981  			ctx = genericapirequest.WithNamespace(ctx, tt.requestedNamespace)
   982  
   983  			watchKey := "/pods"
   984  			if tt.requestedNamespace != "" {
   985  				watchKey += "/" + tt.requestedNamespace
   986  				if tt.requestedName != "" {
   987  					watchKey += "/" + tt.requestedName
   988  				}
   989  			}
   990  
   991  			predicate := createPodPredicate(tt.fieldSelector, true, tt.indexFields)
   992  
   993  			list := &example.PodList{}
   994  			opts := storage.ListOptions{
   995  				ResourceVersion: "",
   996  				Predicate:       predicate,
   997  				Recursive:       true,
   998  			}
   999  			if err := store.GetList(ctx, "/pods", opts, list); err != nil {
  1000  				t.Errorf("Unexpected error: %v", err)
  1001  			}
  1002  
  1003  			opts.ResourceVersion = list.ResourceVersion
  1004  			opts.Recursive = tt.recursive
  1005  
  1006  			w, err := store.Watch(ctx, watchKey, opts)
  1007  			if err != nil {
  1008  				t.Fatalf("Watch failed: %v", err)
  1009  			}
  1010  
  1011  			currentObjs := map[string]*example.Pod{}
  1012  			for _, watchTest := range tt.watchTests {
  1013  				out := &example.Pod{}
  1014  				key := "pods/" + watchTest.obj.Namespace + "/" + watchTest.obj.Name
  1015  				err := store.GuaranteedUpdate(ctx, key, out, true, nil, storage.SimpleUpdate(
  1016  					func(runtime.Object) (runtime.Object, error) {
  1017  						obj := watchTest.obj.DeepCopy()
  1018  						return obj, nil
  1019  					}), nil)
  1020  				if err != nil {
  1021  					t.Fatalf("GuaranteedUpdate failed: %v", err)
  1022  				}
  1023  
  1024  				expectObj := out
  1025  				podIdentifier := watchTest.obj.Namespace + "/" + watchTest.obj.Name
  1026  				if watchTest.watchType == watch.Deleted {
  1027  					expectObj = currentObjs[podIdentifier]
  1028  					expectObj.ResourceVersion = out.ResourceVersion
  1029  					delete(currentObjs, podIdentifier)
  1030  				} else {
  1031  					currentObjs[podIdentifier] = out
  1032  				}
  1033  				if watchTest.expectEvent {
  1034  					testCheckResult(t, w, watch.Event{Type: watchTest.watchType, Object: expectObj})
  1035  				}
  1036  			}
  1037  			w.Stop()
  1038  			testCheckStop(t, w)
  1039  		})
  1040  	}
  1041  }
  1042  
  1043  // RunOptionalTestWatchDispatchBookmarkEvents tests whether bookmark events are sent.
  1044  // This feature is currently implemented in watch cache layer, so this is optional.
  1045  //
  1046  // TODO(#109831): ProgressNotify feature is effectively implementing the same
  1047  //
  1048  //	functionality, so we should refactor this functionality to share the same input.
  1049  func RunTestWatchDispatchBookmarkEvents(ctx context.Context, t *testing.T, store storage.Interface, expectedWatchBookmarks bool) {
  1050  	key, storedObj := testPropagateStore(ctx, t, store, &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: "test-ns"}})
  1051  	startRV := storedObj.ResourceVersion
  1052  
  1053  	tests := []struct {
  1054  		name                string
  1055  		timeout             time.Duration
  1056  		expected            bool
  1057  		allowWatchBookmarks bool
  1058  	}{
  1059  		{ // test old client won't get Bookmark event
  1060  			name:                "allowWatchBookmarks=false",
  1061  			timeout:             3 * time.Second,
  1062  			expected:            false,
  1063  			allowWatchBookmarks: false,
  1064  		},
  1065  		{
  1066  			name:                "allowWatchBookmarks=true",
  1067  			timeout:             3 * time.Second,
  1068  			expected:            expectedWatchBookmarks,
  1069  			allowWatchBookmarks: true,
  1070  		},
  1071  	}
  1072  
  1073  	for i, tt := range tests {
  1074  		t.Run(tt.name, func(t *testing.T) {
  1075  			pred := storage.Everything
  1076  			pred.AllowWatchBookmarks = tt.allowWatchBookmarks
  1077  			ctx, cancel := context.WithTimeout(context.Background(), tt.timeout)
  1078  			defer cancel()
  1079  
  1080  			watcher, err := store.Watch(ctx, key, storage.ListOptions{ResourceVersion: startRV, Predicate: pred})
  1081  			if err != nil {
  1082  				t.Fatalf("Unexpected error: %v", err)
  1083  			}
  1084  			defer watcher.Stop()
  1085  
  1086  			// Create events of pods in a different namespace
  1087  			out := &example.Pod{}
  1088  			obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", Namespace: fmt.Sprintf("other-ns-%d", i)}}
  1089  			objKey := computePodKey(obj)
  1090  
  1091  			if err := store.Create(ctx, objKey, obj, out, 0); err != nil {
  1092  				t.Fatalf("Create failed: %v", err)
  1093  			}
  1094  
  1095  			// Now wait for Bookmark event
  1096  			select {
  1097  			case event, ok := <-watcher.ResultChan():
  1098  				if !ok && tt.expected {
  1099  					t.Errorf("Unexpected object watched (no objects)")
  1100  				}
  1101  				if tt.expected && event.Type != watch.Bookmark {
  1102  					t.Errorf("Unexpected object watched %#v", event)
  1103  				}
  1104  			case <-time.After(time.Second * 3):
  1105  				if tt.expected {
  1106  					t.Errorf("Unexpected object watched (timeout)")
  1107  				}
  1108  			}
  1109  		})
  1110  	}
  1111  }
  1112  
  1113  // RunOptionalTestWatchBookmarksWithCorrectResourceVersion tests whether bookmark events are
  1114  // sent with correct resource versions.
  1115  // This feature is currently implemented in watch cache layer, so this is optional.
  1116  //
  1117  // TODO(#109831): ProgressNotify feature is effectively implementing the same
  1118  //
  1119  //	functionality, so we should refactor this functionality to share the same input.
  1120  func RunTestOptionalWatchBookmarksWithCorrectResourceVersion(ctx context.Context, t *testing.T, store storage.Interface) {
  1121  	// Compute the initial resource version.
  1122  	list := &example.PodList{}
  1123  	storageOpts := storage.ListOptions{
  1124  		Predicate: storage.Everything,
  1125  		Recursive: true,
  1126  	}
  1127  	if err := store.GetList(ctx, "/pods", storageOpts, list); err != nil {
  1128  		t.Errorf("Unexpected error: %v", err)
  1129  	}
  1130  	startRV := list.ResourceVersion
  1131  
  1132  	key := "/pods/test-ns"
  1133  	pred := storage.Everything
  1134  	pred.AllowWatchBookmarks = true
  1135  
  1136  	ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
  1137  	defer cancel()
  1138  
  1139  	watcher, err := store.Watch(ctx, key, storage.ListOptions{ResourceVersion: startRV, Predicate: pred, Recursive: true})
  1140  	if err != nil {
  1141  		t.Fatalf("Unexpected error: %v", err)
  1142  	}
  1143  	defer watcher.Stop()
  1144  
  1145  	done := make(chan struct{})
  1146  	errc := make(chan error, 1)
  1147  	var wg sync.WaitGroup
  1148  	wg.Add(1)
  1149  	// We must wait for the waitgroup to exit before we terminate the cache or the server in prior defers.
  1150  	defer wg.Wait()
  1151  	// Call close first, so the goroutine knows to exit.
  1152  	defer close(done)
  1153  
  1154  	go func() {
  1155  		defer wg.Done()
  1156  		for i := 0; i < 100; i++ {
  1157  			select {
  1158  			case <-done:
  1159  				return
  1160  			default:
  1161  				out := &example.Pod{}
  1162  				pod := &example.Pod{
  1163  					ObjectMeta: metav1.ObjectMeta{
  1164  						Name:      fmt.Sprintf("foo-%d", i),
  1165  						Namespace: "test-ns",
  1166  					},
  1167  				}
  1168  				podKey := computePodKey(pod)
  1169  				if err := store.Create(ctx, podKey, pod, out, 0); err != nil {
  1170  					errc <- fmt.Errorf("failed to create pod %v: %v", pod, err)
  1171  					return
  1172  				}
  1173  				time.Sleep(10 * time.Millisecond)
  1174  			}
  1175  		}
  1176  	}()
  1177  
  1178  	bookmarkReceived := false
  1179  	lastObservedResourceVersion := uint64(0)
  1180  
  1181  	for {
  1182  		select {
  1183  		case err := <-errc:
  1184  			t.Fatal(err)
  1185  		case event, ok := <-watcher.ResultChan():
  1186  			if !ok {
  1187  				// Make sure we have received a bookmark event
  1188  				if !bookmarkReceived {
  1189  					t.Fatalf("Unpexected error, we did not received a bookmark event")
  1190  				}
  1191  				return
  1192  			}
  1193  			rv, err := storage.APIObjectVersioner{}.ObjectResourceVersion(event.Object)
  1194  			if err != nil {
  1195  				t.Fatalf("failed to parse resourceVersion from %#v", event)
  1196  			}
  1197  			if event.Type == watch.Bookmark {
  1198  				bookmarkReceived = true
  1199  				// bookmark event has a RV greater than or equal to the before one
  1200  				if rv < lastObservedResourceVersion {
  1201  					t.Fatalf("Unexpected bookmark resourceVersion %v less than observed %v)", rv, lastObservedResourceVersion)
  1202  				}
  1203  			} else {
  1204  				// non-bookmark event has a RV greater than anything before
  1205  				if rv <= lastObservedResourceVersion {
  1206  					t.Fatalf("Unexpected event resourceVersion %v less than or equal to bookmark %v)", rv, lastObservedResourceVersion)
  1207  				}
  1208  			}
  1209  			lastObservedResourceVersion = rv
  1210  		}
  1211  	}
  1212  }
  1213  
  1214  // RunSendInitialEventsBackwardCompatibility test backward compatibility
  1215  // when SendInitialEvents option is set against various implementations.
  1216  // Backward compatibility is defined as RV = "" || RV = "O" and AllowWatchBookmark is set to false.
  1217  // In that case we expect a watch request to be established.
  1218  func RunSendInitialEventsBackwardCompatibility(ctx context.Context, t *testing.T, store storage.Interface) {
  1219  	opts := storage.ListOptions{Predicate: storage.Everything}
  1220  	opts.SendInitialEvents = pointer.Bool(true)
  1221  	w, err := store.Watch(ctx, "/pods", opts)
  1222  	require.NoError(t, err)
  1223  	w.Stop()
  1224  }
  1225  
  1226  // RunWatchSemantics test the following cases:
  1227  //
  1228  // +-----------------+---------------------+-------------------+
  1229  // | ResourceVersion | AllowWatchBookmarks | SendInitialEvents |
  1230  // +=================+=====================+===================+
  1231  // | Unset           | true/false          | true/false        |
  1232  // | 0               | true/false          | true/false        |
  1233  // | 1               | true/false          | true/false        |
  1234  // | Current         | true/false          | true/false        |
  1235  // +-----------------+---------------------+-------------------+
  1236  // where:
  1237  // - false indicates the value of the param was set to "false" by a test case
  1238  // - true  indicates the value of the param was set to "true" by a test case
  1239  func RunWatchSemantics(ctx context.Context, t *testing.T, store storage.Interface) {
  1240  	featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WatchList, true)
  1241  	trueVal, falseVal := true, false
  1242  	addEventsFromCreatedPods := func(createdInitialPods []*example.Pod) []watch.Event {
  1243  		var ret []watch.Event
  1244  		for _, createdPod := range createdInitialPods {
  1245  			ret = append(ret, watch.Event{Type: watch.Added, Object: createdPod})
  1246  		}
  1247  		return ret
  1248  	}
  1249  	initialEventsEndFromLastCreatedPod := func(createdInitialPods []*example.Pod) watch.Event {
  1250  		return watch.Event{
  1251  			Type: watch.Bookmark,
  1252  			Object: &example.Pod{
  1253  				ObjectMeta: metav1.ObjectMeta{
  1254  					ResourceVersion: createdInitialPods[len(createdInitialPods)-1].ResourceVersion,
  1255  					Annotations:     map[string]string{metav1.InitialEventsAnnotationKey: "true"},
  1256  				},
  1257  			},
  1258  		}
  1259  	}
  1260  	scenarios := []struct {
  1261  		name                string
  1262  		allowWatchBookmarks bool
  1263  		sendInitialEvents   *bool
  1264  		resourceVersion     string
  1265  		// useCurrentRV if set gets the current RV from the storage
  1266  		// after adding the initial pods which is then used to establish a new watch request
  1267  		useCurrentRV bool
  1268  
  1269  		initialPods                []*example.Pod
  1270  		podsAfterEstablishingWatch []*example.Pod
  1271  
  1272  		expectedInitialEvents                      func(createdInitialPods []*example.Pod) []watch.Event
  1273  		expectedInitialEventsBookmarkWithMinimalRV func(createdInitialPods []*example.Pod) watch.Event
  1274  		expectedEventsAfterEstablishingWatch       func(createdPodsAfterWatch []*example.Pod) []watch.Event
  1275  	}{
  1276  		{
  1277  			name:                  "allowWatchBookmarks=true, sendInitialEvents=true, RV=unset",
  1278  			allowWatchBookmarks:   true,
  1279  			sendInitialEvents:     &trueVal,
  1280  			initialPods:           []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1281  			expectedInitialEvents: addEventsFromCreatedPods,
  1282  			expectedInitialEventsBookmarkWithMinimalRV: initialEventsEndFromLastCreatedPod,
  1283  			podsAfterEstablishingWatch:                 []*example.Pod{makePod("4"), makePod("5")},
  1284  			expectedEventsAfterEstablishingWatch:       addEventsFromCreatedPods,
  1285  		},
  1286  		{
  1287  			name:                                 "allowWatchBookmarks=true, sendInitialEvents=false, RV=unset",
  1288  			allowWatchBookmarks:                  true,
  1289  			sendInitialEvents:                    &falseVal,
  1290  			initialPods:                          []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1291  			podsAfterEstablishingWatch:           []*example.Pod{makePod("4"), makePod("5")},
  1292  			expectedEventsAfterEstablishingWatch: addEventsFromCreatedPods,
  1293  		},
  1294  		{
  1295  			name:                                 "allowWatchBookmarks=false, sendInitialEvents=false, RV=unset",
  1296  			sendInitialEvents:                    &falseVal,
  1297  			initialPods:                          []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1298  			podsAfterEstablishingWatch:           []*example.Pod{makePod("4"), makePod("5")},
  1299  			expectedEventsAfterEstablishingWatch: addEventsFromCreatedPods,
  1300  		},
  1301  		{
  1302  			name:                                 "allowWatchBookmarks=false, sendInitialEvents=true, RV=unset",
  1303  			sendInitialEvents:                    &trueVal,
  1304  			initialPods:                          []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1305  			expectedInitialEvents:                addEventsFromCreatedPods,
  1306  			podsAfterEstablishingWatch:           []*example.Pod{makePod("4"), makePod("5")},
  1307  			expectedEventsAfterEstablishingWatch: addEventsFromCreatedPods,
  1308  		},
  1309  
  1310  		{
  1311  			name:                  "allowWatchBookmarks=true, sendInitialEvents=true, RV=0",
  1312  			allowWatchBookmarks:   true,
  1313  			sendInitialEvents:     &trueVal,
  1314  			resourceVersion:       "0",
  1315  			initialPods:           []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1316  			expectedInitialEvents: addEventsFromCreatedPods,
  1317  			expectedInitialEventsBookmarkWithMinimalRV: initialEventsEndFromLastCreatedPod,
  1318  			podsAfterEstablishingWatch:                 []*example.Pod{makePod("4"), makePod("5")},
  1319  			expectedEventsAfterEstablishingWatch:       addEventsFromCreatedPods,
  1320  		},
  1321  		{
  1322  			name:                                 "allowWatchBookmarks=true, sendInitialEvents=false, RV=0",
  1323  			allowWatchBookmarks:                  true,
  1324  			sendInitialEvents:                    &falseVal,
  1325  			resourceVersion:                      "0",
  1326  			initialPods:                          []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1327  			podsAfterEstablishingWatch:           []*example.Pod{makePod("4"), makePod("5")},
  1328  			expectedEventsAfterEstablishingWatch: addEventsFromCreatedPods,
  1329  		},
  1330  		{
  1331  			name:                                 "allowWatchBookmarks=false, sendInitialEvents=false, RV=0",
  1332  			sendInitialEvents:                    &falseVal,
  1333  			resourceVersion:                      "0",
  1334  			initialPods:                          []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1335  			podsAfterEstablishingWatch:           []*example.Pod{makePod("4"), makePod("5")},
  1336  			expectedEventsAfterEstablishingWatch: addEventsFromCreatedPods,
  1337  		},
  1338  		{
  1339  			name:                                 "allowWatchBookmarks=false, sendInitialEvents=true, RV=0",
  1340  			sendInitialEvents:                    &trueVal,
  1341  			resourceVersion:                      "0",
  1342  			initialPods:                          []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1343  			expectedInitialEvents:                addEventsFromCreatedPods,
  1344  			podsAfterEstablishingWatch:           []*example.Pod{makePod("4"), makePod("5")},
  1345  			expectedEventsAfterEstablishingWatch: addEventsFromCreatedPods,
  1346  		},
  1347  
  1348  		{
  1349  			name:                  "allowWatchBookmarks=true, sendInitialEvents=true, RV=1",
  1350  			allowWatchBookmarks:   true,
  1351  			sendInitialEvents:     &trueVal,
  1352  			resourceVersion:       "1",
  1353  			initialPods:           []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1354  			expectedInitialEvents: addEventsFromCreatedPods,
  1355  			expectedInitialEventsBookmarkWithMinimalRV: initialEventsEndFromLastCreatedPod,
  1356  			podsAfterEstablishingWatch:                 []*example.Pod{makePod("4"), makePod("5")},
  1357  			expectedEventsAfterEstablishingWatch:       addEventsFromCreatedPods,
  1358  		},
  1359  		{
  1360  			name:                                 "allowWatchBookmarks=true, sendInitialEvents=false, RV=1",
  1361  			allowWatchBookmarks:                  true,
  1362  			sendInitialEvents:                    &falseVal,
  1363  			resourceVersion:                      "1",
  1364  			initialPods:                          []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1365  			expectedInitialEvents:                addEventsFromCreatedPods,
  1366  			podsAfterEstablishingWatch:           []*example.Pod{makePod("4"), makePod("5")},
  1367  			expectedEventsAfterEstablishingWatch: addEventsFromCreatedPods,
  1368  		},
  1369  		{
  1370  			name:                                 "allowWatchBookmarks=false, sendInitialEvents=false, RV=1",
  1371  			sendInitialEvents:                    &falseVal,
  1372  			resourceVersion:                      "1",
  1373  			initialPods:                          []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1374  			expectedInitialEvents:                addEventsFromCreatedPods,
  1375  			podsAfterEstablishingWatch:           []*example.Pod{makePod("4"), makePod("5")},
  1376  			expectedEventsAfterEstablishingWatch: addEventsFromCreatedPods,
  1377  		},
  1378  		{
  1379  			name:                                 "allowWatchBookmarks=false, sendInitialEvents=true, RV=1",
  1380  			sendInitialEvents:                    &trueVal,
  1381  			resourceVersion:                      "1",
  1382  			initialPods:                          []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1383  			expectedInitialEvents:                addEventsFromCreatedPods,
  1384  			podsAfterEstablishingWatch:           []*example.Pod{makePod("4"), makePod("5")},
  1385  			expectedEventsAfterEstablishingWatch: addEventsFromCreatedPods,
  1386  		},
  1387  
  1388  		{
  1389  			name:                  "allowWatchBookmarks=true, sendInitialEvents=true, RV=useCurrentRV",
  1390  			allowWatchBookmarks:   true,
  1391  			sendInitialEvents:     &trueVal,
  1392  			useCurrentRV:          true,
  1393  			initialPods:           []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1394  			expectedInitialEvents: addEventsFromCreatedPods,
  1395  			expectedInitialEventsBookmarkWithMinimalRV: initialEventsEndFromLastCreatedPod,
  1396  			podsAfterEstablishingWatch:                 []*example.Pod{makePod("4"), makePod("5")},
  1397  			expectedEventsAfterEstablishingWatch:       addEventsFromCreatedPods,
  1398  		},
  1399  		{
  1400  			name:                                 "allowWatchBookmarks=true, sendInitialEvents=false, RV=useCurrentRV",
  1401  			allowWatchBookmarks:                  true,
  1402  			sendInitialEvents:                    &falseVal,
  1403  			useCurrentRV:                         true,
  1404  			initialPods:                          []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1405  			podsAfterEstablishingWatch:           []*example.Pod{makePod("4"), makePod("5")},
  1406  			expectedEventsAfterEstablishingWatch: addEventsFromCreatedPods,
  1407  		},
  1408  		{
  1409  			name:                                 "allowWatchBookmarks=false, sendInitialEvents=false, RV=useCurrentRV",
  1410  			sendInitialEvents:                    &falseVal,
  1411  			useCurrentRV:                         true,
  1412  			initialPods:                          []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1413  			podsAfterEstablishingWatch:           []*example.Pod{makePod("4"), makePod("5")},
  1414  			expectedEventsAfterEstablishingWatch: addEventsFromCreatedPods,
  1415  		},
  1416  		{
  1417  			name:                                 "allowWatchBookmarks=false, sendInitialEvents=true, RV=useCurrentRV",
  1418  			sendInitialEvents:                    &trueVal,
  1419  			useCurrentRV:                         true,
  1420  			initialPods:                          []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1421  			expectedInitialEvents:                addEventsFromCreatedPods,
  1422  			podsAfterEstablishingWatch:           []*example.Pod{makePod("4"), makePod("5")},
  1423  			expectedEventsAfterEstablishingWatch: addEventsFromCreatedPods,
  1424  		},
  1425  
  1426  		{
  1427  			name:                                 "legacy, RV=0",
  1428  			resourceVersion:                      "0",
  1429  			initialPods:                          []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1430  			expectedInitialEvents:                addEventsFromCreatedPods,
  1431  			podsAfterEstablishingWatch:           []*example.Pod{makePod("4"), makePod("5")},
  1432  			expectedEventsAfterEstablishingWatch: addEventsFromCreatedPods,
  1433  		},
  1434  		{
  1435  			name:                                 "legacy, RV=unset",
  1436  			initialPods:                          []*example.Pod{makePod("1"), makePod("2"), makePod("3")},
  1437  			expectedInitialEvents:                addEventsFromCreatedPods,
  1438  			podsAfterEstablishingWatch:           []*example.Pod{makePod("4"), makePod("5")},
  1439  			expectedEventsAfterEstablishingWatch: addEventsFromCreatedPods,
  1440  		},
  1441  	}
  1442  	for idx, scenario := range scenarios {
  1443  		t.Run(scenario.name, func(t *testing.T) {
  1444  			t.Parallel()
  1445  			// set up env
  1446  			if scenario.expectedInitialEvents == nil {
  1447  				scenario.expectedInitialEvents = func(_ []*example.Pod) []watch.Event { return nil }
  1448  			}
  1449  			if scenario.expectedEventsAfterEstablishingWatch == nil {
  1450  				scenario.expectedEventsAfterEstablishingWatch = func(_ []*example.Pod) []watch.Event { return nil }
  1451  			}
  1452  
  1453  			var createdPods []*example.Pod
  1454  			ns := fmt.Sprintf("ns-%v", idx)
  1455  			for _, obj := range scenario.initialPods {
  1456  				obj.Namespace = ns
  1457  				out := &example.Pod{}
  1458  				err := store.Create(ctx, computePodKey(obj), obj, out, 0)
  1459  				require.NoError(t, err, "failed to add a pod: %v", obj)
  1460  				createdPods = append(createdPods, out)
  1461  			}
  1462  
  1463  			if scenario.useCurrentRV {
  1464  				currentStorageRV, err := storage.GetCurrentResourceVersionFromStorage(ctx, store, func() runtime.Object { return &example.PodList{} }, "/pods", "")
  1465  				require.NoError(t, err)
  1466  				scenario.resourceVersion = fmt.Sprintf("%d", currentStorageRV)
  1467  			}
  1468  
  1469  			opts := storage.ListOptions{Predicate: storage.Everything, Recursive: true}
  1470  			opts.SendInitialEvents = scenario.sendInitialEvents
  1471  			opts.Predicate.AllowWatchBookmarks = scenario.allowWatchBookmarks
  1472  			if len(scenario.resourceVersion) > 0 {
  1473  				opts.ResourceVersion = scenario.resourceVersion
  1474  			}
  1475  
  1476  			w, err := store.Watch(context.Background(), fmt.Sprintf("/pods/%s", ns), opts)
  1477  			require.NoError(t, err, "failed to create watch: %v")
  1478  			defer w.Stop()
  1479  
  1480  			// make sure we only get initial events
  1481  			testCheckResultsInStrictOrder(t, w, scenario.expectedInitialEvents(createdPods))
  1482  
  1483  			// make sure that the actual bookmark has at least RV >= to the expected one
  1484  			if scenario.expectedInitialEventsBookmarkWithMinimalRV != nil {
  1485  				testCheckResultFunc(t, w, func(actualEvent watch.Event) {
  1486  					expectedBookmarkEventWithMinRV := scenario.expectedInitialEventsBookmarkWithMinimalRV(createdPods)
  1487  					expectedObj, err := meta.Accessor(expectedBookmarkEventWithMinRV.Object)
  1488  					require.NoError(t, err)
  1489  					expectedRV, err := storage.APIObjectVersioner{}.ObjectResourceVersion(expectedBookmarkEventWithMinRV.Object)
  1490  					require.NoError(t, err)
  1491  
  1492  					actualObj, err := meta.Accessor(actualEvent.Object)
  1493  					require.NoError(t, err)
  1494  					actualRV, err := storage.APIObjectVersioner{}.ObjectResourceVersion(actualEvent.Object)
  1495  					require.NoError(t, err)
  1496  
  1497  					require.GreaterOrEqual(t, actualRV, expectedRV)
  1498  
  1499  					// once we know that the RV is at least >= the expected one
  1500  					// rewrite it so that we can compare the objs
  1501  					expectedObj.SetResourceVersion(actualObj.GetResourceVersion())
  1502  					expectNoDiff(t, "incorrect event", expectedBookmarkEventWithMinRV, actualEvent)
  1503  				})
  1504  			}
  1505  
  1506  			createdPods = []*example.Pod{}
  1507  			// add a pod that is greater than the storage's RV when the watch was started
  1508  			for _, obj := range scenario.podsAfterEstablishingWatch {
  1509  				obj.Namespace = ns
  1510  				out := &example.Pod{}
  1511  				err = store.Create(ctx, computePodKey(obj), obj, out, 0)
  1512  				require.NoError(t, err, "failed to add a pod: %v")
  1513  				createdPods = append(createdPods, out)
  1514  			}
  1515  			testCheckResultsInStrictOrder(t, w, scenario.expectedEventsAfterEstablishingWatch(createdPods))
  1516  			testCheckNoMoreResults(t, w)
  1517  		})
  1518  	}
  1519  }
  1520  
  1521  // RunWatchSemanticInitialEventsExtended checks if the bookmark event
  1522  // marking the end of the list stream contains the global RV.
  1523  //
  1524  // note that this scenario differs from the one in RunWatchSemantics
  1525  // by adding the pod to a different ns to advance the global RV
  1526  func RunWatchSemanticInitialEventsExtended(ctx context.Context, t *testing.T, store storage.Interface) {
  1527  	trueVal := true
  1528  	expectedInitialEventsInStrictOrder := func(initialPods []*example.Pod, globalResourceVersion string) []watch.Event {
  1529  		watchEvents := []watch.Event{}
  1530  		for _, initialPod := range initialPods {
  1531  			watchEvents = append(watchEvents, watch.Event{Type: watch.Added, Object: initialPod})
  1532  		}
  1533  		watchEvents = append(watchEvents, watch.Event{Type: watch.Bookmark, Object: &example.Pod{
  1534  			ObjectMeta: metav1.ObjectMeta{
  1535  				ResourceVersion: globalResourceVersion,
  1536  				Annotations:     map[string]string{metav1.InitialEventsAnnotationKey: "true"},
  1537  			},
  1538  		}})
  1539  		return watchEvents
  1540  	}
  1541  	featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WatchList, true)
  1542  
  1543  	initialPods := []*example.Pod{}
  1544  	ns := "ns-foo"
  1545  	for _, initialPod := range []*example.Pod{makePod("1"), makePod("2"), makePod("3"), makePod("4"), makePod("5")} {
  1546  		initialPod.Namespace = ns
  1547  		out := &example.Pod{}
  1548  		err := store.Create(ctx, computePodKey(initialPod), initialPod, out, 0)
  1549  		require.NoError(t, err, "failed to add a pod: %v")
  1550  		initialPods = append(initialPods, out)
  1551  	}
  1552  
  1553  	// add the pod to a different ns to advance the global RV
  1554  	pod := makePod("1")
  1555  	pod.Namespace = "other-ns-foo"
  1556  	otherNsPod := &example.Pod{}
  1557  	err := store.Create(ctx, computePodKey(pod), pod, otherNsPod, 0)
  1558  	require.NoError(t, err, "failed to add a pod: %v")
  1559  
  1560  	opts := storage.ListOptions{Predicate: storage.Everything, Recursive: true}
  1561  	opts.SendInitialEvents = &trueVal
  1562  	opts.Predicate.AllowWatchBookmarks = true
  1563  
  1564  	w, err := store.Watch(context.Background(), fmt.Sprintf("/pods/%s", ns), opts)
  1565  	require.NoError(t, err, "failed to create watch: %v")
  1566  	defer w.Stop()
  1567  
  1568  	// make sure we only get initial events from the first ns
  1569  	// followed by the bookmark with the global RV
  1570  	testCheckResultsInStrictOrder(t, w, expectedInitialEventsInStrictOrder(initialPods, otherNsPod.ResourceVersion))
  1571  	testCheckNoMoreResults(t, w)
  1572  }
  1573  
  1574  func makePod(namePrefix string) *example.Pod {
  1575  	return &example.Pod{
  1576  		ObjectMeta: metav1.ObjectMeta{
  1577  			Name: fmt.Sprintf("pod-%s", namePrefix),
  1578  		},
  1579  	}
  1580  }
  1581  
  1582  type testWatchStruct struct {
  1583  	obj         *example.Pod
  1584  	expectEvent bool
  1585  	watchType   watch.EventType
  1586  }
  1587  
  1588  func createPodPredicate(field fields.Selector, namespaceScoped bool, indexField []string) storage.SelectionPredicate {
  1589  	return storage.SelectionPredicate{
  1590  		Label:       labels.Everything(),
  1591  		Field:       field,
  1592  		GetAttrs:    determinePodGetAttrFunc(namespaceScoped, indexField),
  1593  		IndexFields: indexField,
  1594  	}
  1595  }
  1596  
  1597  func determinePodGetAttrFunc(namespaceScoped bool, indexField []string) storage.AttrFunc {
  1598  	if indexField != nil {
  1599  		if namespaceScoped {
  1600  			return namespacedScopedNodeNameAttrFunc
  1601  		}
  1602  		return clusterScopedNodeNameAttrFunc
  1603  	}
  1604  	if namespaceScoped {
  1605  		return storage.DefaultNamespaceScopedAttr
  1606  	}
  1607  	return storage.DefaultClusterScopedAttr
  1608  }
  1609  
  1610  func namespacedScopedNodeNameAttrFunc(obj runtime.Object) (labels.Set, fields.Set, error) {
  1611  	pod := obj.(*example.Pod)
  1612  	return nil, fields.Set{
  1613  		"spec.nodeName":      pod.Spec.NodeName,
  1614  		"metadata.name":      pod.ObjectMeta.Name,
  1615  		"metadata.namespace": pod.ObjectMeta.Namespace,
  1616  	}, nil
  1617  }
  1618  
  1619  func clusterScopedNodeNameAttrFunc(obj runtime.Object) (labels.Set, fields.Set, error) {
  1620  	pod := obj.(*example.Pod)
  1621  	return nil, fields.Set{
  1622  		"spec.nodeName": pod.Spec.NodeName,
  1623  		"metadata.name": pod.ObjectMeta.Name,
  1624  	}, nil
  1625  }
  1626  
  1627  func basePod(podName string) *example.Pod {
  1628  	return baseNamespacedPod(podName, "")
  1629  }
  1630  
  1631  func basePodUpdated(podName string) *example.Pod {
  1632  	return baseNamespacedPodUpdated(podName, "")
  1633  }
  1634  
  1635  func basePodAssigned(podName, nodeName string) *example.Pod {
  1636  	return baseNamespacedPodAssigned(podName, "", nodeName)
  1637  }
  1638  
  1639  func baseNamespacedPod(podName, namespace string) *example.Pod {
  1640  	return &example.Pod{
  1641  		ObjectMeta: metav1.ObjectMeta{Name: podName, Namespace: namespace},
  1642  	}
  1643  }
  1644  
  1645  func baseNamespacedPodUpdated(podName, namespace string) *example.Pod {
  1646  	return &example.Pod{
  1647  		ObjectMeta: metav1.ObjectMeta{Name: podName, Namespace: namespace},
  1648  		Status:     example.PodStatus{Phase: "Running"},
  1649  	}
  1650  }
  1651  
  1652  func baseNamespacedPodAssigned(podName, namespace, nodeName string) *example.Pod {
  1653  	return &example.Pod{
  1654  		ObjectMeta: metav1.ObjectMeta{Name: podName, Namespace: namespace},
  1655  		Spec:       example.PodSpec{NodeName: nodeName},
  1656  	}
  1657  }