@@ -22,15 +22,17 @@ import (
22
22
"math/rand"
23
23
"reflect"
24
24
"strconv"
25
+ "syscall"
25
26
"testing"
26
27
"time"
27
28
28
- "k8s.io/api/core/v1"
29
+ v1 "k8s.io/api/core/v1"
29
30
apierrors "k8s.io/apimachinery/pkg/api/errors"
30
31
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
31
32
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
32
33
"k8s.io/apimachinery/pkg/runtime"
33
34
"k8s.io/apimachinery/pkg/runtime/schema"
35
+ "k8s.io/apimachinery/pkg/util/clock"
34
36
"k8s.io/apimachinery/pkg/util/wait"
35
37
"k8s.io/apimachinery/pkg/watch"
36
38
)
@@ -358,6 +360,82 @@ func TestReflectorListAndWatchWithErrors(t *testing.T) {
358
360
}
359
361
}
360
362
363
+ func TestReflectorListAndWatchInitConnBackoff (t * testing.T ) {
364
+ maxBackoff := 50 * time .Millisecond
365
+ table := []struct {
366
+ numConnFails int
367
+ expLowerBound time.Duration
368
+ expUpperBound time.Duration
369
+ }{
370
+ {5 , 32 * time .Millisecond , 64 * time .Millisecond }, // case where maxBackoff is not hit, time should grow exponentially
371
+ {40 , 35 * 2 * maxBackoff , 40 * 2 * maxBackoff }, // case where maxBoff is hit, backoff time should flatten
372
+
373
+ }
374
+ for _ , test := range table {
375
+ t .Run (fmt .Sprintf ("%d connection failures takes at least %d ms" , test .numConnFails , 1 << test .numConnFails ),
376
+ func (t * testing.T ) {
377
+ stopCh := make (chan struct {})
378
+ connFails := test .numConnFails
379
+ fakeClock := clock .NewFakeClock (time .Unix (0 , 0 ))
380
+ bm := wait .NewExponentialBackoffManager (time .Millisecond , maxBackoff , 100 * time .Millisecond , 2.0 , 1.0 , fakeClock )
381
+ done := make (chan struct {})
382
+ defer close (done )
383
+ go func () {
384
+ i := 0
385
+ for {
386
+ select {
387
+ case <- done :
388
+ return
389
+ default :
390
+ }
391
+ if fakeClock .HasWaiters () {
392
+ step := (1 << (i + 1 )) * time .Millisecond
393
+ if step > maxBackoff * 2 {
394
+ step = maxBackoff * 2
395
+ }
396
+ fakeClock .Step (step )
397
+ i ++
398
+ }
399
+ time .Sleep (100 * time .Microsecond )
400
+ }
401
+ }()
402
+ lw := & testLW {
403
+ WatchFunc : func (options metav1.ListOptions ) (watch.Interface , error ) {
404
+ if connFails > 0 {
405
+ connFails --
406
+ return nil , syscall .ECONNREFUSED
407
+ }
408
+ close (stopCh )
409
+ return watch .NewFake (), nil
410
+ },
411
+ ListFunc : func (options metav1.ListOptions ) (runtime.Object , error ) {
412
+ return & v1.PodList {ListMeta : metav1.ListMeta {ResourceVersion : "1" }}, nil
413
+ },
414
+ }
415
+ r := & Reflector {
416
+ name : "test-reflector" ,
417
+ listerWatcher : lw ,
418
+ store : NewFIFO (MetaNamespaceKeyFunc ),
419
+ initConnBackoffManager : bm ,
420
+ clock : fakeClock ,
421
+ watchErrorHandler : WatchErrorHandler (DefaultWatchErrorHandler ),
422
+ }
423
+ start := fakeClock .Now ()
424
+ err := r .ListAndWatch (stopCh )
425
+ elapsed := fakeClock .Since (start )
426
+ if err != nil {
427
+ t .Errorf ("unexpected error %v" , err )
428
+ }
429
+ if elapsed < (test .expLowerBound ) {
430
+ t .Errorf ("expected lower bound of ListAndWatch: %v, got %v" , test .expLowerBound , elapsed )
431
+ }
432
+ if elapsed > (test .expUpperBound ) {
433
+ t .Errorf ("expected upper bound of ListAndWatch: %v, got %v" , test .expUpperBound , elapsed )
434
+ }
435
+ })
436
+ }
437
+ }
438
+
361
439
func TestReflectorResync (t * testing.T ) {
362
440
iteration := 0
363
441
stopCh := make (chan struct {})
0 commit comments