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