k8s.io/apiserver@v0.29.3/pkg/storage/cacher/cacher_whitebox_test.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 cacher 18 19 import ( 20 "context" 21 "crypto/rand" 22 "errors" 23 "fmt" 24 "reflect" 25 goruntime "runtime" 26 "strconv" 27 "sync" 28 "testing" 29 "time" 30 31 "github.com/stretchr/testify/require" 32 "google.golang.org/grpc/metadata" 33 34 apiequality "k8s.io/apimachinery/pkg/api/equality" 35 apierrors "k8s.io/apimachinery/pkg/api/errors" 36 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 37 "k8s.io/apimachinery/pkg/fields" 38 "k8s.io/apimachinery/pkg/labels" 39 "k8s.io/apimachinery/pkg/runtime" 40 "k8s.io/apimachinery/pkg/runtime/schema" 41 "k8s.io/apimachinery/pkg/util/wait" 42 "k8s.io/apimachinery/pkg/watch" 43 "k8s.io/apiserver/pkg/apis/example" 44 examplev1 "k8s.io/apiserver/pkg/apis/example/v1" 45 "k8s.io/apiserver/pkg/features" 46 "k8s.io/apiserver/pkg/storage" 47 etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing" 48 utilfeature "k8s.io/apiserver/pkg/util/feature" 49 featuregatetesting "k8s.io/component-base/featuregate/testing" 50 "k8s.io/utils/clock" 51 testingclock "k8s.io/utils/clock/testing" 52 "k8s.io/utils/pointer" 53 ) 54 55 func newTestCacher(s storage.Interface) (*Cacher, storage.Versioner, error) { 56 prefix := "pods" 57 config := Config{ 58 Storage: s, 59 Versioner: storage.APIObjectVersioner{}, 60 GroupResource: schema.GroupResource{Resource: "pods"}, 61 ResourcePrefix: prefix, 62 KeyFunc: func(obj runtime.Object) (string, error) { return storage.NamespaceKeyFunc(prefix, obj) }, 63 GetAttrsFunc: func(obj runtime.Object) (labels.Set, fields.Set, error) { 64 pod, ok := obj.(*example.Pod) 65 if !ok { 66 return storage.DefaultNamespaceScopedAttr(obj) 67 } 68 labelsSet, fieldsSet, err := storage.DefaultNamespaceScopedAttr(obj) 69 if err != nil { 70 return nil, nil, err 71 } 72 fieldsSet["spec.nodeName"] = pod.Spec.NodeName 73 return labelsSet, fieldsSet, nil 74 }, 75 NewFunc: func() runtime.Object { return &example.Pod{} }, 76 NewListFunc: func() runtime.Object { return &example.PodList{} }, 77 Codec: codecs.LegacyCodec(examplev1.SchemeGroupVersion), 78 Clock: clock.RealClock{}, 79 } 80 cacher, err := NewCacherFromConfig(config) 81 return cacher, storage.APIObjectVersioner{}, err 82 } 83 84 type dummyStorage struct { 85 sync.RWMutex 86 err error 87 getListFn func(_ context.Context, _ string, _ storage.ListOptions, listObj runtime.Object) error 88 watchFn func(_ context.Context, _ string, _ storage.ListOptions) (watch.Interface, error) 89 } 90 91 func (d *dummyStorage) RequestWatchProgress(ctx context.Context) error { 92 return nil 93 } 94 95 type dummyWatch struct { 96 ch chan watch.Event 97 } 98 99 func (w *dummyWatch) ResultChan() <-chan watch.Event { 100 return w.ch 101 } 102 103 func (w *dummyWatch) Stop() { 104 close(w.ch) 105 } 106 107 func newDummyWatch() watch.Interface { 108 return &dummyWatch{ 109 ch: make(chan watch.Event), 110 } 111 } 112 113 func (d *dummyStorage) Versioner() storage.Versioner { return nil } 114 func (d *dummyStorage) Create(_ context.Context, _ string, _, _ runtime.Object, _ uint64) error { 115 return fmt.Errorf("unimplemented") 116 } 117 func (d *dummyStorage) Delete(_ context.Context, _ string, _ runtime.Object, _ *storage.Preconditions, _ storage.ValidateObjectFunc, _ runtime.Object) error { 118 return fmt.Errorf("unimplemented") 119 } 120 func (d *dummyStorage) Watch(ctx context.Context, key string, opts storage.ListOptions) (watch.Interface, error) { 121 if d.watchFn != nil { 122 return d.watchFn(ctx, key, opts) 123 } 124 d.RLock() 125 defer d.RUnlock() 126 127 return newDummyWatch(), d.err 128 } 129 func (d *dummyStorage) Get(_ context.Context, _ string, _ storage.GetOptions, _ runtime.Object) error { 130 d.RLock() 131 defer d.RUnlock() 132 133 return d.err 134 } 135 func (d *dummyStorage) GetList(ctx context.Context, resPrefix string, opts storage.ListOptions, listObj runtime.Object) error { 136 if d.getListFn != nil { 137 return d.getListFn(ctx, resPrefix, opts, listObj) 138 } 139 d.RLock() 140 defer d.RUnlock() 141 podList := listObj.(*example.PodList) 142 podList.ListMeta = metav1.ListMeta{ResourceVersion: "100"} 143 return d.err 144 } 145 func (d *dummyStorage) GuaranteedUpdate(_ context.Context, _ string, _ runtime.Object, _ bool, _ *storage.Preconditions, _ storage.UpdateFunc, _ runtime.Object) error { 146 return fmt.Errorf("unimplemented") 147 } 148 func (d *dummyStorage) Count(_ string) (int64, error) { 149 return 0, fmt.Errorf("unimplemented") 150 } 151 func (d *dummyStorage) injectError(err error) { 152 d.Lock() 153 defer d.Unlock() 154 155 d.err = err 156 } 157 158 func TestGetListCacheBypass(t *testing.T) { 159 type testCase struct { 160 opts storage.ListOptions 161 expectBypass bool 162 } 163 commonTestCases := []testCase{ 164 {opts: storage.ListOptions{ResourceVersion: "0"}, expectBypass: false}, 165 {opts: storage.ListOptions{ResourceVersion: "1"}, expectBypass: false}, 166 167 {opts: storage.ListOptions{ResourceVersion: "", Predicate: storage.SelectionPredicate{Continue: "a"}}, expectBypass: true}, 168 {opts: storage.ListOptions{ResourceVersion: "0", Predicate: storage.SelectionPredicate{Continue: "a"}}, expectBypass: true}, 169 {opts: storage.ListOptions{ResourceVersion: "1", Predicate: storage.SelectionPredicate{Continue: "a"}}, expectBypass: true}, 170 171 {opts: storage.ListOptions{ResourceVersion: "", Predicate: storage.SelectionPredicate{Limit: 500}}, expectBypass: true}, 172 {opts: storage.ListOptions{ResourceVersion: "0", Predicate: storage.SelectionPredicate{Limit: 500}}, expectBypass: false}, 173 {opts: storage.ListOptions{ResourceVersion: "1", Predicate: storage.SelectionPredicate{Limit: 500}}, expectBypass: true}, 174 175 {opts: storage.ListOptions{ResourceVersion: "", ResourceVersionMatch: metav1.ResourceVersionMatchExact}, expectBypass: true}, 176 {opts: storage.ListOptions{ResourceVersion: "0", ResourceVersionMatch: metav1.ResourceVersionMatchExact}, expectBypass: true}, 177 {opts: storage.ListOptions{ResourceVersion: "1", ResourceVersionMatch: metav1.ResourceVersionMatchExact}, expectBypass: true}, 178 } 179 180 t.Run("ConsistentListFromStorage", func(t *testing.T) { 181 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ConsistentListFromCache, false)() 182 testCases := append(commonTestCases, 183 testCase{opts: storage.ListOptions{ResourceVersion: ""}, expectBypass: true}, 184 ) 185 for _, tc := range testCases { 186 testGetListCacheBypass(t, tc.opts, tc.expectBypass) 187 } 188 189 }) 190 t.Run("ConsistentListFromCache", func(t *testing.T) { 191 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ConsistentListFromCache, true)() 192 testCases := append(commonTestCases, 193 testCase{opts: storage.ListOptions{ResourceVersion: ""}, expectBypass: false}, 194 ) 195 for _, tc := range testCases { 196 testGetListCacheBypass(t, tc.opts, tc.expectBypass) 197 } 198 }) 199 } 200 201 func testGetListCacheBypass(t *testing.T, options storage.ListOptions, expectBypass bool) { 202 backingStorage := &dummyStorage{} 203 cacher, _, err := newTestCacher(backingStorage) 204 if err != nil { 205 t.Fatalf("Couldn't create cacher: %v", err) 206 } 207 defer cacher.Stop() 208 209 result := &example.PodList{} 210 211 // Wait until cacher is initialized. 212 if err := cacher.ready.wait(context.Background()); err != nil { 213 t.Fatalf("unexpected error waiting for the cache to be ready") 214 } 215 // Inject error to underlying layer and check if cacher is not bypassed. 216 backingStorage.getListFn = func(_ context.Context, key string, opts storage.ListOptions, listObj runtime.Object) error { 217 currentResourceVersion := "42" 218 switch { 219 // request made by getCurrentResourceVersionFromStorage by checking Limit 220 case key == cacher.resourcePrefix: 221 podList := listObj.(*example.PodList) 222 podList.ResourceVersion = currentResourceVersion 223 return nil 224 // request made by storage.GetList with revision from original request or 225 // returned by getCurrentResourceVersionFromStorage 226 case opts.ResourceVersion == options.ResourceVersion || opts.ResourceVersion == currentResourceVersion: 227 return errDummy 228 default: 229 t.Fatalf("Unexpected request %+v", opts) 230 return nil 231 } 232 } 233 err = cacher.GetList(context.TODO(), "pods/ns", options, result) 234 if err != nil && err != errDummy { 235 t.Fatalf("Unexpected error for List request with options: %v, err: %v", options, err) 236 } 237 gotBypass := err == errDummy 238 if gotBypass != expectBypass { 239 t.Errorf("Unexpected bypass result for List request with options %+v, bypass expected: %v, got: %v", options, expectBypass, gotBypass) 240 } 241 } 242 243 func TestGetListNonRecursiveCacheBypass(t *testing.T) { 244 backingStorage := &dummyStorage{} 245 cacher, _, err := newTestCacher(backingStorage) 246 if err != nil { 247 t.Fatalf("Couldn't create cacher: %v", err) 248 } 249 defer cacher.Stop() 250 251 pred := storage.SelectionPredicate{ 252 Limit: 500, 253 } 254 result := &example.PodList{} 255 256 // Wait until cacher is initialized. 257 if err := cacher.ready.wait(context.Background()); err != nil { 258 t.Fatalf("unexpected error waiting for the cache to be ready") 259 } 260 261 // Inject error to underlying layer and check if cacher is not bypassed. 262 backingStorage.injectError(errDummy) 263 err = cacher.GetList(context.TODO(), "pods/ns", storage.ListOptions{ 264 ResourceVersion: "0", 265 Predicate: pred, 266 }, result) 267 if err != nil { 268 t.Errorf("GetList with Limit and RV=0 should be served from cache: %v", err) 269 } 270 271 err = cacher.GetList(context.TODO(), "pods/ns", storage.ListOptions{ 272 ResourceVersion: "", 273 Predicate: pred, 274 }, result) 275 if err != errDummy { 276 t.Errorf("GetList with Limit without RV=0 should bypass cacher: %v", err) 277 } 278 } 279 280 func TestGetCacheBypass(t *testing.T) { 281 backingStorage := &dummyStorage{} 282 cacher, _, err := newTestCacher(backingStorage) 283 if err != nil { 284 t.Fatalf("Couldn't create cacher: %v", err) 285 } 286 defer cacher.Stop() 287 288 result := &example.Pod{} 289 290 // Wait until cacher is initialized. 291 if err := cacher.ready.wait(context.Background()); err != nil { 292 t.Fatalf("unexpected error waiting for the cache to be ready") 293 } 294 295 // Inject error to underlying layer and check if cacher is not bypassed. 296 backingStorage.injectError(errDummy) 297 err = cacher.Get(context.TODO(), "pods/ns/pod-0", storage.GetOptions{ 298 IgnoreNotFound: true, 299 ResourceVersion: "0", 300 }, result) 301 if err != nil { 302 t.Errorf("Get with RV=0 should be served from cache: %v", err) 303 } 304 305 err = cacher.Get(context.TODO(), "pods/ns/pod-0", storage.GetOptions{ 306 IgnoreNotFound: true, 307 ResourceVersion: "", 308 }, result) 309 if err != errDummy { 310 t.Errorf("Get without RV=0 should bypass cacher: %v", err) 311 } 312 } 313 314 func TestWatchCacheBypass(t *testing.T) { 315 backingStorage := &dummyStorage{} 316 cacher, _, err := newTestCacher(backingStorage) 317 if err != nil { 318 t.Fatalf("Couldn't create cacher: %v", err) 319 } 320 defer cacher.Stop() 321 322 // Wait until cacher is initialized. 323 if err := cacher.ready.wait(context.Background()); err != nil { 324 t.Fatalf("unexpected error waiting for the cache to be ready") 325 } 326 327 // Inject error to underlying layer and check if cacher is not bypassed. 328 backingStorage.injectError(errDummy) 329 _, err = cacher.Watch(context.TODO(), "pod/ns", storage.ListOptions{ 330 ResourceVersion: "0", 331 Predicate: storage.Everything, 332 }) 333 if err != nil { 334 t.Errorf("Watch with RV=0 should be served from cache: %v", err) 335 } 336 337 // With unset RV, check if cacher is bypassed. 338 _, err = cacher.Watch(context.TODO(), "pod/ns", storage.ListOptions{ 339 ResourceVersion: "", 340 }) 341 if err != errDummy { 342 t.Errorf("Watch with unset RV should bypass cacher: %v", err) 343 } 344 } 345 346 func TestEmptyWatchEventCache(t *testing.T) { 347 server, etcdStorage := newEtcdTestStorage(t, etcd3testing.PathPrefix()) 348 defer server.Terminate(t) 349 350 // add a few objects 351 v := storage.APIObjectVersioner{} 352 lastRV := uint64(0) 353 for i := 0; i < 5; i++ { 354 pod := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("foo-%d", i), Namespace: "test-ns"}} 355 out := &example.Pod{} 356 key := computePodKey(pod) 357 if err := etcdStorage.Create(context.Background(), key, pod, out, 0); err != nil { 358 t.Fatalf("Create failed: %v", err) 359 } 360 var err error 361 if lastRV, err = v.ParseResourceVersion(out.ResourceVersion); err != nil { 362 t.Fatalf("Unexpected error: %v", err) 363 } 364 } 365 366 cacher, _, err := newTestCacher(etcdStorage) 367 if err != nil { 368 t.Fatalf("Couldn't create cacher: %v", err) 369 } 370 defer cacher.Stop() 371 372 // Given that cacher is always initialized from the "current" version of etcd, 373 // we now have a cacher with an empty cache of watch events and a resourceVersion of rv. 374 // It should support establishing watches from rv and higher, but not older. 375 376 expectedResourceExpiredError := apierrors.NewResourceExpired("").ErrStatus 377 tests := []struct { 378 name string 379 resourceVersion uint64 380 expectedEvent *watch.Event 381 }{ 382 { 383 name: "RV-1", 384 resourceVersion: lastRV - 1, 385 expectedEvent: &watch.Event{Type: watch.Error, Object: &expectedResourceExpiredError}, 386 }, 387 { 388 name: "RV", 389 resourceVersion: lastRV, 390 }, 391 { 392 name: "RV+1", 393 resourceVersion: lastRV + 1, 394 }, 395 } 396 397 for _, tt := range tests { 398 t.Run(tt.name, func(t *testing.T) { 399 opts := storage.ListOptions{ 400 ResourceVersion: strconv.Itoa(int(tt.resourceVersion)), 401 Predicate: storage.Everything, 402 } 403 watcher, err := cacher.Watch(context.Background(), "/pods/test-ns", opts) 404 if err != nil { 405 t.Fatalf("Failed to create watch: %v", err) 406 } 407 defer watcher.Stop() 408 select { 409 case event := <-watcher.ResultChan(): 410 if tt.expectedEvent == nil { 411 t.Errorf("Unexpected event: type=%#v, object=%#v", event.Type, event.Object) 412 break 413 } 414 if e, a := tt.expectedEvent.Type, event.Type; e != a { 415 t.Errorf("Expected: %s, got: %s", e, a) 416 } 417 if e, a := tt.expectedEvent.Object, event.Object; !apiequality.Semantic.DeepDerivative(e, a) { 418 t.Errorf("Expected: %#v, got: %#v", e, a) 419 } 420 case <-time.After(3 * time.Second): 421 if tt.expectedEvent != nil { 422 t.Errorf("Failed to get an event") 423 } 424 // watch remained established successfully 425 } 426 }) 427 } 428 } 429 430 func TestWatchNotHangingOnStartupFailure(t *testing.T) { 431 // Configure cacher so that it can't initialize, because of 432 // constantly failing lists to the underlying storage. 433 dummyErr := fmt.Errorf("dummy") 434 backingStorage := &dummyStorage{err: dummyErr} 435 cacher, _, err := newTestCacher(backingStorage) 436 if err != nil { 437 t.Fatalf("Couldn't create cacher: %v", err) 438 } 439 defer cacher.Stop() 440 441 ctx, cancel := context.WithCancel(context.Background()) 442 // Cancel the watch after some time to check if it will properly 443 // terminate instead of hanging forever. 444 go func() { 445 defer cancel() 446 cacher.clock.Sleep(5 * time.Second) 447 }() 448 449 // Watch hangs waiting on watchcache being initialized. 450 // Ensure that it terminates when its context is cancelled 451 // (e.g. the request is terminated for whatever reason). 452 _, err = cacher.Watch(ctx, "pods/ns", storage.ListOptions{ResourceVersion: "0"}) 453 if err == nil || err.Error() != apierrors.NewServiceUnavailable(context.Canceled.Error()).Error() { 454 t.Errorf("Unexpected error: %#v", err) 455 } 456 } 457 458 func TestWatcherNotGoingBackInTime(t *testing.T) { 459 backingStorage := &dummyStorage{} 460 cacher, v, err := newTestCacher(backingStorage) 461 if err != nil { 462 t.Fatalf("Couldn't create cacher: %v", err) 463 } 464 defer cacher.Stop() 465 466 // Wait until cacher is initialized. 467 if err := cacher.ready.wait(context.Background()); err != nil { 468 t.Fatalf("unexpected error waiting for the cache to be ready") 469 } 470 471 // Ensure there is some budget for slowing down processing. 472 cacher.dispatchTimeoutBudget.returnUnused(100 * time.Millisecond) 473 474 makePod := func(i int) *examplev1.Pod { 475 return &examplev1.Pod{ 476 ObjectMeta: metav1.ObjectMeta{ 477 Name: fmt.Sprintf("pod-%d", 1000+i), 478 Namespace: "ns", 479 ResourceVersion: fmt.Sprintf("%d", 1000+i), 480 }, 481 } 482 } 483 if err := cacher.watchCache.Add(makePod(0)); err != nil { 484 t.Errorf("error: %v", err) 485 } 486 487 totalPods := 100 488 489 // Create watcher that will be slowing down reading. 490 w1, err := cacher.Watch(context.TODO(), "pods/ns", storage.ListOptions{ 491 ResourceVersion: "999", 492 Predicate: storage.Everything, 493 }) 494 if err != nil { 495 t.Fatalf("Failed to create watch: %v", err) 496 } 497 defer w1.Stop() 498 go func() { 499 a := 0 500 for range w1.ResultChan() { 501 time.Sleep(time.Millisecond) 502 a++ 503 if a == 100 { 504 break 505 } 506 } 507 }() 508 509 // Now push a ton of object to cache. 510 for i := 1; i < totalPods; i++ { 511 cacher.watchCache.Add(makePod(i)) 512 } 513 514 // Create fast watcher and ensure it will get each object exactly once. 515 w2, err := cacher.Watch(context.TODO(), "pods/ns", storage.ListOptions{ResourceVersion: "999", Predicate: storage.Everything}) 516 if err != nil { 517 t.Fatalf("Failed to create watch: %v", err) 518 } 519 defer w2.Stop() 520 521 shouldContinue := true 522 currentRV := uint64(0) 523 for shouldContinue { 524 select { 525 case event, ok := <-w2.ResultChan(): 526 if !ok { 527 shouldContinue = false 528 break 529 } 530 rv, err := v.ParseResourceVersion(event.Object.(metaRuntimeInterface).GetResourceVersion()) 531 if err != nil { 532 t.Errorf("unexpected parsing error: %v", err) 533 } else { 534 if rv < currentRV { 535 t.Errorf("watcher going back in time") 536 } 537 currentRV = rv 538 } 539 case <-time.After(time.Second): 540 w2.Stop() 541 } 542 } 543 } 544 545 func TestCacherDontAcceptRequestsStopped(t *testing.T) { 546 backingStorage := &dummyStorage{} 547 cacher, _, err := newTestCacher(backingStorage) 548 if err != nil { 549 t.Fatalf("Couldn't create cacher: %v", err) 550 } 551 552 // Wait until cacher is initialized. 553 if err := cacher.ready.wait(context.Background()); err != nil { 554 t.Fatalf("unexpected error waiting for the cache to be ready") 555 } 556 557 w, err := cacher.Watch(context.Background(), "pods/ns", storage.ListOptions{ResourceVersion: "0", Predicate: storage.Everything}) 558 if err != nil { 559 t.Fatalf("Failed to create watch: %v", err) 560 } 561 562 watchClosed := make(chan struct{}) 563 go func() { 564 defer close(watchClosed) 565 for event := range w.ResultChan() { 566 switch event.Type { 567 case watch.Added, watch.Modified, watch.Deleted: 568 // ok 569 default: 570 t.Errorf("unexpected event %#v", event) 571 } 572 } 573 }() 574 575 cacher.Stop() 576 577 _, err = cacher.Watch(context.Background(), "pods/ns", storage.ListOptions{ResourceVersion: "0", Predicate: storage.Everything}) 578 if err == nil { 579 t.Fatalf("Success to create Watch: %v", err) 580 } 581 582 result := &example.Pod{} 583 err = cacher.Get(context.TODO(), "pods/ns/pod-0", storage.GetOptions{ 584 IgnoreNotFound: true, 585 ResourceVersion: "1", 586 }, result) 587 if err == nil { 588 t.Fatalf("Success to create Get: %v", err) 589 } 590 591 listResult := &example.PodList{} 592 err = cacher.GetList(context.TODO(), "pods/ns", storage.ListOptions{ 593 ResourceVersion: "1", 594 Recursive: true, 595 }, listResult) 596 if err == nil { 597 t.Fatalf("Success to create GetList: %v", err) 598 } 599 600 select { 601 case <-watchClosed: 602 case <-time.After(wait.ForeverTestTimeout): 603 t.Errorf("timed out waiting for watch to close") 604 } 605 } 606 607 func TestCacherDontMissEventsOnReinitialization(t *testing.T) { 608 makePod := func(i int) *example.Pod { 609 return &example.Pod{ 610 ObjectMeta: metav1.ObjectMeta{ 611 Name: fmt.Sprintf("pod-%d", i), 612 Namespace: "ns", 613 ResourceVersion: fmt.Sprintf("%d", i), 614 }, 615 } 616 } 617 618 listCalls, watchCalls := 0, 0 619 backingStorage := &dummyStorage{ 620 getListFn: func(_ context.Context, _ string, _ storage.ListOptions, listObj runtime.Object) error { 621 podList := listObj.(*example.PodList) 622 var err error 623 switch listCalls { 624 case 0: 625 podList.ListMeta = metav1.ListMeta{ResourceVersion: "1"} 626 case 1: 627 podList.ListMeta = metav1.ListMeta{ResourceVersion: "10"} 628 default: 629 err = fmt.Errorf("unexpected list call") 630 } 631 listCalls++ 632 return err 633 }, 634 watchFn: func(_ context.Context, _ string, _ storage.ListOptions) (watch.Interface, error) { 635 var w *watch.FakeWatcher 636 var err error 637 switch watchCalls { 638 case 0: 639 w = watch.NewFakeWithChanSize(10, false) 640 for i := 2; i < 8; i++ { 641 w.Add(makePod(i)) 642 } 643 // Emit an error to force relisting. 644 w.Error(nil) 645 w.Stop() 646 case 1: 647 w = watch.NewFakeWithChanSize(10, false) 648 for i := 12; i < 18; i++ { 649 w.Add(makePod(i)) 650 } 651 w.Stop() 652 default: 653 err = fmt.Errorf("unexpected watch call") 654 } 655 watchCalls++ 656 return w, err 657 }, 658 } 659 cacher, _, err := newTestCacher(backingStorage) 660 if err != nil { 661 t.Fatalf("Couldn't create cacher: %v", err) 662 } 663 defer cacher.Stop() 664 665 concurrency := 1000 666 wg := sync.WaitGroup{} 667 wg.Add(concurrency) 668 669 // Ensure that test doesn't deadlock if cacher already processed everything 670 // and get back into Pending state before some watches get called. 671 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 672 defer cancel() 673 674 errCh := make(chan error, concurrency) 675 for i := 0; i < concurrency; i++ { 676 go func() { 677 defer wg.Done() 678 w, err := cacher.Watch(ctx, "pods", storage.ListOptions{ResourceVersion: "1", Predicate: storage.Everything}) 679 if err != nil { 680 // Watch failed to initialize (this most probably means that cacher 681 // already moved back to Pending state before watch initialized. 682 // Ignore this case. 683 return 684 } 685 defer w.Stop() 686 687 prevRV := -1 688 for event := range w.ResultChan() { 689 if event.Type == watch.Error { 690 break 691 } 692 object := event.Object 693 if co, ok := object.(runtime.CacheableObject); ok { 694 object = co.GetObject() 695 } 696 rv, err := strconv.Atoi(object.(*example.Pod).ResourceVersion) 697 if err != nil { 698 errCh <- fmt.Errorf("incorrect resource version: %v", err) 699 return 700 } 701 if prevRV != -1 && prevRV+1 != rv { 702 errCh <- fmt.Errorf("unexpected event received, prevRV=%d, rv=%d", prevRV, rv) 703 return 704 } 705 prevRV = rv 706 } 707 708 }() 709 } 710 wg.Wait() 711 close(errCh) 712 713 for err := range errCh { 714 t.Error(err) 715 } 716 } 717 718 func TestCacherNoLeakWithMultipleWatchers(t *testing.T) { 719 backingStorage := &dummyStorage{} 720 cacher, _, err := newTestCacher(backingStorage) 721 if err != nil { 722 t.Fatalf("Couldn't create cacher: %v", err) 723 } 724 defer cacher.Stop() 725 726 // Wait until cacher is initialized. 727 if err := cacher.ready.wait(context.Background()); err != nil { 728 t.Fatalf("unexpected error waiting for the cache to be ready") 729 } 730 pred := storage.Everything 731 pred.AllowWatchBookmarks = true 732 733 // run the collision test for 3 seconds to let ~2 buckets expire 734 stopCh := make(chan struct{}) 735 var watchErr error 736 time.AfterFunc(3*time.Second, func() { close(stopCh) }) 737 738 wg := &sync.WaitGroup{} 739 740 wg.Add(1) 741 go func() { 742 defer wg.Done() 743 for { 744 select { 745 case <-stopCh: 746 return 747 default: 748 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 749 defer cancel() 750 w, err := cacher.Watch(ctx, "pods/ns", storage.ListOptions{ResourceVersion: "0", Predicate: pred}) 751 if err != nil { 752 watchErr = fmt.Errorf("Failed to create watch: %v", err) 753 return 754 } 755 w.Stop() 756 } 757 } 758 }() 759 760 wg.Add(1) 761 go func() { 762 defer wg.Done() 763 for { 764 select { 765 case <-stopCh: 766 return 767 default: 768 cacher.Lock() 769 cacher.bookmarkWatchers.popExpiredWatchersThreadUnsafe() 770 cacher.Unlock() 771 } 772 } 773 }() 774 775 // wait for adding/removing watchers to end 776 wg.Wait() 777 778 if watchErr != nil { 779 t.Fatal(watchErr) 780 } 781 782 // wait out the expiration period and pop expired watchers 783 time.Sleep(2 * time.Second) 784 cacher.Lock() 785 defer cacher.Unlock() 786 cacher.bookmarkWatchers.popExpiredWatchersThreadUnsafe() 787 if len(cacher.bookmarkWatchers.watchersBuckets) != 0 { 788 numWatchers := 0 789 for bucketID, v := range cacher.bookmarkWatchers.watchersBuckets { 790 numWatchers += len(v) 791 t.Errorf("there are %v watchers at bucket Id %v with start Id %v", len(v), bucketID, cacher.bookmarkWatchers.startBucketID) 792 } 793 t.Errorf("unexpected bookmark watchers %v", numWatchers) 794 } 795 } 796 797 func testCacherSendBookmarkEvents(t *testing.T, allowWatchBookmarks, expectedBookmarks bool) { 798 backingStorage := &dummyStorage{} 799 cacher, _, err := newTestCacher(backingStorage) 800 if err != nil { 801 t.Fatalf("Couldn't create cacher: %v", err) 802 } 803 defer cacher.Stop() 804 805 // Wait until cacher is initialized. 806 if err := cacher.ready.wait(context.Background()); err != nil { 807 t.Fatalf("unexpected error waiting for the cache to be ready") 808 } 809 pred := storage.Everything 810 pred.AllowWatchBookmarks = allowWatchBookmarks 811 812 ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second) 813 defer cancel() 814 w, err := cacher.Watch(ctx, "pods/ns", storage.ListOptions{ResourceVersion: "0", Predicate: pred}) 815 if err != nil { 816 t.Fatalf("Failed to create watch: %v", err) 817 } 818 819 resourceVersion := uint64(1000) 820 errc := make(chan error, 1) 821 go func() { 822 deadline := time.Now().Add(time.Second) 823 for i := 0; time.Now().Before(deadline); i++ { 824 err := cacher.watchCache.Add(&examplev1.Pod{ 825 ObjectMeta: metav1.ObjectMeta{ 826 Name: fmt.Sprintf("pod-%d", i), 827 Namespace: "ns", 828 ResourceVersion: fmt.Sprintf("%v", resourceVersion+uint64(i)), 829 }}) 830 if err != nil { 831 errc <- fmt.Errorf("failed to add a pod: %v", err) 832 return 833 } 834 time.Sleep(100 * time.Millisecond) 835 } 836 }() 837 838 timeoutCh := time.After(2 * time.Second) 839 lastObservedRV := uint64(0) 840 for { 841 select { 842 case err := <-errc: 843 t.Fatal(err) 844 return 845 case event, ok := <-w.ResultChan(): 846 if !ok { 847 t.Fatal("Unexpected closed") 848 } 849 rv, err := cacher.versioner.ObjectResourceVersion(event.Object) 850 if err != nil { 851 t.Errorf("failed to parse resource version from %#v: %v", event.Object, err) 852 } 853 if event.Type == watch.Bookmark { 854 if !expectedBookmarks { 855 t.Fatalf("Unexpected bookmark events received") 856 } 857 858 if rv < lastObservedRV { 859 t.Errorf("Unexpected bookmark event resource version %v (last %v)", rv, lastObservedRV) 860 } 861 return 862 } 863 lastObservedRV = rv 864 case <-timeoutCh: 865 if expectedBookmarks { 866 t.Fatal("Unexpected timeout to receive a bookmark event") 867 } 868 return 869 } 870 } 871 } 872 873 func TestCacherSendBookmarkEvents(t *testing.T) { 874 testCases := []struct { 875 allowWatchBookmarks bool 876 expectedBookmarks bool 877 }{ 878 { 879 allowWatchBookmarks: true, 880 expectedBookmarks: true, 881 }, 882 { 883 allowWatchBookmarks: false, 884 expectedBookmarks: false, 885 }, 886 } 887 888 for _, tc := range testCases { 889 testCacherSendBookmarkEvents(t, tc.allowWatchBookmarks, tc.expectedBookmarks) 890 } 891 } 892 893 func TestCacherSendsMultipleWatchBookmarks(t *testing.T) { 894 backingStorage := &dummyStorage{} 895 cacher, _, err := newTestCacher(backingStorage) 896 if err != nil { 897 t.Fatalf("Couldn't create cacher: %v", err) 898 } 899 defer cacher.Stop() 900 // Update bookmarkFrequency to speed up test. 901 // Note that the frequency lower than 1s doesn't change much due to 902 // resolution how frequency we recompute. 903 cacher.bookmarkWatchers.bookmarkFrequency = time.Second 904 905 // Wait until cacher is initialized. 906 if err := cacher.ready.wait(context.Background()); err != nil { 907 t.Fatalf("unexpected error waiting for the cache to be ready") 908 } 909 pred := storage.Everything 910 pred.AllowWatchBookmarks = true 911 912 makePod := func(index int) *examplev1.Pod { 913 return &examplev1.Pod{ 914 ObjectMeta: metav1.ObjectMeta{ 915 Name: fmt.Sprintf("pod-%d", index), 916 Namespace: "ns", 917 ResourceVersion: fmt.Sprintf("%v", 100+index), 918 }, 919 } 920 } 921 922 // Create pod to initialize watch cache. 923 if err := cacher.watchCache.Add(makePod(0)); err != nil { 924 t.Fatalf("failed to add a pod: %v", err) 925 } 926 927 ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second) 928 defer cancel() 929 w, err := cacher.Watch(ctx, "pods/ns", storage.ListOptions{ResourceVersion: "100", Predicate: pred}) 930 if err != nil { 931 t.Fatalf("Failed to create watch: %v", err) 932 } 933 934 // Create one more pod, to ensure that current RV is higher and thus 935 // bookmarks will be delievere (events are delivered for RV higher 936 // than the max from init events). 937 if err := cacher.watchCache.Add(makePod(1)); err != nil { 938 t.Fatalf("failed to add a pod: %v", err) 939 } 940 941 timeoutCh := time.After(5 * time.Second) 942 lastObservedRV := uint64(0) 943 // Ensure that a watcher gets two bookmarks. 944 for observedBookmarks := 0; observedBookmarks < 2; { 945 select { 946 case event, ok := <-w.ResultChan(): 947 if !ok { 948 t.Fatal("Unexpected closed") 949 } 950 rv, err := cacher.versioner.ObjectResourceVersion(event.Object) 951 if err != nil { 952 t.Errorf("failed to parse resource version from %#v: %v", event.Object, err) 953 } 954 if event.Type == watch.Bookmark { 955 observedBookmarks++ 956 if rv < lastObservedRV { 957 t.Errorf("Unexpected bookmark event resource version %v (last %v)", rv, lastObservedRV) 958 } 959 } 960 lastObservedRV = rv 961 case <-timeoutCh: 962 t.Fatal("Unexpected timeout to receive bookmark events") 963 } 964 } 965 } 966 967 func TestDispatchingBookmarkEventsWithConcurrentStop(t *testing.T) { 968 backingStorage := &dummyStorage{} 969 cacher, _, err := newTestCacher(backingStorage) 970 if err != nil { 971 t.Fatalf("Couldn't create cacher: %v", err) 972 } 973 defer cacher.Stop() 974 975 // Wait until cacher is initialized. 976 if err := cacher.ready.wait(context.Background()); err != nil { 977 t.Fatalf("unexpected error waiting for the cache to be ready") 978 } 979 980 // Ensure there is some budget for slowing down processing. 981 cacher.dispatchTimeoutBudget.returnUnused(100 * time.Millisecond) 982 983 resourceVersion := uint64(1000) 984 err = cacher.watchCache.Add(&examplev1.Pod{ 985 ObjectMeta: metav1.ObjectMeta{ 986 Name: "pod-0", 987 Namespace: "ns", 988 ResourceVersion: fmt.Sprintf("%v", resourceVersion), 989 }}) 990 if err != nil { 991 t.Fatalf("failed to add a pod: %v", err) 992 } 993 994 for i := 0; i < 1000; i++ { 995 pred := storage.Everything 996 pred.AllowWatchBookmarks = true 997 ctx, cancel := context.WithTimeout(context.Background(), time.Second) 998 defer cancel() 999 w, err := cacher.Watch(ctx, "pods/ns", storage.ListOptions{ResourceVersion: "999", Predicate: pred}) 1000 if err != nil { 1001 t.Fatalf("Failed to create watch: %v", err) 1002 } 1003 bookmark := &watchCacheEvent{ 1004 Type: watch.Bookmark, 1005 ResourceVersion: uint64(i), 1006 Object: cacher.newFunc(), 1007 } 1008 err = cacher.versioner.UpdateObject(bookmark.Object, bookmark.ResourceVersion) 1009 if err != nil { 1010 t.Fatalf("failure to update version of object (%d) %#v", bookmark.ResourceVersion, bookmark.Object) 1011 } 1012 1013 wg := sync.WaitGroup{} 1014 wg.Add(2) 1015 go func() { 1016 cacher.processEvent(bookmark) 1017 wg.Done() 1018 }() 1019 1020 go func() { 1021 w.Stop() 1022 wg.Done() 1023 }() 1024 1025 done := make(chan struct{}) 1026 go func() { 1027 for range w.ResultChan() { 1028 } 1029 close(done) 1030 }() 1031 1032 select { 1033 case <-done: 1034 case <-time.After(time.Second): 1035 t.Fatal("receive result timeout") 1036 } 1037 w.Stop() 1038 wg.Wait() 1039 } 1040 } 1041 1042 func TestBookmarksOnResourceVersionUpdates(t *testing.T) { 1043 backingStorage := &dummyStorage{} 1044 cacher, _, err := newTestCacher(backingStorage) 1045 if err != nil { 1046 t.Fatalf("Couldn't create cacher: %v", err) 1047 } 1048 defer cacher.Stop() 1049 1050 // Ensure that bookmarks are sent more frequently than every 1m. 1051 cacher.bookmarkWatchers = newTimeBucketWatchers(clock.RealClock{}, 2*time.Second) 1052 1053 // Wait until cacher is initialized. 1054 if err := cacher.ready.wait(context.Background()); err != nil { 1055 t.Fatalf("unexpected error waiting for the cache to be ready") 1056 } 1057 1058 makePod := func(i int) *examplev1.Pod { 1059 return &examplev1.Pod{ 1060 ObjectMeta: metav1.ObjectMeta{ 1061 Name: fmt.Sprintf("pod-%d", i), 1062 Namespace: "ns", 1063 ResourceVersion: fmt.Sprintf("%d", i), 1064 }, 1065 } 1066 } 1067 if err := cacher.watchCache.Add(makePod(1000)); err != nil { 1068 t.Errorf("error: %v", err) 1069 } 1070 1071 pred := storage.Everything 1072 pred.AllowWatchBookmarks = true 1073 1074 w, err := cacher.Watch(context.TODO(), "/pods/ns", storage.ListOptions{ 1075 ResourceVersion: "1000", 1076 Predicate: pred, 1077 }) 1078 if err != nil { 1079 t.Fatalf("Failed to create watch: %v", err) 1080 } 1081 1082 expectedRV := 2000 1083 1084 var rcErr error 1085 1086 wg := sync.WaitGroup{} 1087 wg.Add(1) 1088 go func() { 1089 defer wg.Done() 1090 for { 1091 event, ok := <-w.ResultChan() 1092 if !ok { 1093 rcErr = errors.New("Unexpected closed channel") 1094 return 1095 } 1096 rv, err := cacher.versioner.ObjectResourceVersion(event.Object) 1097 if err != nil { 1098 t.Errorf("failed to parse resource version from %#v: %v", event.Object, err) 1099 } 1100 if event.Type == watch.Bookmark && rv == uint64(expectedRV) { 1101 return 1102 } 1103 } 1104 }() 1105 1106 // Simulate progress notify event. 1107 cacher.watchCache.UpdateResourceVersion(strconv.Itoa(expectedRV)) 1108 1109 wg.Wait() 1110 if rcErr != nil { 1111 t.Fatal(rcErr) 1112 } 1113 } 1114 1115 type fakeTimeBudget struct{} 1116 1117 func (f *fakeTimeBudget) takeAvailable() time.Duration { 1118 return 2 * time.Second 1119 } 1120 1121 func (f *fakeTimeBudget) returnUnused(_ time.Duration) {} 1122 1123 func TestStartingResourceVersion(t *testing.T) { 1124 backingStorage := &dummyStorage{} 1125 cacher, _, err := newTestCacher(backingStorage) 1126 if err != nil { 1127 t.Fatalf("Couldn't create cacher: %v", err) 1128 } 1129 defer cacher.Stop() 1130 1131 // Wait until cacher is initialized. 1132 if err := cacher.ready.wait(context.Background()); err != nil { 1133 t.Fatalf("unexpected error waiting for the cache to be ready") 1134 } 1135 1136 // Ensure there is some budget for slowing down processing. 1137 // We use the fakeTimeBudget to prevent this test from flaking under 1138 // the following conditions: 1139 // 1) in total we create 11 events that has to be processed by the watcher 1140 // 2) the size of the channels are set to 10 for the watcher 1141 // 3) if the test is cpu-starved and the internal goroutine is not picking 1142 // up these events from the channel, after consuming the whole time 1143 // budget (defaulted to 100ms) on waiting, we will simply close the watch, 1144 // which will cause the test failure 1145 // Using fakeTimeBudget gives us always a budget to wait and have a test 1146 // pick up something from ResultCh in the meantime. 1147 // 1148 // The same can potentially happen in production, but in that case a watch 1149 // can be resumed by the client. This doesn't work in the case of this test, 1150 // because we explicitly want to test the behavior that object changes are 1151 // happening after the watch was initiated. 1152 cacher.dispatchTimeoutBudget = &fakeTimeBudget{} 1153 1154 makePod := func(i int) *examplev1.Pod { 1155 return &examplev1.Pod{ 1156 ObjectMeta: metav1.ObjectMeta{ 1157 Name: "foo", 1158 Namespace: "ns", 1159 Labels: map[string]string{"foo": strconv.Itoa(i)}, 1160 ResourceVersion: fmt.Sprintf("%d", i), 1161 }, 1162 } 1163 } 1164 1165 if err := cacher.watchCache.Add(makePod(1000)); err != nil { 1166 t.Errorf("error: %v", err) 1167 } 1168 // Advance RV by 10. 1169 startVersion := uint64(1010) 1170 1171 watcher, err := cacher.Watch(context.TODO(), "pods/ns/foo", storage.ListOptions{ResourceVersion: strconv.FormatUint(startVersion, 10), Predicate: storage.Everything}) 1172 if err != nil { 1173 t.Fatalf("Unexpected error: %v", err) 1174 } 1175 defer watcher.Stop() 1176 1177 for i := 1; i <= 11; i++ { 1178 if err := cacher.watchCache.Update(makePod(1000 + i)); err != nil { 1179 t.Errorf("error: %v", err) 1180 } 1181 } 1182 1183 e, ok := <-watcher.ResultChan() 1184 if !ok { 1185 t.Errorf("unexpectedly closed watch") 1186 } 1187 object := e.Object 1188 if co, ok := object.(runtime.CacheableObject); ok { 1189 object = co.GetObject() 1190 } 1191 pod := object.(*examplev1.Pod) 1192 podRV, err := cacher.versioner.ParseResourceVersion(pod.ResourceVersion) 1193 if err != nil { 1194 t.Fatalf("unexpected error: %v", err) 1195 } 1196 1197 // event should have at least rv + 1, since we're starting the watch at rv 1198 if podRV <= startVersion { 1199 t.Errorf("expected event with resourceVersion of at least %d, got %d", startVersion+1, podRV) 1200 } 1201 } 1202 1203 func TestDispatchEventWillNotBeBlockedByTimedOutWatcher(t *testing.T) { 1204 backingStorage := &dummyStorage{} 1205 cacher, _, err := newTestCacher(backingStorage) 1206 if err != nil { 1207 t.Fatalf("Couldn't create cacher: %v", err) 1208 } 1209 defer cacher.Stop() 1210 1211 // Wait until cacher is initialized. 1212 if err := cacher.ready.wait(context.Background()); err != nil { 1213 t.Fatalf("unexpected error waiting for the cache to be ready") 1214 } 1215 1216 // Ensure there is some budget for slowing down processing. 1217 // We use the fakeTimeBudget to prevent this test from flaking under 1218 // the following conditions: 1219 // 1) the watch w1 is blocked, so we were consuming the whole budget once 1220 // its buffer was filled in (10 items) 1221 // 2) the budget is refreshed once per second, so it basically wasn't 1222 // happening in the test at all 1223 // 3) if the test was cpu-starved and we weren't able to consume events 1224 // from w2 ResultCh it could have happened that its buffer was also 1225 // filling in and given we no longer had timeBudget (consumed in (1)) 1226 // trying to put next item was simply breaking the watch 1227 // Using fakeTimeBudget gives us always a budget to wait and have a test 1228 // pick up something from ResultCh in the meantime. 1229 cacher.dispatchTimeoutBudget = &fakeTimeBudget{} 1230 1231 makePod := func(i int) *examplev1.Pod { 1232 return &examplev1.Pod{ 1233 ObjectMeta: metav1.ObjectMeta{ 1234 Name: fmt.Sprintf("pod-%d", 1000+i), 1235 Namespace: "ns", 1236 ResourceVersion: fmt.Sprintf("%d", 1000+i), 1237 }, 1238 } 1239 } 1240 if err := cacher.watchCache.Add(makePod(0)); err != nil { 1241 t.Errorf("error: %v", err) 1242 } 1243 1244 totalPods := 50 1245 1246 // Create watcher that will be blocked. 1247 w1, err := cacher.Watch(context.TODO(), "pods/ns", storage.ListOptions{ResourceVersion: "999", Predicate: storage.Everything}) 1248 if err != nil { 1249 t.Fatalf("Failed to create watch: %v", err) 1250 } 1251 defer w1.Stop() 1252 1253 // Create fast watcher and ensure it will get all objects. 1254 w2, err := cacher.Watch(context.TODO(), "pods/ns", storage.ListOptions{ResourceVersion: "999", Predicate: storage.Everything}) 1255 if err != nil { 1256 t.Fatalf("Failed to create watch: %v", err) 1257 } 1258 defer w2.Stop() 1259 1260 // Now push a ton of object to cache. 1261 for i := 1; i < totalPods; i++ { 1262 cacher.watchCache.Add(makePod(i)) 1263 } 1264 1265 shouldContinue := true 1266 eventsCount := 0 1267 for shouldContinue { 1268 select { 1269 case event, ok := <-w2.ResultChan(): 1270 if !ok { 1271 shouldContinue = false 1272 break 1273 } 1274 if event.Type == watch.Added { 1275 eventsCount++ 1276 if eventsCount == totalPods { 1277 shouldContinue = false 1278 } 1279 } 1280 case <-time.After(wait.ForeverTestTimeout): 1281 shouldContinue = false 1282 w2.Stop() 1283 } 1284 } 1285 if eventsCount != totalPods { 1286 t.Errorf("watcher is blocked by slower one (count: %d)", eventsCount) 1287 } 1288 } 1289 1290 func verifyEvents(t *testing.T, w watch.Interface, events []watch.Event, strictOrder bool) { 1291 _, _, line, _ := goruntime.Caller(1) 1292 actualEvents := make([]watch.Event, len(events)) 1293 for idx := range events { 1294 select { 1295 case event := <-w.ResultChan(): 1296 actualEvents[idx] = event 1297 case <-time.After(wait.ForeverTestTimeout): 1298 t.Logf("(called from line %d)", line) 1299 t.Errorf("Timed out waiting for an event") 1300 } 1301 } 1302 validateEvents := func(expected, actual watch.Event) (bool, []string) { 1303 errors := []string{} 1304 if e, a := expected.Type, actual.Type; e != a { 1305 errors = append(errors, fmt.Sprintf("Expected: %s, got: %s", e, a)) 1306 } 1307 actualObject := actual.Object 1308 if co, ok := actualObject.(runtime.CacheableObject); ok { 1309 actualObject = co.GetObject() 1310 } 1311 if e, a := expected.Object, actualObject; !apiequality.Semantic.DeepEqual(e, a) { 1312 errors = append(errors, fmt.Sprintf("Expected: %#v, got: %#v", e, a)) 1313 } 1314 return len(errors) == 0, errors 1315 } 1316 1317 if len(events) != len(actualEvents) { 1318 t.Fatalf("unexpected number of events: %d, expected: %d, acutalEvents: %#v, expectedEvents:%#v", len(actualEvents), len(events), actualEvents, events) 1319 } 1320 1321 if strictOrder { 1322 for idx, expectedEvent := range events { 1323 valid, errors := validateEvents(expectedEvent, actualEvents[idx]) 1324 if !valid { 1325 t.Logf("(called from line %d)", line) 1326 for _, err := range errors { 1327 t.Errorf(err) 1328 } 1329 } 1330 } 1331 } 1332 for _, expectedEvent := range events { 1333 validated := false 1334 for _, actualEvent := range actualEvents { 1335 if validated, _ = validateEvents(expectedEvent, actualEvent); validated { 1336 break 1337 } 1338 } 1339 if !validated { 1340 t.Fatalf("Expected: %#v but didn't find", expectedEvent) 1341 } 1342 } 1343 } 1344 1345 func TestCachingDeleteEvents(t *testing.T) { 1346 backingStorage := &dummyStorage{} 1347 cacher, _, err := newTestCacher(backingStorage) 1348 if err != nil { 1349 t.Fatalf("Couldn't create cacher: %v", err) 1350 } 1351 defer cacher.Stop() 1352 1353 // Wait until cacher is initialized. 1354 if err := cacher.ready.wait(context.Background()); err != nil { 1355 t.Fatalf("unexpected error waiting for the cache to be ready") 1356 } 1357 1358 fooPredicate := storage.SelectionPredicate{ 1359 Label: labels.SelectorFromSet(map[string]string{"foo": "true"}), 1360 Field: fields.Everything(), 1361 } 1362 barPredicate := storage.SelectionPredicate{ 1363 Label: labels.SelectorFromSet(map[string]string{"bar": "true"}), 1364 Field: fields.Everything(), 1365 } 1366 1367 createWatch := func(pred storage.SelectionPredicate) watch.Interface { 1368 w, err := cacher.Watch(context.TODO(), "pods/ns", storage.ListOptions{ResourceVersion: "999", Predicate: pred}) 1369 if err != nil { 1370 t.Fatalf("Failed to create watch: %v", err) 1371 } 1372 return w 1373 } 1374 1375 allEventsWatcher := createWatch(storage.Everything) 1376 defer allEventsWatcher.Stop() 1377 fooEventsWatcher := createWatch(fooPredicate) 1378 defer fooEventsWatcher.Stop() 1379 barEventsWatcher := createWatch(barPredicate) 1380 defer barEventsWatcher.Stop() 1381 1382 makePod := func(labels map[string]string, rv string) *examplev1.Pod { 1383 return &examplev1.Pod{ 1384 ObjectMeta: metav1.ObjectMeta{ 1385 Name: "pod", 1386 Namespace: "ns", 1387 Labels: labels, 1388 ResourceVersion: rv, 1389 }, 1390 } 1391 } 1392 pod1 := makePod(map[string]string{"foo": "true", "bar": "true"}, "1001") 1393 pod2 := makePod(map[string]string{"foo": "true"}, "1002") 1394 pod3 := makePod(map[string]string{}, "1003") 1395 pod4 := makePod(map[string]string{}, "1004") 1396 pod1DeletedAt2 := pod1.DeepCopyObject().(*examplev1.Pod) 1397 pod1DeletedAt2.ResourceVersion = "1002" 1398 pod2DeletedAt3 := pod2.DeepCopyObject().(*examplev1.Pod) 1399 pod2DeletedAt3.ResourceVersion = "1003" 1400 1401 allEvents := []watch.Event{ 1402 {Type: watch.Added, Object: pod1.DeepCopy()}, 1403 {Type: watch.Modified, Object: pod2.DeepCopy()}, 1404 {Type: watch.Modified, Object: pod3.DeepCopy()}, 1405 {Type: watch.Deleted, Object: pod4.DeepCopy()}, 1406 } 1407 fooEvents := []watch.Event{ 1408 {Type: watch.Added, Object: pod1.DeepCopy()}, 1409 {Type: watch.Modified, Object: pod2.DeepCopy()}, 1410 {Type: watch.Deleted, Object: pod2DeletedAt3.DeepCopy()}, 1411 } 1412 barEvents := []watch.Event{ 1413 {Type: watch.Added, Object: pod1.DeepCopy()}, 1414 {Type: watch.Deleted, Object: pod1DeletedAt2.DeepCopy()}, 1415 } 1416 1417 cacher.watchCache.Add(pod1) 1418 cacher.watchCache.Update(pod2) 1419 cacher.watchCache.Update(pod3) 1420 cacher.watchCache.Delete(pod4) 1421 1422 verifyEvents(t, allEventsWatcher, allEvents, true) 1423 verifyEvents(t, fooEventsWatcher, fooEvents, true) 1424 verifyEvents(t, barEventsWatcher, barEvents, true) 1425 } 1426 1427 func testCachingObjects(t *testing.T, watchersCount int) { 1428 backingStorage := &dummyStorage{} 1429 cacher, _, err := newTestCacher(backingStorage) 1430 if err != nil { 1431 t.Fatalf("Couldn't create cacher: %v", err) 1432 } 1433 defer cacher.Stop() 1434 1435 // Wait until cacher is initialized. 1436 if err := cacher.ready.wait(context.Background()); err != nil { 1437 t.Fatalf("unexpected error waiting for the cache to be ready") 1438 } 1439 1440 dispatchedEvents := []*watchCacheEvent{} 1441 cacher.watchCache.eventHandler = func(event *watchCacheEvent) { 1442 dispatchedEvents = append(dispatchedEvents, event) 1443 cacher.processEvent(event) 1444 } 1445 1446 watchers := make([]watch.Interface, 0, watchersCount) 1447 for i := 0; i < watchersCount; i++ { 1448 w, err := cacher.Watch(context.TODO(), "pods/ns", storage.ListOptions{ResourceVersion: "1000", Predicate: storage.Everything}) 1449 if err != nil { 1450 t.Fatalf("Failed to create watch: %v", err) 1451 } 1452 defer w.Stop() 1453 watchers = append(watchers, w) 1454 } 1455 1456 makePod := func(name, rv string) *examplev1.Pod { 1457 return &examplev1.Pod{ 1458 ObjectMeta: metav1.ObjectMeta{ 1459 Name: name, 1460 Namespace: "ns", 1461 ResourceVersion: rv, 1462 }, 1463 } 1464 } 1465 pod1 := makePod("pod", "1001") 1466 pod2 := makePod("pod", "1002") 1467 pod3 := makePod("pod", "1003") 1468 1469 cacher.watchCache.Add(pod1) 1470 cacher.watchCache.Update(pod2) 1471 cacher.watchCache.Delete(pod3) 1472 1473 // At this point, we already have dispatchedEvents fully propagated. 1474 1475 verifyEvents := func(w watch.Interface) { 1476 var event watch.Event 1477 for index := range dispatchedEvents { 1478 select { 1479 case event = <-w.ResultChan(): 1480 case <-time.After(wait.ForeverTestTimeout): 1481 t.Fatalf("timeout watiching for the event") 1482 } 1483 1484 var object runtime.Object 1485 if _, ok := event.Object.(runtime.CacheableObject); !ok { 1486 t.Fatalf("Object in %s event should support caching: %#v", event.Type, event.Object) 1487 } 1488 object = event.Object.(runtime.CacheableObject).GetObject() 1489 1490 if event.Type == watch.Deleted { 1491 resourceVersion, err := cacher.versioner.ObjectResourceVersion(cacher.watchCache.cache[index].PrevObject) 1492 if err != nil { 1493 t.Fatalf("Failed to parse resource version: %v", err) 1494 } 1495 updateResourceVersion(object, cacher.versioner, resourceVersion) 1496 } 1497 1498 var e runtime.Object 1499 switch event.Type { 1500 case watch.Added, watch.Modified: 1501 e = cacher.watchCache.cache[index].Object 1502 case watch.Deleted: 1503 e = cacher.watchCache.cache[index].PrevObject 1504 default: 1505 t.Errorf("unexpected watch event: %#v", event) 1506 } 1507 if a := object; !reflect.DeepEqual(a, e) { 1508 t.Errorf("event object messed up for %s: %#v, expected: %#v", event.Type, a, e) 1509 } 1510 } 1511 } 1512 1513 for i := range watchers { 1514 verifyEvents(watchers[i]) 1515 } 1516 } 1517 1518 func TestCachingObjects(t *testing.T) { 1519 t.Run("single watcher", func(t *testing.T) { testCachingObjects(t, 1) }) 1520 t.Run("many watcher", func(t *testing.T) { testCachingObjects(t, 3) }) 1521 } 1522 1523 func TestCacheIntervalInvalidationStopsWatch(t *testing.T) { 1524 backingStorage := &dummyStorage{} 1525 cacher, _, err := newTestCacher(backingStorage) 1526 if err != nil { 1527 t.Fatalf("Couldn't create cacher: %v", err) 1528 } 1529 defer cacher.Stop() 1530 1531 // Wait until cacher is initialized. 1532 if err := cacher.ready.wait(context.Background()); err != nil { 1533 t.Fatalf("unexpected error waiting for the cache to be ready") 1534 } 1535 // Ensure there is enough budget for slow processing since 1536 // the entire watch cache is going to be served through the 1537 // interval and events won't be popped from the cacheWatcher's 1538 // input channel until much later. 1539 cacher.dispatchTimeoutBudget.returnUnused(100 * time.Millisecond) 1540 1541 // We define a custom index validator such that the interval is 1542 // able to serve the first bufferSize elements successfully, but 1543 // on trying to fill it's buffer again, the indexValidator simulates 1544 // an invalidation leading to the watch being closed and the number 1545 // of events we actually process to be bufferSize, each event of 1546 // type watch.Added. 1547 valid := true 1548 invalidateCacheInterval := func() { 1549 valid = false 1550 } 1551 once := sync.Once{} 1552 indexValidator := func(index int) bool { 1553 isValid := valid && (index >= cacher.watchCache.startIndex) 1554 once.Do(invalidateCacheInterval) 1555 return isValid 1556 } 1557 cacher.watchCache.indexValidator = indexValidator 1558 1559 makePod := func(i int) *examplev1.Pod { 1560 return &examplev1.Pod{ 1561 ObjectMeta: metav1.ObjectMeta{ 1562 Name: fmt.Sprintf("pod-%d", 1000+i), 1563 Namespace: "ns", 1564 ResourceVersion: fmt.Sprintf("%d", 1000+i), 1565 }, 1566 } 1567 } 1568 1569 // 250 is arbitrary, point is to have enough elements such that 1570 // it generates more than bufferSize number of events allowing 1571 // us to simulate the invalidation of the cache interval. 1572 totalPods := 250 1573 for i := 0; i < totalPods; i++ { 1574 err := cacher.watchCache.Add(makePod(i)) 1575 if err != nil { 1576 t.Errorf("error: %v", err) 1577 } 1578 } 1579 ctx, cancel := context.WithCancel(context.Background()) 1580 defer cancel() 1581 1582 w, err := cacher.Watch(ctx, "pods/ns", storage.ListOptions{ 1583 ResourceVersion: "999", 1584 Predicate: storage.Everything, 1585 }) 1586 if err != nil { 1587 t.Fatalf("Failed to create watch: %v", err) 1588 } 1589 defer w.Stop() 1590 1591 received := 0 1592 resChan := w.ResultChan() 1593 for event := range resChan { 1594 received++ 1595 t.Logf("event type: %v, events received so far: %d", event.Type, received) 1596 if event.Type != watch.Added { 1597 t.Errorf("unexpected event type, expected: %s, got: %s, event: %v", watch.Added, event.Type, event) 1598 } 1599 } 1600 // Since the watch is stopped after the interval is invalidated, 1601 // we should have processed exactly bufferSize number of elements. 1602 if received != bufferSize { 1603 t.Errorf("unexpected number of events received, expected: %d, got: %d", bufferSize+1, received) 1604 } 1605 } 1606 1607 func TestWaitUntilWatchCacheFreshAndForceAllEvents(t *testing.T) { 1608 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WatchList, true)() 1609 backingStorage := &dummyStorage{} 1610 cacher, _, err := newTestCacher(backingStorage) 1611 if err != nil { 1612 t.Fatalf("Couldn't create cacher: %v", err) 1613 } 1614 defer cacher.Stop() 1615 1616 opts := storage.ListOptions{ 1617 Predicate: storage.Everything, 1618 SendInitialEvents: pointer.Bool(true), 1619 ResourceVersion: "105", 1620 } 1621 opts.Predicate.AllowWatchBookmarks = true 1622 1623 w, err := cacher.Watch(context.Background(), "pods/ns", opts) 1624 require.NoError(t, err, "failed to create watch: %v") 1625 defer w.Stop() 1626 verifyEvents(t, w, []watch.Event{ 1627 { 1628 Type: watch.Error, 1629 Object: &metav1.Status{ 1630 Status: metav1.StatusFailure, 1631 Message: storage.NewTooLargeResourceVersionError(105, 100, resourceVersionTooHighRetrySeconds).Error(), 1632 Details: storage.NewTooLargeResourceVersionError(105, 100, resourceVersionTooHighRetrySeconds).(*apierrors.StatusError).Status().Details, 1633 Reason: metav1.StatusReasonTimeout, 1634 Code: 504, 1635 }, 1636 }, 1637 }, true) 1638 1639 go func() { 1640 cacher.watchCache.Add(makeTestPodDetails("pod1", 105, "node1", map[string]string{"label": "value1"})) 1641 }() 1642 w, err = cacher.Watch(context.Background(), "pods/ns", opts) 1643 require.NoError(t, err, "failed to create watch: %v") 1644 defer w.Stop() 1645 verifyEvents(t, w, []watch.Event{ 1646 { 1647 Type: watch.Added, 1648 Object: makeTestPodDetails("pod1", 105, "node1", map[string]string{"label": "value1"}), 1649 }, 1650 }, true) 1651 } 1652 1653 type fakeStorage struct { 1654 pods []example.Pod 1655 storage.Interface 1656 } 1657 1658 func newObjectStorage(fakePods []example.Pod) *fakeStorage { 1659 return &fakeStorage{ 1660 pods: fakePods, 1661 } 1662 } 1663 1664 func (m fakeStorage) GetList(ctx context.Context, key string, opts storage.ListOptions, listObj runtime.Object) error { 1665 podList := listObj.(*example.PodList) 1666 podList.ListMeta = metav1.ListMeta{ResourceVersion: "12345"} 1667 podList.Items = m.pods 1668 return nil 1669 } 1670 func (m fakeStorage) Watch(_ context.Context, _ string, _ storage.ListOptions) (watch.Interface, error) { 1671 return newDummyWatch(), nil 1672 } 1673 1674 func BenchmarkCacher_GetList(b *testing.B) { 1675 testCases := []struct { 1676 totalObjectNum int 1677 expectObjectNum int 1678 }{ 1679 { 1680 totalObjectNum: 5000, 1681 expectObjectNum: 50, 1682 }, 1683 { 1684 totalObjectNum: 5000, 1685 expectObjectNum: 500, 1686 }, 1687 { 1688 totalObjectNum: 5000, 1689 expectObjectNum: 1000, 1690 }, 1691 { 1692 totalObjectNum: 5000, 1693 expectObjectNum: 2500, 1694 }, 1695 { 1696 totalObjectNum: 5000, 1697 expectObjectNum: 5000, 1698 }, 1699 } 1700 for _, tc := range testCases { 1701 b.Run( 1702 fmt.Sprintf("totalObjectNum=%d, expectObjectNum=%d", tc.totalObjectNum, tc.expectObjectNum), 1703 func(b *testing.B) { 1704 // create sample pods 1705 fakePods := make([]example.Pod, tc.totalObjectNum, tc.totalObjectNum) 1706 for i := range fakePods { 1707 fakePods[i].Namespace = "default" 1708 fakePods[i].Name = fmt.Sprintf("pod-%d", i) 1709 fakePods[i].ResourceVersion = strconv.Itoa(i) 1710 if i%(tc.totalObjectNum/tc.expectObjectNum) == 0 { 1711 fakePods[i].Spec.NodeName = "node-0" 1712 } 1713 data := make([]byte, 1024*2, 1024*2) // 2k labels 1714 rand.Read(data) 1715 fakePods[i].Spec.NodeSelector = map[string]string{ 1716 "key": string(data), 1717 } 1718 } 1719 1720 // build test cacher 1721 cacher, _, err := newTestCacher(newObjectStorage(fakePods)) 1722 if err != nil { 1723 b.Fatalf("new cacher: %v", err) 1724 } 1725 defer cacher.Stop() 1726 1727 // prepare result and pred 1728 parsedField, err := fields.ParseSelector("spec.nodeName=node-0") 1729 if err != nil { 1730 b.Fatalf("parse selector: %v", err) 1731 } 1732 pred := storage.SelectionPredicate{ 1733 Label: labels.Everything(), 1734 Field: parsedField, 1735 } 1736 1737 // now we start benchmarking 1738 b.ResetTimer() 1739 for i := 0; i < b.N; i++ { 1740 result := &example.PodList{} 1741 err = cacher.GetList(context.TODO(), "pods", storage.ListOptions{ 1742 Predicate: pred, 1743 Recursive: true, 1744 ResourceVersion: "12345", 1745 }, result) 1746 if err != nil { 1747 b.Fatalf("GetList cache: %v", err) 1748 } 1749 if len(result.Items) != tc.expectObjectNum { 1750 b.Fatalf("expect %d but got %d", tc.expectObjectNum, len(result.Items)) 1751 } 1752 } 1753 }) 1754 } 1755 } 1756 1757 // TestDoNotPopExpiredWatchersWhenNoEventsSeen makes sure that 1758 // a bookmark event will be delivered after the cacher has seen an event. 1759 // Previously the watchers have been removed from the "want bookmark" queue. 1760 func TestDoNotPopExpiredWatchersWhenNoEventsSeen(t *testing.T) { 1761 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.WatchList, true)() 1762 backingStorage := &dummyStorage{} 1763 cacher, _, err := newTestCacher(backingStorage) 1764 if err != nil { 1765 t.Fatalf("Couldn't create cacher: %v", err) 1766 } 1767 defer cacher.Stop() 1768 1769 // wait until cacher is initialized. 1770 if err := cacher.ready.wait(context.Background()); err != nil { 1771 t.Fatalf("unexpected error waiting for the cache to be ready") 1772 } 1773 1774 pred := storage.Everything 1775 pred.AllowWatchBookmarks = true 1776 opts := storage.ListOptions{ 1777 Predicate: pred, 1778 SendInitialEvents: pointer.Bool(true), 1779 } 1780 w, err := cacher.Watch(context.Background(), "pods/ns", opts) 1781 require.NoError(t, err, "failed to create watch: %v") 1782 defer w.Stop() 1783 1784 // Ensure that popExpiredWatchers is called to ensure that our watch isn't removed from bookmarkWatchers. 1785 // We do that every ~1s, so waiting 2 seconds seems enough. 1786 time.Sleep(2 * time.Second) 1787 1788 // Send an event to ensure that lastProcessedResourceVersion in Cacher will change to non-zero value. 1789 makePod := func(rv uint64) *example.Pod { 1790 return &example.Pod{ 1791 ObjectMeta: metav1.ObjectMeta{ 1792 Name: fmt.Sprintf("pod-%d", rv), 1793 Namespace: "ns", 1794 ResourceVersion: fmt.Sprintf("%d", rv), 1795 Annotations: map[string]string{}, 1796 }, 1797 } 1798 } 1799 err = cacher.watchCache.Add(makePod(102)) 1800 require.NoError(t, err) 1801 1802 verifyEvents(t, w, []watch.Event{ 1803 {Type: watch.Added, Object: makePod(102)}, 1804 {Type: watch.Bookmark, Object: &example.Pod{ 1805 ObjectMeta: metav1.ObjectMeta{ 1806 ResourceVersion: "102", 1807 Annotations: map[string]string{"k8s.io/initial-events-end": "true"}, 1808 }, 1809 }}, 1810 }, true) 1811 } 1812 1813 func TestForgetWatcher(t *testing.T) { 1814 backingStorage := &dummyStorage{} 1815 cacher, _, err := newTestCacher(backingStorage) 1816 require.NoError(t, err) 1817 defer cacher.Stop() 1818 1819 // wait until cacher is initialized. 1820 if err := cacher.ready.wait(context.Background()); err != nil { 1821 t.Fatalf("unexpected error waiting for the cache to be ready") 1822 } 1823 1824 assertCacherInternalState := func(expectedWatchersCounter, expectedValueWatchersCounter int) { 1825 cacher.Lock() 1826 defer cacher.Unlock() 1827 1828 require.Equal(t, expectedWatchersCounter, len(cacher.watchers.allWatchers)) 1829 require.Equal(t, expectedValueWatchersCounter, len(cacher.watchers.valueWatchers)) 1830 } 1831 assertCacherInternalState(0, 0) 1832 1833 var forgetWatcherFn func(bool) 1834 var forgetCounter int 1835 forgetWatcherWrapped := func(drainWatcher bool) { 1836 forgetCounter++ 1837 forgetWatcherFn(drainWatcher) 1838 } 1839 w := newCacheWatcher( 1840 0, 1841 func(_ string, _ labels.Set, _ fields.Set) bool { return true }, 1842 nil, 1843 storage.APIObjectVersioner{}, 1844 testingclock.NewFakeClock(time.Now()).Now().Add(2*time.Minute), 1845 true, 1846 schema.GroupResource{Resource: "pods"}, 1847 "1", 1848 ) 1849 forgetWatcherFn = forgetWatcher(cacher, w, 0, namespacedName{}, "", false) 1850 addWatcher := func(w *cacheWatcher) { 1851 cacher.Lock() 1852 defer cacher.Unlock() 1853 1854 cacher.watchers.addWatcher(w, 0, namespacedName{}, "", false) 1855 } 1856 1857 addWatcher(w) 1858 assertCacherInternalState(1, 0) 1859 1860 forgetWatcherWrapped(false) 1861 assertCacherInternalState(0, 0) 1862 require.Equal(t, 1, forgetCounter) 1863 1864 forgetWatcherWrapped(false) 1865 assertCacherInternalState(0, 0) 1866 require.Equal(t, 2, forgetCounter) 1867 } 1868 1869 func TestWatchStreamSeparation(t *testing.T) { 1870 tcs := []struct { 1871 name string 1872 separateCacheWatchRPC bool 1873 useWatchCacheContextMetadata bool 1874 expectBookmarkOnWatchCache bool 1875 expectBookmarkOnEtcd bool 1876 }{ 1877 { 1878 name: "common RPC > both get bookmarks", 1879 separateCacheWatchRPC: false, 1880 expectBookmarkOnEtcd: true, 1881 expectBookmarkOnWatchCache: true, 1882 }, 1883 { 1884 name: "common RPC & watch cache context > both get bookmarks", 1885 separateCacheWatchRPC: false, 1886 useWatchCacheContextMetadata: true, 1887 expectBookmarkOnEtcd: true, 1888 expectBookmarkOnWatchCache: true, 1889 }, 1890 { 1891 name: "separate RPC > only etcd gets bookmarks", 1892 separateCacheWatchRPC: true, 1893 expectBookmarkOnEtcd: true, 1894 expectBookmarkOnWatchCache: false, 1895 }, 1896 { 1897 name: "separate RPC & watch cache context > only watch cache gets bookmarks", 1898 separateCacheWatchRPC: true, 1899 useWatchCacheContextMetadata: true, 1900 expectBookmarkOnEtcd: false, 1901 expectBookmarkOnWatchCache: true, 1902 }, 1903 } 1904 for _, tc := range tcs { 1905 t.Run(tc.name, func(t *testing.T) { 1906 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SeparateCacheWatchRPC, tc.separateCacheWatchRPC)() 1907 _, cacher, _, terminate := testSetupWithEtcdServer(t) 1908 t.Cleanup(terminate) 1909 if err := cacher.ready.wait(context.TODO()); err != nil { 1910 t.Fatalf("unexpected error waiting for the cache to be ready") 1911 } 1912 1913 getCacherRV := func() uint64 { 1914 cacher.watchCache.RLock() 1915 defer cacher.watchCache.RUnlock() 1916 return cacher.watchCache.resourceVersion 1917 } 1918 waitContext, cancel := context.WithTimeout(context.Background(), 2*time.Second) 1919 defer cancel() 1920 waitForEtcdBookmark := watchAndWaitForBookmark(t, waitContext, cacher.storage) 1921 1922 var out example.Pod 1923 err := cacher.Create(context.Background(), "foo", &example.Pod{}, &out, 0) 1924 if err != nil { 1925 t.Fatal(err) 1926 } 1927 versioner := storage.APIObjectVersioner{} 1928 var lastResourceVersion uint64 1929 lastResourceVersion, err = versioner.ObjectResourceVersion(&out) 1930 if err != nil { 1931 t.Fatal(err) 1932 } 1933 1934 var contextMetadata metadata.MD 1935 if tc.useWatchCacheContextMetadata { 1936 contextMetadata = cacher.watchCache.waitingUntilFresh.contextMetadata 1937 } 1938 // Wait before sending watch progress request to avoid https://github.com/etcd-io/etcd/issues/17507 1939 // TODO(https://github.com/etcd-io/etcd/issues/17507): Remove sleep when etcd is upgraded to version with fix. 1940 time.Sleep(time.Second) 1941 err = cacher.storage.RequestWatchProgress(metadata.NewOutgoingContext(context.Background(), contextMetadata)) 1942 if err != nil { 1943 t.Fatal(err) 1944 } 1945 // Give time for bookmark to arrive 1946 time.Sleep(time.Second) 1947 1948 etcdWatchResourceVersion := waitForEtcdBookmark() 1949 gotEtcdWatchBookmark := etcdWatchResourceVersion == lastResourceVersion 1950 if gotEtcdWatchBookmark != tc.expectBookmarkOnEtcd { 1951 t.Errorf("Unexpected etcd bookmark check result, rv: %d, got: %v, want: %v", etcdWatchResourceVersion, etcdWatchResourceVersion, tc.expectBookmarkOnEtcd) 1952 } 1953 1954 watchCacheResourceVersion := getCacherRV() 1955 cacherGotBookmark := watchCacheResourceVersion == lastResourceVersion 1956 if cacherGotBookmark != tc.expectBookmarkOnWatchCache { 1957 t.Errorf("Unexpected watch cache bookmark check result, rv: %d, got: %v, want: %v", watchCacheResourceVersion, cacherGotBookmark, tc.expectBookmarkOnWatchCache) 1958 } 1959 }) 1960 } 1961 } 1962 1963 func watchAndWaitForBookmark(t *testing.T, ctx context.Context, etcdStorage storage.Interface) func() (resourceVersion uint64) { 1964 opts := storage.ListOptions{ResourceVersion: "", Predicate: storage.Everything, Recursive: true} 1965 opts.Predicate.AllowWatchBookmarks = true 1966 w, err := etcdStorage.Watch(ctx, "/pods/", opts) 1967 if err != nil { 1968 t.Fatal(err) 1969 } 1970 1971 versioner := storage.APIObjectVersioner{} 1972 var rv uint64 1973 var wg sync.WaitGroup 1974 wg.Add(1) 1975 go func() { 1976 defer wg.Done() 1977 for event := range w.ResultChan() { 1978 if event.Type == watch.Bookmark { 1979 rv, err = versioner.ObjectResourceVersion(event.Object) 1980 break 1981 } 1982 } 1983 }() 1984 return func() (resourceVersion uint64) { 1985 defer w.Stop() 1986 wg.Wait() 1987 if err != nil { 1988 t.Fatal(err) 1989 } 1990 return rv 1991 } 1992 }