k8s.io/client-go@v0.31.1/tools/cache/reflector_test.go (about)

     1  /*
     2  Copyright 2014 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 cache
    18  
    19  import (
    20  	"context"
    21  	"errors"
    22  	"fmt"
    23  	"math/rand"
    24  	"reflect"
    25  	goruntime "runtime"
    26  	"strconv"
    27  	"syscall"
    28  	"testing"
    29  	"time"
    30  
    31  	"github.com/stretchr/testify/assert"
    32  	"github.com/stretchr/testify/require"
    33  
    34  	v1 "k8s.io/api/core/v1"
    35  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    36  	"k8s.io/apimachinery/pkg/api/meta"
    37  	"k8s.io/apimachinery/pkg/api/resource"
    38  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    39  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    40  	"k8s.io/apimachinery/pkg/runtime"
    41  	"k8s.io/apimachinery/pkg/runtime/schema"
    42  	"k8s.io/apimachinery/pkg/util/sets"
    43  	"k8s.io/apimachinery/pkg/util/wait"
    44  	"k8s.io/apimachinery/pkg/watch"
    45  	"k8s.io/utils/clock"
    46  	testingclock "k8s.io/utils/clock/testing"
    47  )
    48  
    49  var nevererrc chan error
    50  
    51  type testLW struct {
    52  	ListFunc  func(options metav1.ListOptions) (runtime.Object, error)
    53  	WatchFunc func(options metav1.ListOptions) (watch.Interface, error)
    54  }
    55  
    56  func (t *testLW) List(options metav1.ListOptions) (runtime.Object, error) {
    57  	return t.ListFunc(options)
    58  }
    59  func (t *testLW) Watch(options metav1.ListOptions) (watch.Interface, error) {
    60  	return t.WatchFunc(options)
    61  }
    62  
    63  func TestCloseWatchChannelOnError(t *testing.T) {
    64  	r := NewReflector(&testLW{}, &v1.Pod{}, NewStore(MetaNamespaceKeyFunc), 0)
    65  	pod := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}}
    66  	fw := watch.NewFake()
    67  	r.listerWatcher = &testLW{
    68  		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
    69  			return fw, nil
    70  		},
    71  		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
    72  			return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "1"}}, nil
    73  		},
    74  	}
    75  	go r.ListAndWatch(wait.NeverStop)
    76  	fw.Error(pod)
    77  	select {
    78  	case _, ok := <-fw.ResultChan():
    79  		if ok {
    80  			t.Errorf("Watch channel left open after cancellation")
    81  		}
    82  	case <-time.After(wait.ForeverTestTimeout):
    83  		t.Errorf("the cancellation is at least %s late", wait.ForeverTestTimeout.String())
    84  		break
    85  	}
    86  }
    87  
    88  func TestRunUntil(t *testing.T) {
    89  	stopCh := make(chan struct{})
    90  	store := NewStore(MetaNamespaceKeyFunc)
    91  	r := NewReflector(&testLW{}, &v1.Pod{}, store, 0)
    92  	fw := watch.NewFake()
    93  	r.listerWatcher = &testLW{
    94  		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
    95  			return fw, nil
    96  		},
    97  		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
    98  			return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "1"}}, nil
    99  		},
   100  	}
   101  	doneCh := make(chan struct{})
   102  	go func() {
   103  		defer close(doneCh)
   104  		r.Run(stopCh)
   105  	}()
   106  	// Synchronously add a dummy pod into the watch channel so we
   107  	// know the RunUntil go routine is in the watch handler.
   108  	fw.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}})
   109  
   110  	close(stopCh)
   111  	resultCh := fw.ResultChan()
   112  	for {
   113  		select {
   114  		case <-doneCh:
   115  			if resultCh == nil {
   116  				return // both closed
   117  			}
   118  			doneCh = nil
   119  		case _, ok := <-resultCh:
   120  			if ok {
   121  				t.Fatalf("Watch channel left open after stopping the watch")
   122  			}
   123  			if doneCh == nil {
   124  				return // both closed
   125  			}
   126  			resultCh = nil
   127  		case <-time.After(wait.ForeverTestTimeout):
   128  			t.Fatalf("the cancellation is at least %s late", wait.ForeverTestTimeout.String())
   129  		}
   130  	}
   131  }
   132  
   133  func TestReflectorResyncChan(t *testing.T) {
   134  	s := NewStore(MetaNamespaceKeyFunc)
   135  	g := NewReflector(&testLW{}, &v1.Pod{}, s, time.Millisecond)
   136  	a, _ := g.resyncChan()
   137  	b := time.After(wait.ForeverTestTimeout)
   138  	select {
   139  	case <-a:
   140  		t.Logf("got timeout as expected")
   141  	case <-b:
   142  		t.Errorf("resyncChan() is at least 99 milliseconds late??")
   143  	}
   144  }
   145  
   146  // TestReflectorWatchStoppedBefore ensures that neither List nor Watch are
   147  // called if the stop channel is closed before Reflector.watch is called.
   148  func TestReflectorWatchStoppedBefore(t *testing.T) {
   149  	stopCh := make(chan struct{})
   150  	close(stopCh)
   151  
   152  	lw := &ListWatch{
   153  		ListFunc: func(_ metav1.ListOptions) (runtime.Object, error) {
   154  			t.Fatal("ListFunc called unexpectedly")
   155  			return nil, nil
   156  		},
   157  		WatchFunc: func(_ metav1.ListOptions) (watch.Interface, error) {
   158  			// If WatchFunc is never called, the watcher it returns doesn't need to be stopped.
   159  			t.Fatal("WatchFunc called unexpectedly")
   160  			return nil, nil
   161  		},
   162  	}
   163  	target := NewReflector(lw, &v1.Pod{}, nil, 0)
   164  
   165  	err := target.watch(nil, stopCh, nil)
   166  	require.NoError(t, err)
   167  }
   168  
   169  // TestReflectorWatchStoppedAfter ensures that neither the watcher is stopped if
   170  // the stop channel is closed after Reflector.watch has started watching.
   171  func TestReflectorWatchStoppedAfter(t *testing.T) {
   172  	stopCh := make(chan struct{})
   173  
   174  	var watchers []*watch.FakeWatcher
   175  
   176  	lw := &ListWatch{
   177  		ListFunc: func(_ metav1.ListOptions) (runtime.Object, error) {
   178  			t.Fatal("ListFunc called unexpectedly")
   179  			return nil, nil
   180  		},
   181  		WatchFunc: func(_ metav1.ListOptions) (watch.Interface, error) {
   182  			// Simulate the stop channel being closed after watching has started
   183  			go func() {
   184  				time.Sleep(10 * time.Millisecond)
   185  				close(stopCh)
   186  			}()
   187  			// Use a fake watcher that never sends events
   188  			w := watch.NewFake()
   189  			watchers = append(watchers, w)
   190  			return w, nil
   191  		},
   192  	}
   193  	target := NewReflector(lw, &v1.Pod{}, nil, 0)
   194  
   195  	err := target.watch(nil, stopCh, nil)
   196  	require.NoError(t, err)
   197  	require.Len(t, watchers, 1)
   198  	require.True(t, watchers[0].IsStopped())
   199  }
   200  
   201  func BenchmarkReflectorResyncChanMany(b *testing.B) {
   202  	s := NewStore(MetaNamespaceKeyFunc)
   203  	g := NewReflector(&testLW{}, &v1.Pod{}, s, 25*time.Millisecond)
   204  	// The improvement to this (calling the timer's Stop() method) makes
   205  	// this benchmark about 40% faster.
   206  	for i := 0; i < b.N; i++ {
   207  		g.resyncPeriod = time.Duration(rand.Float64() * float64(time.Millisecond) * 25)
   208  		_, stop := g.resyncChan()
   209  		stop()
   210  	}
   211  }
   212  
   213  // TestReflectorHandleWatchStoppedBefore ensures that handleWatch stops when
   214  // stopCh is already closed before handleWatch was called. It also ensures that
   215  // ResultChan is only called once and that Stop is called after ResultChan.
   216  func TestReflectorHandleWatchStoppedBefore(t *testing.T) {
   217  	s := NewStore(MetaNamespaceKeyFunc)
   218  	g := NewReflector(&testLW{}, &v1.Pod{}, s, 0)
   219  	stopCh := make(chan struct{})
   220  	// Simulate the watch channel being closed before the watchHandler is called
   221  	close(stopCh)
   222  	var calls []string
   223  	resultCh := make(chan watch.Event)
   224  	fw := watch.MockWatcher{
   225  		StopFunc: func() {
   226  			calls = append(calls, "Stop")
   227  			close(resultCh)
   228  		},
   229  		ResultChanFunc: func() <-chan watch.Event {
   230  			calls = append(calls, "ResultChan")
   231  			return resultCh
   232  		},
   233  	}
   234  	err := handleWatch(time.Now(), fw, s, g.expectedType, g.expectedGVK, g.name, g.typeDescription, g.setLastSyncResourceVersion, g.clock, nevererrc, stopCh)
   235  	if err == nil {
   236  		t.Errorf("unexpected non-error")
   237  	}
   238  	// Ensure the watcher methods are called exactly once in this exact order.
   239  	// TODO(karlkfi): Fix watchHandler to call Stop()
   240  	// assert.Equal(t, []string{"ResultChan", "Stop"}, calls)
   241  	assert.Equal(t, []string{"ResultChan"}, calls)
   242  }
   243  
   244  // TestReflectorHandleWatchStoppedAfter ensures that handleWatch stops when
   245  // stopCh is closed after handleWatch was called. It also ensures that
   246  // ResultChan is only called once and that Stop is called after ResultChan.
   247  func TestReflectorHandleWatchStoppedAfter(t *testing.T) {
   248  	s := NewStore(MetaNamespaceKeyFunc)
   249  	g := NewReflector(&testLW{}, &v1.Pod{}, s, 0)
   250  	var calls []string
   251  	stopCh := make(chan struct{})
   252  	resultCh := make(chan watch.Event)
   253  	fw := watch.MockWatcher{
   254  		StopFunc: func() {
   255  			calls = append(calls, "Stop")
   256  			close(resultCh)
   257  		},
   258  		ResultChanFunc: func() <-chan watch.Event {
   259  			calls = append(calls, "ResultChan")
   260  			resultCh = make(chan watch.Event)
   261  			// Simulate the watch handler being stopped asynchronously by the
   262  			// caller, after watching has started.
   263  			go func() {
   264  				time.Sleep(10 * time.Millisecond)
   265  				close(stopCh)
   266  			}()
   267  			return resultCh
   268  		},
   269  	}
   270  	err := handleWatch(time.Now(), fw, s, g.expectedType, g.expectedGVK, g.name, g.typeDescription, g.setLastSyncResourceVersion, g.clock, nevererrc, stopCh)
   271  	if err == nil {
   272  		t.Errorf("unexpected non-error")
   273  	}
   274  	// Ensure the watcher methods are called exactly once in this exact order.
   275  	// TODO(karlkfi): Fix watchHandler to call Stop()
   276  	// assert.Equal(t, []string{"ResultChan", "Stop"}, calls)
   277  	assert.Equal(t, []string{"ResultChan"}, calls)
   278  }
   279  
   280  // TestReflectorHandleWatchResultChanClosedBefore ensures that handleWatch
   281  // stops when the result channel is closed before handleWatch was called.
   282  func TestReflectorHandleWatchResultChanClosedBefore(t *testing.T) {
   283  	s := NewStore(MetaNamespaceKeyFunc)
   284  	g := NewReflector(&testLW{}, &v1.Pod{}, s, 0)
   285  	var calls []string
   286  	resultCh := make(chan watch.Event)
   287  	fw := watch.MockWatcher{
   288  		StopFunc: func() {
   289  			calls = append(calls, "Stop")
   290  		},
   291  		ResultChanFunc: func() <-chan watch.Event {
   292  			calls = append(calls, "ResultChan")
   293  			return resultCh
   294  		},
   295  	}
   296  	// Simulate the result channel being closed by the producer before handleWatch is called.
   297  	close(resultCh)
   298  	err := handleWatch(time.Now(), fw, s, g.expectedType, g.expectedGVK, g.name, g.typeDescription, g.setLastSyncResourceVersion, g.clock, nevererrc, wait.NeverStop)
   299  	if err == nil {
   300  		t.Errorf("unexpected non-error")
   301  	}
   302  	// Ensure the watcher methods are called exactly once in this exact order.
   303  	// TODO(karlkfi): Fix watchHandler to call Stop()
   304  	// assert.Equal(t, []string{"ResultChan", "Stop"}, calls)
   305  	assert.Equal(t, []string{"ResultChan"}, calls)
   306  }
   307  
   308  // TestReflectorHandleWatchResultChanClosedAfter ensures that handleWatch
   309  // stops when the result channel is closed after handleWatch has started watching.
   310  func TestReflectorHandleWatchResultChanClosedAfter(t *testing.T) {
   311  	s := NewStore(MetaNamespaceKeyFunc)
   312  	g := NewReflector(&testLW{}, &v1.Pod{}, s, 0)
   313  	var calls []string
   314  	resultCh := make(chan watch.Event)
   315  	fw := watch.MockWatcher{
   316  		StopFunc: func() {
   317  			calls = append(calls, "Stop")
   318  		},
   319  		ResultChanFunc: func() <-chan watch.Event {
   320  			calls = append(calls, "ResultChan")
   321  			resultCh = make(chan watch.Event)
   322  			// Simulate the result channel being closed by the producer, after
   323  			// watching has started.
   324  			go func() {
   325  				time.Sleep(10 * time.Millisecond)
   326  				close(resultCh)
   327  			}()
   328  			return resultCh
   329  		},
   330  	}
   331  	err := handleWatch(time.Now(), fw, s, g.expectedType, g.expectedGVK, g.name, g.typeDescription, g.setLastSyncResourceVersion, g.clock, nevererrc, wait.NeverStop)
   332  	if err == nil {
   333  		t.Errorf("unexpected non-error")
   334  	}
   335  	// Ensure the watcher methods are called exactly once in this exact order.
   336  	// TODO(karlkfi): Fix watchHandler to call Stop()
   337  	// assert.Equal(t, []string{"ResultChan", "Stop"}, calls)
   338  	assert.Equal(t, []string{"ResultChan"}, calls)
   339  }
   340  
   341  func TestReflectorWatchHandler(t *testing.T) {
   342  	s := NewStore(MetaNamespaceKeyFunc)
   343  	g := NewReflector(&testLW{}, &v1.Pod{}, s, 0)
   344  	// Wrap setLastSyncResourceVersion so we can tell the watchHandler to stop
   345  	// watching after all the events have been consumed. This avoids race
   346  	// conditions which can happen if the producer calls Stop(), instead of the
   347  	// consumer.
   348  	stopCh := make(chan struct{})
   349  	setLastSyncResourceVersion := func(rv string) {
   350  		g.setLastSyncResourceVersion(rv)
   351  		if rv == "32" {
   352  			close(stopCh)
   353  		}
   354  	}
   355  	fw := watch.NewFake()
   356  	s.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})
   357  	s.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}})
   358  	go func() {
   359  		fw.Add(&v1.Service{ObjectMeta: metav1.ObjectMeta{Name: "rejected"}})
   360  		fw.Delete(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}})
   361  		fw.Modify(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar", ResourceVersion: "55"}})
   362  		fw.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "baz", ResourceVersion: "32"}})
   363  		fw.Stop()
   364  	}()
   365  	err := handleWatch(time.Now(), fw, s, g.expectedType, g.expectedGVK, g.name, g.typeDescription, setLastSyncResourceVersion, g.clock, nevererrc, stopCh)
   366  	// TODO(karlkfi): Fix FakeWatcher to avoid race condition between watcher.Stop() & close(stopCh)
   367  	if err != nil && !errors.Is(err, errorStopRequested) {
   368  		t.Errorf("unexpected error %v", err)
   369  	}
   370  
   371  	mkPod := func(id string, rv string) *v1.Pod {
   372  		return &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: id, ResourceVersion: rv}}
   373  	}
   374  
   375  	// Validate that the Store was updated by the events
   376  	table := []struct {
   377  		Pod    *v1.Pod
   378  		exists bool
   379  	}{
   380  		{mkPod("foo", ""), false},
   381  		{mkPod("rejected", ""), false},
   382  		{mkPod("bar", "55"), true},
   383  		{mkPod("baz", "32"), true},
   384  	}
   385  	for _, item := range table {
   386  		obj, exists, _ := s.Get(item.Pod)
   387  		if e, a := item.exists, exists; e != a {
   388  			t.Errorf("%v: expected %v, got %v", item.Pod, e, a)
   389  		}
   390  		if !exists {
   391  			continue
   392  		}
   393  		if e, a := item.Pod.ResourceVersion, obj.(*v1.Pod).ResourceVersion; e != a {
   394  			t.Errorf("%v: expected %v, got %v", item.Pod, e, a)
   395  		}
   396  	}
   397  
   398  	// Validate that setLastSyncResourceVersion was called with the RV from the last event.
   399  	if e, a := "32", g.LastSyncResourceVersion(); e != a {
   400  		t.Errorf("expected %v, got %v", e, a)
   401  	}
   402  }
   403  
   404  func TestReflectorStopWatch(t *testing.T) {
   405  	s := NewStore(MetaNamespaceKeyFunc)
   406  	g := NewReflector(&testLW{}, &v1.Pod{}, s, 0)
   407  	fw := watch.NewFake()
   408  	stopWatch := make(chan struct{})
   409  	close(stopWatch)
   410  	err := handleWatch(time.Now(), fw, s, g.expectedType, g.expectedGVK, g.name, g.typeDescription, g.setLastSyncResourceVersion, g.clock, nevererrc, stopWatch)
   411  	if err != errorStopRequested {
   412  		t.Errorf("expected stop error, got %q", err)
   413  	}
   414  }
   415  
   416  func TestReflectorListAndWatch(t *testing.T) {
   417  	createdFakes := make(chan *watch.FakeWatcher)
   418  
   419  	// The ListFunc says that it's at revision 1. Therefore, we expect our WatchFunc
   420  	// to get called at the beginning of the watch with 1, and again with 3 when we
   421  	// inject an error.
   422  	expectedRVs := []string{"1", "3"}
   423  	lw := &testLW{
   424  		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
   425  			rv := options.ResourceVersion
   426  			fw := watch.NewFake()
   427  			if e, a := expectedRVs[0], rv; e != a {
   428  				t.Errorf("Expected rv %v, but got %v", e, a)
   429  			}
   430  			expectedRVs = expectedRVs[1:]
   431  			// channel is not buffered because the for loop below needs to block. But
   432  			// we don't want to block here, so report the new fake via a go routine.
   433  			go func() { createdFakes <- fw }()
   434  			return fw, nil
   435  		},
   436  		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
   437  			return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "1"}}, nil
   438  		},
   439  	}
   440  	s := NewFIFO(MetaNamespaceKeyFunc)
   441  	r := NewReflector(lw, &v1.Pod{}, s, 0)
   442  	go r.ListAndWatch(wait.NeverStop)
   443  
   444  	ids := []string{"foo", "bar", "baz", "qux", "zoo"}
   445  	var fw *watch.FakeWatcher
   446  	for i, id := range ids {
   447  		if fw == nil {
   448  			fw = <-createdFakes
   449  		}
   450  		sendingRV := strconv.FormatUint(uint64(i+2), 10)
   451  		fw.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: id, ResourceVersion: sendingRV}})
   452  		if sendingRV == "3" {
   453  			// Inject a failure.
   454  			fw.Stop()
   455  			fw = nil
   456  		}
   457  	}
   458  
   459  	// Verify we received the right ids with the right resource versions.
   460  	for i, id := range ids {
   461  		pod := Pop(s).(*v1.Pod)
   462  		if e, a := id, pod.Name; e != a {
   463  			t.Errorf("%v: Expected %v, got %v", i, e, a)
   464  		}
   465  		if e, a := strconv.FormatUint(uint64(i+2), 10), pod.ResourceVersion; e != a {
   466  			t.Errorf("%v: Expected %v, got %v", i, e, a)
   467  		}
   468  	}
   469  
   470  	if len(expectedRVs) != 0 {
   471  		t.Error("called watchStarter an unexpected number of times")
   472  	}
   473  }
   474  
   475  func TestReflectorListAndWatchWithErrors(t *testing.T) {
   476  	mkPod := func(id string, rv string) *v1.Pod {
   477  		return &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: id, ResourceVersion: rv}}
   478  	}
   479  	mkList := func(rv string, pods ...*v1.Pod) *v1.PodList {
   480  		list := &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: rv}}
   481  		for _, pod := range pods {
   482  			list.Items = append(list.Items, *pod)
   483  		}
   484  		return list
   485  	}
   486  	table := []struct {
   487  		list     *v1.PodList
   488  		listErr  error
   489  		events   []watch.Event
   490  		watchErr error
   491  	}{
   492  		{
   493  			list: mkList("1"),
   494  			events: []watch.Event{
   495  				{Type: watch.Added, Object: mkPod("foo", "2")},
   496  				{Type: watch.Added, Object: mkPod("bar", "3")},
   497  			},
   498  		}, {
   499  			list: mkList("3", mkPod("foo", "2"), mkPod("bar", "3")),
   500  			events: []watch.Event{
   501  				{Type: watch.Deleted, Object: mkPod("foo", "4")},
   502  				{Type: watch.Added, Object: mkPod("qux", "5")},
   503  			},
   504  		}, {
   505  			listErr: fmt.Errorf("a list error"),
   506  		}, {
   507  			list:     mkList("5", mkPod("bar", "3"), mkPod("qux", "5")),
   508  			watchErr: fmt.Errorf("a watch error"),
   509  		}, {
   510  			list: mkList("5", mkPod("bar", "3"), mkPod("qux", "5")),
   511  			events: []watch.Event{
   512  				{Type: watch.Added, Object: mkPod("baz", "6")},
   513  			},
   514  		}, {
   515  			list: mkList("6", mkPod("bar", "3"), mkPod("qux", "5"), mkPod("baz", "6")),
   516  		},
   517  	}
   518  
   519  	s := NewFIFO(MetaNamespaceKeyFunc)
   520  	for line, item := range table {
   521  		if item.list != nil {
   522  			// Test that the list is what currently exists in the store.
   523  			current := s.List()
   524  			checkMap := map[string]string{}
   525  			for _, item := range current {
   526  				pod := item.(*v1.Pod)
   527  				checkMap[pod.Name] = pod.ResourceVersion
   528  			}
   529  			for _, pod := range item.list.Items {
   530  				if e, a := pod.ResourceVersion, checkMap[pod.Name]; e != a {
   531  					t.Errorf("%v: expected %v, got %v for pod %v", line, e, a, pod.Name)
   532  				}
   533  			}
   534  			if e, a := len(item.list.Items), len(checkMap); e != a {
   535  				t.Errorf("%v: expected %v, got %v", line, e, a)
   536  			}
   537  		}
   538  		watchRet, watchErr := item.events, item.watchErr
   539  		stopCh := make(chan struct{})
   540  		lw := &testLW{
   541  			WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
   542  				if watchErr != nil {
   543  					return nil, watchErr
   544  				}
   545  				watchErr = fmt.Errorf("second watch")
   546  				fw := watch.NewFake()
   547  				go func() {
   548  					for _, e := range watchRet {
   549  						fw.Action(e.Type, e.Object)
   550  					}
   551  					// Because FakeWatcher doesn't buffer events, it's safe to
   552  					// close the stop channel immediately without missing events.
   553  					// But usually, the event producer would instead close the
   554  					// result channel, and wait for the consumer to stop the
   555  					// watcher, to avoid race conditions.
   556  					// TODO: Fix the FakeWatcher to separate watcher.Stop from close(resultCh)
   557  					close(stopCh)
   558  				}()
   559  				return fw, nil
   560  			},
   561  			ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
   562  				return item.list, item.listErr
   563  			},
   564  		}
   565  		r := NewReflector(lw, &v1.Pod{}, s, 0)
   566  		err := r.ListAndWatch(stopCh)
   567  		if item.listErr != nil && !errors.Is(err, item.listErr) {
   568  			t.Errorf("unexpected ListAndWatch error: %v", err)
   569  		}
   570  		if item.watchErr != nil && !errors.Is(err, item.watchErr) {
   571  			t.Errorf("unexpected ListAndWatch error: %v", err)
   572  		}
   573  		if item.listErr == nil && item.watchErr == nil {
   574  			assert.NoError(t, err)
   575  		}
   576  	}
   577  }
   578  
   579  func TestReflectorListAndWatchInitConnBackoff(t *testing.T) {
   580  	maxBackoff := 50 * time.Millisecond
   581  	table := []struct {
   582  		numConnFails  int
   583  		expLowerBound time.Duration
   584  		expUpperBound time.Duration
   585  	}{
   586  		{5, 32 * time.Millisecond, 64 * time.Millisecond}, // case where maxBackoff is not hit, time should grow exponentially
   587  		{40, 35 * 2 * maxBackoff, 40 * 2 * maxBackoff},    // case where maxBoff is hit, backoff time should flatten
   588  
   589  	}
   590  	for _, test := range table {
   591  		t.Run(fmt.Sprintf("%d connection failures takes at least %d ms", test.numConnFails, 1<<test.numConnFails),
   592  			func(t *testing.T) {
   593  				stopCh := make(chan struct{})
   594  				connFails := test.numConnFails
   595  				fakeClock := testingclock.NewFakeClock(time.Unix(0, 0))
   596  				bm := wait.NewExponentialBackoffManager(time.Millisecond, maxBackoff, 100*time.Millisecond, 2.0, 1.0, fakeClock)
   597  				done := make(chan struct{})
   598  				defer close(done)
   599  				go func() {
   600  					i := 0
   601  					for {
   602  						select {
   603  						case <-done:
   604  							return
   605  						default:
   606  						}
   607  						if fakeClock.HasWaiters() {
   608  							step := (1 << (i + 1)) * time.Millisecond
   609  							if step > maxBackoff*2 {
   610  								step = maxBackoff * 2
   611  							}
   612  							fakeClock.Step(step)
   613  							i++
   614  						}
   615  						time.Sleep(100 * time.Microsecond)
   616  					}
   617  				}()
   618  				lw := &testLW{
   619  					WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
   620  						if connFails > 0 {
   621  							connFails--
   622  							return nil, syscall.ECONNREFUSED
   623  						}
   624  						close(stopCh)
   625  						return watch.NewFake(), nil
   626  					},
   627  					ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
   628  						return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "1"}}, nil
   629  					},
   630  				}
   631  				r := &Reflector{
   632  					name:              "test-reflector",
   633  					listerWatcher:     lw,
   634  					store:             NewFIFO(MetaNamespaceKeyFunc),
   635  					backoffManager:    bm,
   636  					clock:             fakeClock,
   637  					watchErrorHandler: WatchErrorHandler(DefaultWatchErrorHandler),
   638  				}
   639  				start := fakeClock.Now()
   640  				err := r.ListAndWatch(stopCh)
   641  				elapsed := fakeClock.Since(start)
   642  				if err != nil {
   643  					t.Errorf("unexpected error %v", err)
   644  				}
   645  				if elapsed < (test.expLowerBound) {
   646  					t.Errorf("expected lower bound of ListAndWatch: %v, got %v", test.expLowerBound, elapsed)
   647  				}
   648  				if elapsed > (test.expUpperBound) {
   649  					t.Errorf("expected upper bound of ListAndWatch: %v, got %v", test.expUpperBound, elapsed)
   650  				}
   651  			})
   652  	}
   653  }
   654  
   655  type fakeBackoff struct {
   656  	clock clock.Clock
   657  	calls int
   658  }
   659  
   660  func (f *fakeBackoff) Backoff() clock.Timer {
   661  	f.calls++
   662  	return f.clock.NewTimer(time.Duration(0))
   663  }
   664  
   665  func TestBackoffOnTooManyRequests(t *testing.T) {
   666  	err := apierrors.NewTooManyRequests("too many requests", 1)
   667  	clock := &clock.RealClock{}
   668  	bm := &fakeBackoff{clock: clock}
   669  
   670  	lw := &testLW{
   671  		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
   672  			return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "1"}}, nil
   673  		},
   674  		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
   675  			switch bm.calls {
   676  			case 0:
   677  				return nil, err
   678  			case 1:
   679  				w := watch.NewFakeWithChanSize(1, false)
   680  				status := err.Status()
   681  				w.Error(&status)
   682  				return w, nil
   683  			default:
   684  				w := watch.NewFake()
   685  				w.Stop()
   686  				return w, nil
   687  			}
   688  		},
   689  	}
   690  
   691  	r := &Reflector{
   692  		name:              "test-reflector",
   693  		listerWatcher:     lw,
   694  		store:             NewFIFO(MetaNamespaceKeyFunc),
   695  		backoffManager:    bm,
   696  		clock:             clock,
   697  		watchErrorHandler: WatchErrorHandler(DefaultWatchErrorHandler),
   698  	}
   699  
   700  	stopCh := make(chan struct{})
   701  	if err := r.ListAndWatch(stopCh); err != nil {
   702  		t.Fatal(err)
   703  	}
   704  	close(stopCh)
   705  	if bm.calls != 2 {
   706  		t.Errorf("unexpected watch backoff calls: %d", bm.calls)
   707  	}
   708  }
   709  
   710  func TestNoRelistOnTooManyRequests(t *testing.T) {
   711  	err := apierrors.NewTooManyRequests("too many requests", 1)
   712  	clock := &clock.RealClock{}
   713  	bm := &fakeBackoff{clock: clock}
   714  	listCalls, watchCalls := 0, 0
   715  
   716  	lw := &testLW{
   717  		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
   718  			listCalls++
   719  			return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "1"}}, nil
   720  		},
   721  		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
   722  			watchCalls++
   723  			if watchCalls < 5 {
   724  				return nil, err
   725  			}
   726  			w := watch.NewFake()
   727  			w.Stop()
   728  			return w, nil
   729  		},
   730  	}
   731  
   732  	r := &Reflector{
   733  		name:              "test-reflector",
   734  		listerWatcher:     lw,
   735  		store:             NewFIFO(MetaNamespaceKeyFunc),
   736  		backoffManager:    bm,
   737  		clock:             clock,
   738  		watchErrorHandler: WatchErrorHandler(DefaultWatchErrorHandler),
   739  	}
   740  
   741  	stopCh := make(chan struct{})
   742  	if err := r.ListAndWatch(stopCh); err != nil {
   743  		t.Fatal(err)
   744  	}
   745  	close(stopCh)
   746  	if listCalls != 1 {
   747  		t.Errorf("unexpected list calls: %d", listCalls)
   748  	}
   749  	if watchCalls != 5 {
   750  		t.Errorf("unexpected watch calls: %d", watchCalls)
   751  	}
   752  }
   753  
   754  func TestRetryInternalError(t *testing.T) {
   755  	testCases := []struct {
   756  		name                string
   757  		maxInternalDuration time.Duration
   758  		rewindTime          int
   759  		wantRetries         int
   760  	}{
   761  		{
   762  			name:                "retries off",
   763  			maxInternalDuration: time.Duration(0),
   764  			wantRetries:         0,
   765  		},
   766  		{
   767  			name:                "retries on, all calls fail",
   768  			maxInternalDuration: time.Second * 30,
   769  			wantRetries:         31,
   770  		},
   771  		{
   772  			name:                "retries on, one call successful",
   773  			maxInternalDuration: time.Second * 30,
   774  			rewindTime:          10,
   775  			wantRetries:         40,
   776  		},
   777  	}
   778  
   779  	for _, tc := range testCases {
   780  		err := apierrors.NewInternalError(fmt.Errorf("etcdserver: no leader"))
   781  		fakeClock := testingclock.NewFakeClock(time.Now())
   782  		bm := &fakeBackoff{clock: fakeClock}
   783  
   784  		counter := 0
   785  
   786  		lw := &testLW{
   787  			ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
   788  				return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "1"}}, nil
   789  			},
   790  			WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
   791  				counter = counter + 1
   792  				t.Logf("Counter: %v", counter)
   793  				if counter == tc.rewindTime {
   794  					t.Logf("Rewinding")
   795  					fakeClock.Step(time.Minute)
   796  				}
   797  
   798  				fakeClock.Step(time.Second)
   799  				w := watch.NewFakeWithChanSize(1, false)
   800  				status := err.Status()
   801  				w.Error(&status)
   802  				return w, nil
   803  			},
   804  		}
   805  
   806  		r := &Reflector{
   807  			name:              "test-reflector",
   808  			listerWatcher:     lw,
   809  			store:             NewFIFO(MetaNamespaceKeyFunc),
   810  			backoffManager:    bm,
   811  			clock:             fakeClock,
   812  			watchErrorHandler: WatchErrorHandler(DefaultWatchErrorHandler),
   813  		}
   814  
   815  		r.MaxInternalErrorRetryDuration = tc.maxInternalDuration
   816  
   817  		stopCh := make(chan struct{})
   818  		r.ListAndWatch(stopCh)
   819  		close(stopCh)
   820  
   821  		if counter-1 != tc.wantRetries {
   822  			t.Errorf("%v unexpected number of retries: %d", tc, counter-1)
   823  		}
   824  	}
   825  }
   826  
   827  func TestReflectorResync(t *testing.T) {
   828  	iteration := 0
   829  	stopCh := make(chan struct{})
   830  	rerr := errors.New("expected resync reached")
   831  	s := &FakeCustomStore{
   832  		ResyncFunc: func() error {
   833  			iteration++
   834  			if iteration == 2 {
   835  				return rerr
   836  			}
   837  			return nil
   838  		},
   839  	}
   840  
   841  	lw := &testLW{
   842  		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
   843  			fw := watch.NewFake()
   844  			return fw, nil
   845  		},
   846  		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
   847  			return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "0"}}, nil
   848  		},
   849  	}
   850  	resyncPeriod := 1 * time.Millisecond
   851  	r := NewReflector(lw, &v1.Pod{}, s, resyncPeriod)
   852  	if err := r.ListAndWatch(stopCh); err != nil {
   853  		// error from Resync is not propaged up to here.
   854  		t.Errorf("expected error %v", err)
   855  	}
   856  	if iteration != 2 {
   857  		t.Errorf("exactly 2 iterations were expected, got: %v", iteration)
   858  	}
   859  }
   860  
   861  func TestReflectorWatchListPageSize(t *testing.T) {
   862  	stopCh := make(chan struct{})
   863  	s := NewStore(MetaNamespaceKeyFunc)
   864  
   865  	lw := &testLW{
   866  		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
   867  			// Stop once the reflector begins watching since we're only interested in the list.
   868  			close(stopCh)
   869  			fw := watch.NewFake()
   870  			return fw, nil
   871  		},
   872  		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
   873  			if options.Limit != 4 {
   874  				t.Fatalf("Expected list Limit of 4 but got %d", options.Limit)
   875  			}
   876  			pods := make([]v1.Pod, 10)
   877  			for i := 0; i < 10; i++ {
   878  				pods[i] = v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("pod-%d", i), ResourceVersion: fmt.Sprintf("%d", i)}}
   879  			}
   880  			switch options.Continue {
   881  			case "":
   882  				return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "10", Continue: "C1"}, Items: pods[0:4]}, nil
   883  			case "C1":
   884  				return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "10", Continue: "C2"}, Items: pods[4:8]}, nil
   885  			case "C2":
   886  				return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "10"}, Items: pods[8:10]}, nil
   887  			default:
   888  				t.Fatalf("Unrecognized continue: %s", options.Continue)
   889  			}
   890  			return nil, nil
   891  		},
   892  	}
   893  	r := NewReflector(lw, &v1.Pod{}, s, 0)
   894  	// Set resource version to test pagination also for not consistent reads.
   895  	r.setLastSyncResourceVersion("10")
   896  	// Set the reflector to paginate the list request in 4 item chunks.
   897  	r.WatchListPageSize = 4
   898  	r.ListAndWatch(stopCh)
   899  
   900  	results := s.List()
   901  	if len(results) != 10 {
   902  		t.Errorf("Expected 10 results, got %d", len(results))
   903  	}
   904  }
   905  
   906  func TestReflectorNotPaginatingNotConsistentReads(t *testing.T) {
   907  	stopCh := make(chan struct{})
   908  	s := NewStore(MetaNamespaceKeyFunc)
   909  
   910  	lw := &testLW{
   911  		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
   912  			// Stop once the reflector begins watching since we're only interested in the list.
   913  			close(stopCh)
   914  			fw := watch.NewFake()
   915  			return fw, nil
   916  		},
   917  		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
   918  			if options.ResourceVersion != "10" {
   919  				t.Fatalf("Expected ResourceVersion: \"10\", got: %s", options.ResourceVersion)
   920  			}
   921  			if options.Limit != 0 {
   922  				t.Fatalf("Expected list Limit of 0 but got %d", options.Limit)
   923  			}
   924  			pods := make([]v1.Pod, 10)
   925  			for i := 0; i < 10; i++ {
   926  				pods[i] = v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("pod-%d", i), ResourceVersion: fmt.Sprintf("%d", i)}}
   927  			}
   928  			return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "10"}, Items: pods}, nil
   929  		},
   930  	}
   931  	r := NewReflector(lw, &v1.Pod{}, s, 0)
   932  	r.setLastSyncResourceVersion("10")
   933  	r.ListAndWatch(stopCh)
   934  
   935  	results := s.List()
   936  	if len(results) != 10 {
   937  		t.Errorf("Expected 10 results, got %d", len(results))
   938  	}
   939  }
   940  
   941  func TestReflectorPaginatingNonConsistentReadsIfWatchCacheDisabled(t *testing.T) {
   942  	var stopCh chan struct{}
   943  	s := NewStore(MetaNamespaceKeyFunc)
   944  
   945  	lw := &testLW{
   946  		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
   947  			// Stop once the reflector begins watching since we're only interested in the list.
   948  			close(stopCh)
   949  			fw := watch.NewFake()
   950  			return fw, nil
   951  		},
   952  		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
   953  			// Check that default pager limit is set.
   954  			if options.Limit != 500 {
   955  				t.Fatalf("Expected list Limit of 500 but got %d", options.Limit)
   956  			}
   957  			pods := make([]v1.Pod, 10)
   958  			for i := 0; i < 10; i++ {
   959  				pods[i] = v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("pod-%d", i), ResourceVersion: fmt.Sprintf("%d", i)}}
   960  			}
   961  			switch options.Continue {
   962  			case "":
   963  				return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "10", Continue: "C1"}, Items: pods[0:4]}, nil
   964  			case "C1":
   965  				return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "10", Continue: "C2"}, Items: pods[4:8]}, nil
   966  			case "C2":
   967  				return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "10"}, Items: pods[8:10]}, nil
   968  			default:
   969  				t.Fatalf("Unrecognized continue: %s", options.Continue)
   970  			}
   971  			return nil, nil
   972  		},
   973  	}
   974  	r := NewReflector(lw, &v1.Pod{}, s, 0)
   975  
   976  	// Initial list should initialize paginatedResult in the reflector.
   977  	stopCh = make(chan struct{})
   978  	r.ListAndWatch(stopCh)
   979  	if results := s.List(); len(results) != 10 {
   980  		t.Errorf("Expected 10 results, got %d", len(results))
   981  	}
   982  
   983  	// Since initial list for ResourceVersion="0" was paginated, the subsequent
   984  	// ones should also be paginated.
   985  	stopCh = make(chan struct{})
   986  	r.ListAndWatch(stopCh)
   987  	if results := s.List(); len(results) != 10 {
   988  		t.Errorf("Expected 10 results, got %d", len(results))
   989  	}
   990  }
   991  
   992  // TestReflectorResyncWithResourceVersion ensures that a reflector keeps track of the ResourceVersion and sends
   993  // it in relist requests to prevent the reflector from traveling back in time if the relist is to a api-server or
   994  // etcd that is partitioned and serving older data than the reflector has already processed.
   995  func TestReflectorResyncWithResourceVersion(t *testing.T) {
   996  	stopCh := make(chan struct{})
   997  	s := NewStore(MetaNamespaceKeyFunc)
   998  	listCallRVs := []string{}
   999  
  1000  	lw := &testLW{
  1001  		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
  1002  			// Stop once the reflector begins watching since we're only interested in the list.
  1003  			close(stopCh)
  1004  			fw := watch.NewFake()
  1005  			return fw, nil
  1006  		},
  1007  		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
  1008  			listCallRVs = append(listCallRVs, options.ResourceVersion)
  1009  			pods := make([]v1.Pod, 8)
  1010  			for i := 0; i < 8; i++ {
  1011  				pods[i] = v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("pod-%d", i), ResourceVersion: fmt.Sprintf("%d", i)}}
  1012  			}
  1013  			switch options.ResourceVersion {
  1014  			case "0":
  1015  				return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "10"}, Items: pods[0:4]}, nil
  1016  			case "10":
  1017  				return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "11"}, Items: pods[0:8]}, nil
  1018  			default:
  1019  				t.Fatalf("Unrecognized ResourceVersion: %s", options.ResourceVersion)
  1020  			}
  1021  			return nil, nil
  1022  		},
  1023  	}
  1024  	r := NewReflector(lw, &v1.Pod{}, s, 0)
  1025  
  1026  	// Initial list should use RV=0
  1027  	r.ListAndWatch(stopCh)
  1028  
  1029  	results := s.List()
  1030  	if len(results) != 4 {
  1031  		t.Errorf("Expected 4 results, got %d", len(results))
  1032  	}
  1033  
  1034  	// relist should use lastSyncResourceVersions (RV=10)
  1035  	stopCh = make(chan struct{})
  1036  	r.ListAndWatch(stopCh)
  1037  
  1038  	results = s.List()
  1039  	if len(results) != 8 {
  1040  		t.Errorf("Expected 8 results, got %d", len(results))
  1041  	}
  1042  
  1043  	expectedRVs := []string{"0", "10"}
  1044  	if !reflect.DeepEqual(listCallRVs, expectedRVs) {
  1045  		t.Errorf("Expected series of list calls with resource versiosn of %v but got: %v", expectedRVs, listCallRVs)
  1046  	}
  1047  }
  1048  
  1049  // TestReflectorExpiredExactResourceVersion tests that a reflector handles the behavior of kubernetes 1.16 an earlier
  1050  // where if the exact ResourceVersion requested is not available for a List request for a non-zero ResourceVersion,
  1051  // an "Expired" error is returned if the ResourceVersion has expired (etcd has compacted it).
  1052  // (In kubernetes 1.17, or when the watch cache is enabled, the List will instead return the list that is no older than
  1053  // the requested ResourceVersion).
  1054  func TestReflectorExpiredExactResourceVersion(t *testing.T) {
  1055  	stopCh := make(chan struct{})
  1056  	s := NewStore(MetaNamespaceKeyFunc)
  1057  	listCallRVs := []string{}
  1058  
  1059  	lw := &testLW{
  1060  		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
  1061  			// Stop once the reflector begins watching since we're only interested in the list.
  1062  			close(stopCh)
  1063  			fw := watch.NewFake()
  1064  			return fw, nil
  1065  		},
  1066  		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
  1067  			listCallRVs = append(listCallRVs, options.ResourceVersion)
  1068  			pods := make([]v1.Pod, 8)
  1069  			for i := 0; i < 8; i++ {
  1070  				pods[i] = v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("pod-%d", i), ResourceVersion: fmt.Sprintf("%d", i)}}
  1071  			}
  1072  			switch options.ResourceVersion {
  1073  			case "0":
  1074  				return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "10"}, Items: pods[0:4]}, nil
  1075  			case "10":
  1076  				// When watch cache is disabled, if the exact ResourceVersion requested is not available, a "Expired" error is returned.
  1077  				return nil, apierrors.NewResourceExpired("The resourceVersion for the provided watch is too old.")
  1078  			case "":
  1079  				return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "11"}, Items: pods[0:8]}, nil
  1080  			default:
  1081  				t.Fatalf("Unrecognized ResourceVersion: %s", options.ResourceVersion)
  1082  			}
  1083  			return nil, nil
  1084  		},
  1085  	}
  1086  	r := NewReflector(lw, &v1.Pod{}, s, 0)
  1087  
  1088  	// Initial list should use RV=0
  1089  	r.ListAndWatch(stopCh)
  1090  
  1091  	results := s.List()
  1092  	if len(results) != 4 {
  1093  		t.Errorf("Expected 4 results, got %d", len(results))
  1094  	}
  1095  
  1096  	// relist should use lastSyncResourceVersions (RV=10) and since RV=10 is expired, it should retry with RV="".
  1097  	stopCh = make(chan struct{})
  1098  	r.ListAndWatch(stopCh)
  1099  
  1100  	results = s.List()
  1101  	if len(results) != 8 {
  1102  		t.Errorf("Expected 8 results, got %d", len(results))
  1103  	}
  1104  
  1105  	expectedRVs := []string{"0", "10", ""}
  1106  	if !reflect.DeepEqual(listCallRVs, expectedRVs) {
  1107  		t.Errorf("Expected series of list calls with resource versiosn of %v but got: %v", expectedRVs, listCallRVs)
  1108  	}
  1109  }
  1110  
  1111  func TestReflectorFullListIfExpired(t *testing.T) {
  1112  	stopCh := make(chan struct{})
  1113  	s := NewStore(MetaNamespaceKeyFunc)
  1114  	listCallRVs := []string{}
  1115  
  1116  	lw := &testLW{
  1117  		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
  1118  			// Stop once the reflector begins watching since we're only interested in the list.
  1119  			close(stopCh)
  1120  			fw := watch.NewFake()
  1121  			return fw, nil
  1122  		},
  1123  		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
  1124  			listCallRVs = append(listCallRVs, options.ResourceVersion)
  1125  			pods := make([]v1.Pod, 8)
  1126  			for i := 0; i < 8; i++ {
  1127  				pods[i] = v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("pod-%d", i), ResourceVersion: fmt.Sprintf("%d", i)}}
  1128  			}
  1129  			rvContinueLimit := func(rv, c string, l int64) metav1.ListOptions {
  1130  				return metav1.ListOptions{ResourceVersion: rv, Continue: c, Limit: l}
  1131  			}
  1132  			switch rvContinueLimit(options.ResourceVersion, options.Continue, options.Limit) {
  1133  			// initial limited list
  1134  			case rvContinueLimit("0", "", 4):
  1135  				return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "10"}, Items: pods[0:4]}, nil
  1136  			// first page of the rv=10 list
  1137  			case rvContinueLimit("10", "", 4):
  1138  				return &v1.PodList{ListMeta: metav1.ListMeta{Continue: "C1", ResourceVersion: "11"}, Items: pods[0:4]}, nil
  1139  			// second page of the above list
  1140  			case rvContinueLimit("", "C1", 4):
  1141  				return nil, apierrors.NewResourceExpired("The resourceVersion for the provided watch is too old.")
  1142  			// rv=10 unlimited list
  1143  			case rvContinueLimit("10", "", 0):
  1144  				return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "11"}, Items: pods[0:8]}, nil
  1145  			default:
  1146  				err := fmt.Errorf("unexpected list options: %#v", options)
  1147  				t.Error(err)
  1148  				return nil, err
  1149  			}
  1150  		},
  1151  	}
  1152  	r := NewReflector(lw, &v1.Pod{}, s, 0)
  1153  	r.WatchListPageSize = 4
  1154  
  1155  	// Initial list should use RV=0
  1156  	if err := r.ListAndWatch(stopCh); err != nil {
  1157  		t.Fatal(err)
  1158  	}
  1159  
  1160  	results := s.List()
  1161  	if len(results) != 4 {
  1162  		t.Errorf("Expected 4 results, got %d", len(results))
  1163  	}
  1164  
  1165  	// relist should use lastSyncResourceVersions (RV=10) and since second page of that expired, it should full list with RV=10
  1166  	stopCh = make(chan struct{})
  1167  	if err := r.ListAndWatch(stopCh); err != nil {
  1168  		t.Fatal(err)
  1169  	}
  1170  
  1171  	results = s.List()
  1172  	if len(results) != 8 {
  1173  		t.Errorf("Expected 8 results, got %d", len(results))
  1174  	}
  1175  
  1176  	expectedRVs := []string{"0", "10", "", "10"}
  1177  	if !reflect.DeepEqual(listCallRVs, expectedRVs) {
  1178  		t.Errorf("Expected series of list calls with resource versiosn of %#v but got: %#v", expectedRVs, listCallRVs)
  1179  	}
  1180  }
  1181  
  1182  func TestReflectorFullListIfTooLarge(t *testing.T) {
  1183  	stopCh := make(chan struct{})
  1184  	s := NewStore(MetaNamespaceKeyFunc)
  1185  	listCallRVs := []string{}
  1186  	version := 30
  1187  
  1188  	lw := &testLW{
  1189  		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
  1190  			// Stop once the reflector begins watching since we're only interested in the list.
  1191  			close(stopCh)
  1192  			fw := watch.NewFake()
  1193  			return fw, nil
  1194  		},
  1195  		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
  1196  			listCallRVs = append(listCallRVs, options.ResourceVersion)
  1197  			resourceVersion := strconv.Itoa(version)
  1198  
  1199  			switch options.ResourceVersion {
  1200  			// initial list
  1201  			case "0":
  1202  				return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "20"}}, nil
  1203  			// relist after the initial list
  1204  			case "20":
  1205  				err := apierrors.NewTimeoutError("too large resource version", 1)
  1206  				err.ErrStatus.Details.Causes = []metav1.StatusCause{{Type: metav1.CauseTypeResourceVersionTooLarge}}
  1207  				return nil, err
  1208  			// relist after the initial list (covers the error format used in api server 1.17.0-1.18.5)
  1209  			case "30":
  1210  				err := apierrors.NewTimeoutError("too large resource version", 1)
  1211  				err.ErrStatus.Details.Causes = []metav1.StatusCause{{Message: "Too large resource version"}}
  1212  				return nil, err
  1213  			// relist after the initial list (covers the error format used in api server before 1.17.0)
  1214  			case "40":
  1215  				err := apierrors.NewTimeoutError("Too large resource version", 1)
  1216  				return nil, err
  1217  			// relist from etcd after "too large" error
  1218  			case "":
  1219  				version += 10
  1220  				return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: resourceVersion}}, nil
  1221  			default:
  1222  				return nil, fmt.Errorf("unexpected List call: %s", options.ResourceVersion)
  1223  			}
  1224  		},
  1225  	}
  1226  	r := NewReflector(lw, &v1.Pod{}, s, 0)
  1227  
  1228  	// Initial list should use RV=0
  1229  	if err := r.ListAndWatch(stopCh); err != nil {
  1230  		t.Fatal(err)
  1231  	}
  1232  
  1233  	// Relist from the future version.
  1234  	// This may happen, as watchcache is initialized from "current global etcd resource version"
  1235  	// when kube-apiserver is starting and if no objects are changing after that each kube-apiserver
  1236  	// may be synced to a different version and they will never converge.
  1237  	// TODO: We should use etcd progress-notify feature to avoid this behavior but until this is
  1238  	// done we simply try to relist from now to avoid continuous errors on relists.
  1239  	for i := 1; i <= 3; i++ {
  1240  		// relist twice to cover the two variants of TooLargeResourceVersion api errors
  1241  		stopCh = make(chan struct{})
  1242  		if err := r.ListAndWatch(stopCh); err != nil {
  1243  			t.Fatal(err)
  1244  		}
  1245  	}
  1246  
  1247  	expectedRVs := []string{"0", "20", "", "30", "", "40", ""}
  1248  	if !reflect.DeepEqual(listCallRVs, expectedRVs) {
  1249  		t.Errorf("Expected series of list calls with resource version of %#v but got: %#v", expectedRVs, listCallRVs)
  1250  	}
  1251  }
  1252  
  1253  func TestGetTypeDescriptionFromObject(t *testing.T) {
  1254  	obj := &unstructured.Unstructured{}
  1255  	gvk := schema.GroupVersionKind{
  1256  		Group:   "mygroup",
  1257  		Version: "v1",
  1258  		Kind:    "MyKind",
  1259  	}
  1260  	obj.SetGroupVersionKind(gvk)
  1261  
  1262  	testCases := map[string]struct {
  1263  		inputType               interface{}
  1264  		expectedTypeDescription string
  1265  	}{
  1266  		"Nil type": {
  1267  			expectedTypeDescription: defaultExpectedTypeName,
  1268  		},
  1269  		"Normal type": {
  1270  			inputType:               &v1.Pod{},
  1271  			expectedTypeDescription: "*v1.Pod",
  1272  		},
  1273  		"Unstructured type without GVK": {
  1274  			inputType:               &unstructured.Unstructured{},
  1275  			expectedTypeDescription: "*unstructured.Unstructured",
  1276  		},
  1277  		"Unstructured type with GVK": {
  1278  			inputType:               obj,
  1279  			expectedTypeDescription: gvk.String(),
  1280  		},
  1281  	}
  1282  	for testName, tc := range testCases {
  1283  		t.Run(testName, func(t *testing.T) {
  1284  			typeDescription := getTypeDescriptionFromObject(tc.inputType)
  1285  			if tc.expectedTypeDescription != typeDescription {
  1286  				t.Fatalf("Expected typeDescription %v, got %v", tc.expectedTypeDescription, typeDescription)
  1287  			}
  1288  		})
  1289  	}
  1290  }
  1291  
  1292  func TestGetExpectedGVKFromObject(t *testing.T) {
  1293  	obj := &unstructured.Unstructured{}
  1294  	gvk := schema.GroupVersionKind{
  1295  		Group:   "mygroup",
  1296  		Version: "v1",
  1297  		Kind:    "MyKind",
  1298  	}
  1299  	obj.SetGroupVersionKind(gvk)
  1300  
  1301  	testCases := map[string]struct {
  1302  		inputType   interface{}
  1303  		expectedGVK *schema.GroupVersionKind
  1304  	}{
  1305  		"Nil type": {},
  1306  		"Some non Unstructured type": {
  1307  			inputType: &v1.Pod{},
  1308  		},
  1309  		"Unstructured type without GVK": {
  1310  			inputType: &unstructured.Unstructured{},
  1311  		},
  1312  		"Unstructured type with GVK": {
  1313  			inputType:   obj,
  1314  			expectedGVK: &gvk,
  1315  		},
  1316  	}
  1317  	for testName, tc := range testCases {
  1318  		t.Run(testName, func(t *testing.T) {
  1319  			expectedGVK := getExpectedGVKFromObject(tc.inputType)
  1320  			gvkNotEqual := (tc.expectedGVK == nil) != (expectedGVK == nil)
  1321  			if tc.expectedGVK != nil && expectedGVK != nil {
  1322  				gvkNotEqual = *tc.expectedGVK != *expectedGVK
  1323  			}
  1324  			if gvkNotEqual {
  1325  				t.Fatalf("Expected expectedGVK %v, got %v", tc.expectedGVK, expectedGVK)
  1326  			}
  1327  		})
  1328  	}
  1329  }
  1330  
  1331  func TestWatchTimeout(t *testing.T) {
  1332  
  1333  	testCases := []struct {
  1334  		name                      string
  1335  		minWatchTimeout           time.Duration
  1336  		expectedMinTimeoutSeconds int64
  1337  	}{
  1338  		{
  1339  			name:                      "no timeout",
  1340  			expectedMinTimeoutSeconds: 5 * 60,
  1341  		},
  1342  		{
  1343  			name:                      "small timeout not honored",
  1344  			minWatchTimeout:           time.Second,
  1345  			expectedMinTimeoutSeconds: 5 * 60,
  1346  		},
  1347  		{
  1348  			name:                      "30m timeout",
  1349  			minWatchTimeout:           30 * time.Minute,
  1350  			expectedMinTimeoutSeconds: 30 * 60,
  1351  		},
  1352  	}
  1353  
  1354  	for _, tc := range testCases {
  1355  		t.Run(tc.name, func(t *testing.T) {
  1356  			stopCh := make(chan struct{})
  1357  			s := NewStore(MetaNamespaceKeyFunc)
  1358  			var gotTimeoutSeconds int64
  1359  
  1360  			lw := &testLW{
  1361  				ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
  1362  					return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "10"}}, nil
  1363  				},
  1364  				WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
  1365  					if options.TimeoutSeconds != nil {
  1366  						gotTimeoutSeconds = *options.TimeoutSeconds
  1367  					}
  1368  
  1369  					// Stop once the reflector begins watching since we're only interested in the list.
  1370  					close(stopCh)
  1371  					return watch.NewFake(), nil
  1372  				},
  1373  			}
  1374  
  1375  			opts := ReflectorOptions{
  1376  				MinWatchTimeout: tc.minWatchTimeout,
  1377  			}
  1378  			r := NewReflectorWithOptions(lw, &v1.Pod{}, s, opts)
  1379  			if err := r.ListAndWatch(stopCh); err != nil {
  1380  				t.Fatal(err)
  1381  			}
  1382  
  1383  			minExpected := tc.expectedMinTimeoutSeconds
  1384  			maxExpected := 2 * tc.expectedMinTimeoutSeconds
  1385  			if gotTimeoutSeconds < minExpected || gotTimeoutSeconds > maxExpected {
  1386  				t.Errorf("unexpected TimeoutSecond, got %v, expected in [%v, %v]", gotTimeoutSeconds, minExpected, maxExpected)
  1387  			}
  1388  		})
  1389  	}
  1390  }
  1391  
  1392  type storeWithRV struct {
  1393  	Store
  1394  
  1395  	// resourceVersions tracks values passed by UpdateResourceVersion
  1396  	resourceVersions []string
  1397  }
  1398  
  1399  func (s *storeWithRV) UpdateResourceVersion(resourceVersion string) {
  1400  	s.resourceVersions = append(s.resourceVersions, resourceVersion)
  1401  }
  1402  
  1403  func newStoreWithRV() *storeWithRV {
  1404  	return &storeWithRV{
  1405  		Store: NewStore(MetaNamespaceKeyFunc),
  1406  	}
  1407  }
  1408  
  1409  func TestReflectorResourceVersionUpdate(t *testing.T) {
  1410  	s := newStoreWithRV()
  1411  
  1412  	stopCh := make(chan struct{})
  1413  	fw := watch.NewFake()
  1414  
  1415  	lw := &testLW{
  1416  		WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) {
  1417  			return fw, nil
  1418  		},
  1419  		ListFunc: func(options metav1.ListOptions) (runtime.Object, error) {
  1420  			return &v1.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "10"}}, nil
  1421  		},
  1422  	}
  1423  	r := NewReflector(lw, &v1.Pod{}, s, 0)
  1424  
  1425  	makePod := func(rv string) *v1.Pod {
  1426  		return &v1.Pod{ObjectMeta: metav1.ObjectMeta{ResourceVersion: rv}}
  1427  	}
  1428  
  1429  	go func() {
  1430  		fw.Action(watch.Added, makePod("10"))
  1431  		fw.Action(watch.Modified, makePod("20"))
  1432  		fw.Action(watch.Bookmark, makePod("30"))
  1433  		fw.Action(watch.Deleted, makePod("40"))
  1434  		close(stopCh)
  1435  	}()
  1436  
  1437  	// Initial list should use RV=0
  1438  	if err := r.ListAndWatch(stopCh); err != nil {
  1439  		t.Fatal(err)
  1440  	}
  1441  
  1442  	expectedRVs := []string{"10", "20", "30", "40"}
  1443  	if !reflect.DeepEqual(s.resourceVersions, expectedRVs) {
  1444  		t.Errorf("Expected series of resource version updates of %#v but got: %#v", expectedRVs, s.resourceVersions)
  1445  	}
  1446  }
  1447  
  1448  const (
  1449  	fakeItemsNum      = 100
  1450  	exemptObjectIndex = fakeItemsNum / 4
  1451  	pageNum           = 3
  1452  )
  1453  
  1454  func getPodListItems(start int, numItems int) (string, string, *v1.PodList) {
  1455  	out := &v1.PodList{
  1456  		Items: make([]v1.Pod, numItems),
  1457  	}
  1458  
  1459  	for i := 0; i < numItems; i++ {
  1460  
  1461  		out.Items[i] = v1.Pod{
  1462  			TypeMeta: metav1.TypeMeta{
  1463  				APIVersion: "v1",
  1464  				Kind:       "Pod",
  1465  			},
  1466  			ObjectMeta: metav1.ObjectMeta{
  1467  				Name:      fmt.Sprintf("pod-%d", i+start),
  1468  				Namespace: "default",
  1469  				Labels: map[string]string{
  1470  					"label-key-1": "label-value-1",
  1471  				},
  1472  				Annotations: map[string]string{
  1473  					"annotations-key-1": "annotations-value-1",
  1474  				},
  1475  			},
  1476  			Spec: v1.PodSpec{
  1477  				Overhead: v1.ResourceList{
  1478  					v1.ResourceCPU:    resource.MustParse("3"),
  1479  					v1.ResourceMemory: resource.MustParse("8"),
  1480  				},
  1481  				NodeSelector: map[string]string{
  1482  					"foo": "bar",
  1483  					"baz": "quux",
  1484  				},
  1485  				Affinity: &v1.Affinity{
  1486  					NodeAffinity: &v1.NodeAffinity{
  1487  						RequiredDuringSchedulingIgnoredDuringExecution: &v1.NodeSelector{
  1488  							NodeSelectorTerms: []v1.NodeSelectorTerm{
  1489  								{MatchExpressions: []v1.NodeSelectorRequirement{{Key: `foo`}}},
  1490  							},
  1491  						},
  1492  						PreferredDuringSchedulingIgnoredDuringExecution: []v1.PreferredSchedulingTerm{
  1493  							{Preference: v1.NodeSelectorTerm{MatchExpressions: []v1.NodeSelectorRequirement{{Key: `foo`}}}},
  1494  						},
  1495  					},
  1496  				},
  1497  				TopologySpreadConstraints: []v1.TopologySpreadConstraint{
  1498  					{TopologyKey: `foo`},
  1499  				},
  1500  				HostAliases: []v1.HostAlias{
  1501  					{IP: "1.1.1.1"},
  1502  					{IP: "2.2.2.2"},
  1503  				},
  1504  				ImagePullSecrets: []v1.LocalObjectReference{
  1505  					{Name: "secret1"},
  1506  					{Name: "secret2"},
  1507  				},
  1508  				Containers: []v1.Container{
  1509  					{
  1510  						Name:  "foobar",
  1511  						Image: "alpine",
  1512  						Resources: v1.ResourceRequirements{
  1513  							Requests: v1.ResourceList{
  1514  								v1.ResourceName(v1.ResourceCPU):    resource.MustParse("1"),
  1515  								v1.ResourceName(v1.ResourceMemory): resource.MustParse("5"),
  1516  							},
  1517  							Limits: v1.ResourceList{
  1518  								v1.ResourceName(v1.ResourceCPU):    resource.MustParse("2"),
  1519  								v1.ResourceName(v1.ResourceMemory): resource.MustParse("10"),
  1520  							},
  1521  						},
  1522  					},
  1523  					{
  1524  						Name:  "foobar2",
  1525  						Image: "alpine",
  1526  						Resources: v1.ResourceRequirements{
  1527  							Requests: v1.ResourceList{
  1528  								v1.ResourceName(v1.ResourceCPU):    resource.MustParse("4"),
  1529  								v1.ResourceName(v1.ResourceMemory): resource.MustParse("12"),
  1530  							},
  1531  							Limits: v1.ResourceList{
  1532  								v1.ResourceName(v1.ResourceCPU):    resource.MustParse("8"),
  1533  								v1.ResourceName(v1.ResourceMemory): resource.MustParse("24"),
  1534  							},
  1535  						},
  1536  					},
  1537  				},
  1538  				InitContainers: []v1.Container{
  1539  					{
  1540  						Name:  "small-init",
  1541  						Image: "alpine",
  1542  						Resources: v1.ResourceRequirements{
  1543  							Requests: v1.ResourceList{
  1544  								v1.ResourceName(v1.ResourceCPU):    resource.MustParse("1"),
  1545  								v1.ResourceName(v1.ResourceMemory): resource.MustParse("5"),
  1546  							},
  1547  							Limits: v1.ResourceList{
  1548  								v1.ResourceName(v1.ResourceCPU):    resource.MustParse("1"),
  1549  								v1.ResourceName(v1.ResourceMemory): resource.MustParse("5"),
  1550  							},
  1551  						},
  1552  					},
  1553  					{
  1554  						Name:  "big-init",
  1555  						Image: "alpine",
  1556  						Resources: v1.ResourceRequirements{
  1557  							Requests: v1.ResourceList{
  1558  								v1.ResourceName(v1.ResourceCPU):    resource.MustParse("40"),
  1559  								v1.ResourceName(v1.ResourceMemory): resource.MustParse("120"),
  1560  							},
  1561  							Limits: v1.ResourceList{
  1562  								v1.ResourceName(v1.ResourceCPU):    resource.MustParse("80"),
  1563  								v1.ResourceName(v1.ResourceMemory): resource.MustParse("240"),
  1564  							},
  1565  						},
  1566  					},
  1567  				},
  1568  				Hostname: fmt.Sprintf("node-%d", i),
  1569  			},
  1570  			Status: v1.PodStatus{
  1571  				Phase: v1.PodRunning,
  1572  				ContainerStatuses: []v1.ContainerStatus{
  1573  					{
  1574  						ContainerID: "docker://numbers",
  1575  						Image:       "alpine",
  1576  						Name:        "foobar",
  1577  						Ready:       false,
  1578  					},
  1579  					{
  1580  						ContainerID: "docker://numbers",
  1581  						Image:       "alpine",
  1582  						Name:        "foobar2",
  1583  						Ready:       false,
  1584  					},
  1585  				},
  1586  				InitContainerStatuses: []v1.ContainerStatus{
  1587  					{
  1588  						ContainerID: "docker://numbers",
  1589  						Image:       "alpine",
  1590  						Name:        "small-init",
  1591  						Ready:       false,
  1592  					},
  1593  					{
  1594  						ContainerID: "docker://numbers",
  1595  						Image:       "alpine",
  1596  						Name:        "big-init",
  1597  						Ready:       false,
  1598  					},
  1599  				},
  1600  				Conditions: []v1.PodCondition{
  1601  					{
  1602  						Type:               v1.PodScheduled,
  1603  						Status:             v1.ConditionTrue,
  1604  						Reason:             "successfully",
  1605  						Message:            "sync pod successfully",
  1606  						LastProbeTime:      metav1.Now(),
  1607  						LastTransitionTime: metav1.Now(),
  1608  					},
  1609  				},
  1610  			},
  1611  		}
  1612  	}
  1613  
  1614  	return out.Items[0].GetName(), out.Items[exemptObjectIndex].GetName(), out
  1615  }
  1616  
  1617  func getConfigmapListItems(start int, numItems int) (string, string, *v1.ConfigMapList) {
  1618  	out := &v1.ConfigMapList{
  1619  		Items: make([]v1.ConfigMap, numItems),
  1620  	}
  1621  
  1622  	for i := 0; i < numItems; i++ {
  1623  		out.Items[i] = v1.ConfigMap{
  1624  			TypeMeta: metav1.TypeMeta{
  1625  				APIVersion: "v1",
  1626  				Kind:       "ConfigMap",
  1627  			},
  1628  			ObjectMeta: metav1.ObjectMeta{
  1629  				Name:      fmt.Sprintf("cm-%d", i+start),
  1630  				Namespace: "default",
  1631  				Labels: map[string]string{
  1632  					"label-key-1": "label-value-1",
  1633  				},
  1634  				Annotations: map[string]string{
  1635  					"annotations-key-1": "annotations-value-1",
  1636  				},
  1637  			},
  1638  			Data: map[string]string{
  1639  				"data-1": "value-1",
  1640  				"data-2": "value-2",
  1641  			},
  1642  		}
  1643  	}
  1644  
  1645  	return out.Items[0].GetName(), out.Items[exemptObjectIndex].GetName(), out
  1646  }
  1647  
  1648  type TestPagingPodsLW struct {
  1649  	totalPageCount   int
  1650  	fetchedPageCount int
  1651  
  1652  	detectedObjectNameList []string
  1653  	exemptObjectNameList   []string
  1654  }
  1655  
  1656  func newPageTestLW(totalPageNum int) *TestPagingPodsLW {
  1657  	return &TestPagingPodsLW{
  1658  		totalPageCount:   totalPageNum,
  1659  		fetchedPageCount: 0,
  1660  	}
  1661  }
  1662  
  1663  func (t *TestPagingPodsLW) List(options metav1.ListOptions) (runtime.Object, error) {
  1664  	firstPodName, exemptPodName, list := getPodListItems(t.fetchedPageCount*fakeItemsNum, fakeItemsNum)
  1665  	t.detectedObjectNameList = append(t.detectedObjectNameList, firstPodName)
  1666  	t.exemptObjectNameList = append(t.exemptObjectNameList, exemptPodName)
  1667  	t.fetchedPageCount++
  1668  	if t.fetchedPageCount >= t.totalPageCount {
  1669  		return list, nil
  1670  	}
  1671  	list.SetContinue("true")
  1672  	return list, nil
  1673  }
  1674  
  1675  func (t *TestPagingPodsLW) Watch(options metav1.ListOptions) (watch.Interface, error) {
  1676  	return nil, nil
  1677  }
  1678  
  1679  func TestReflectorListExtract(t *testing.T) {
  1680  	store := NewStore(func(obj interface{}) (string, error) {
  1681  		pod, ok := obj.(*v1.Pod)
  1682  		if !ok {
  1683  			return "", fmt.Errorf("expect *v1.Pod, but got %T", obj)
  1684  		}
  1685  		return pod.GetName(), nil
  1686  	})
  1687  
  1688  	lw := newPageTestLW(5)
  1689  	reflector := NewReflector(lw, &v1.Pod{}, store, 0)
  1690  	reflector.WatchListPageSize = fakeItemsNum
  1691  
  1692  	// execute list to fill store
  1693  	stopCh := make(chan struct{})
  1694  	if err := reflector.list(stopCh); err != nil {
  1695  		t.Fatal(err)
  1696  	}
  1697  
  1698  	// We will not delete exemptPod,
  1699  	// in order to see if the existence of this Pod causes other Pods that are not used to be unable to properly clear.
  1700  	for _, podName := range lw.exemptObjectNameList {
  1701  		_, exist, err := store.GetByKey(podName)
  1702  		if err != nil || !exist {
  1703  			t.Fatalf("%s should exist in pod store", podName)
  1704  		}
  1705  	}
  1706  
  1707  	// we will pay attention to whether the memory occupied by the first Pod is released
  1708  	// Golang's can only be SetFinalizer for the first element of the array,
  1709  	// so pod-0 will be the object of our attention
  1710  	detectedPodAlreadyBeCleared := make(chan struct{}, len(lw.detectedObjectNameList))
  1711  
  1712  	for _, firstPodName := range lw.detectedObjectNameList {
  1713  		_, exist, err := store.GetByKey(firstPodName)
  1714  		if err != nil || !exist {
  1715  			t.Fatalf("%s should exist in pod store", firstPodName)
  1716  		}
  1717  		firstPod, exist, err := store.GetByKey(firstPodName)
  1718  		if err != nil || !exist {
  1719  			t.Fatalf("%s should exist in pod store", firstPodName)
  1720  		}
  1721  		goruntime.SetFinalizer(firstPod, func(obj interface{}) {
  1722  			t.Logf("%s already be gc\n", obj.(*v1.Pod).GetName())
  1723  			detectedPodAlreadyBeCleared <- struct{}{}
  1724  		})
  1725  	}
  1726  
  1727  	storedObjectKeys := store.ListKeys()
  1728  	for _, k := range storedObjectKeys {
  1729  		// delete all Pods except the exempted Pods.
  1730  		if sets.NewString(lw.exemptObjectNameList...).Has(k) {
  1731  			continue
  1732  		}
  1733  		obj, exist, err := store.GetByKey(k)
  1734  		if err != nil || !exist {
  1735  			t.Fatalf("%s should exist in pod store", k)
  1736  		}
  1737  
  1738  		if err := store.Delete(obj); err != nil {
  1739  			t.Fatalf("delete object: %v", err)
  1740  		}
  1741  		goruntime.GC()
  1742  	}
  1743  
  1744  	clearedNum := 0
  1745  	for {
  1746  		select {
  1747  		case <-detectedPodAlreadyBeCleared:
  1748  			clearedNum++
  1749  			if clearedNum == len(lw.detectedObjectNameList) {
  1750  				return
  1751  			}
  1752  		}
  1753  	}
  1754  }
  1755  
  1756  func BenchmarkExtractList(b *testing.B) {
  1757  	_, _, podList := getPodListItems(0, fakeItemsNum)
  1758  	_, _, configMapList := getConfigmapListItems(0, fakeItemsNum)
  1759  	tests := []struct {
  1760  		name string
  1761  		list runtime.Object
  1762  	}{
  1763  		{
  1764  			name: "PodList",
  1765  			list: podList,
  1766  		},
  1767  		{
  1768  			name: "ConfigMapList",
  1769  			list: configMapList,
  1770  		},
  1771  	}
  1772  
  1773  	for _, tc := range tests {
  1774  		b.Run(tc.name, func(b *testing.B) {
  1775  			b.ResetTimer()
  1776  			for i := 0; i < b.N; i++ {
  1777  				_, err := meta.ExtractList(tc.list)
  1778  				if err != nil {
  1779  					b.Errorf("extract list: %v", err)
  1780  				}
  1781  			}
  1782  			b.StopTimer()
  1783  		})
  1784  	}
  1785  }
  1786  
  1787  func BenchmarkEachListItem(b *testing.B) {
  1788  	_, _, podList := getPodListItems(0, fakeItemsNum)
  1789  	_, _, configMapList := getConfigmapListItems(0, fakeItemsNum)
  1790  	tests := []struct {
  1791  		name string
  1792  		list runtime.Object
  1793  	}{
  1794  		{
  1795  			name: "PodList",
  1796  			list: podList,
  1797  		},
  1798  		{
  1799  			name: "ConfigMapList",
  1800  			list: configMapList,
  1801  		},
  1802  	}
  1803  
  1804  	for _, tc := range tests {
  1805  		b.Run(tc.name, func(b *testing.B) {
  1806  			b.ResetTimer()
  1807  			for i := 0; i < b.N; i++ {
  1808  				err := meta.EachListItem(tc.list, func(object runtime.Object) error {
  1809  					return nil
  1810  				})
  1811  				if err != nil {
  1812  					b.Errorf("each list: %v", err)
  1813  				}
  1814  			}
  1815  			b.StopTimer()
  1816  		})
  1817  	}
  1818  }
  1819  
  1820  func BenchmarkExtractListWithAlloc(b *testing.B) {
  1821  	_, _, podList := getPodListItems(0, fakeItemsNum)
  1822  	_, _, configMapList := getConfigmapListItems(0, fakeItemsNum)
  1823  	tests := []struct {
  1824  		name string
  1825  		list runtime.Object
  1826  	}{
  1827  		{
  1828  			name: "PodList",
  1829  			list: podList,
  1830  		},
  1831  		{
  1832  			name: "ConfigMapList",
  1833  			list: configMapList,
  1834  		},
  1835  	}
  1836  
  1837  	for _, tc := range tests {
  1838  		b.Run(tc.name, func(b *testing.B) {
  1839  			b.ResetTimer()
  1840  			for i := 0; i < b.N; i++ {
  1841  				_, err := meta.ExtractListWithAlloc(tc.list)
  1842  				if err != nil {
  1843  					b.Errorf("extract list with alloc: %v", err)
  1844  				}
  1845  			}
  1846  			b.StopTimer()
  1847  		})
  1848  	}
  1849  }
  1850  
  1851  func BenchmarkEachListItemWithAlloc(b *testing.B) {
  1852  	_, _, podList := getPodListItems(0, fakeItemsNum)
  1853  	_, _, configMapList := getConfigmapListItems(0, fakeItemsNum)
  1854  	tests := []struct {
  1855  		name string
  1856  		list runtime.Object
  1857  	}{
  1858  		{
  1859  			name: "PodList",
  1860  			list: podList,
  1861  		},
  1862  		{
  1863  			name: "ConfigMapList",
  1864  			list: configMapList,
  1865  		},
  1866  	}
  1867  
  1868  	for _, tc := range tests {
  1869  		b.Run(tc.name, func(b *testing.B) {
  1870  			b.ResetTimer()
  1871  			for i := 0; i < b.N; i++ {
  1872  				err := meta.EachListItemWithAlloc(tc.list, func(object runtime.Object) error {
  1873  					return nil
  1874  				})
  1875  				if err != nil {
  1876  					b.Errorf("each list with alloc: %v", err)
  1877  				}
  1878  			}
  1879  			b.StopTimer()
  1880  		})
  1881  	}
  1882  }
  1883  
  1884  func BenchmarkReflectorList(b *testing.B) {
  1885  	ctx, cancel := context.WithTimeout(context.Background(), wait.ForeverTestTimeout)
  1886  	defer cancel()
  1887  
  1888  	store := NewStore(func(obj interface{}) (string, error) {
  1889  		o, err := meta.Accessor(obj)
  1890  		if err != nil {
  1891  			return "", err
  1892  		}
  1893  		return o.GetName(), nil
  1894  	})
  1895  
  1896  	_, _, podList := getPodListItems(0, fakeItemsNum)
  1897  	_, _, configMapList := getConfigmapListItems(0, fakeItemsNum)
  1898  	tests := []struct {
  1899  		name   string
  1900  		sample func() interface{}
  1901  		list   runtime.Object
  1902  	}{
  1903  		{
  1904  			name: "PodList",
  1905  			sample: func() interface{} {
  1906  				return v1.Pod{}
  1907  			},
  1908  			list: podList,
  1909  		},
  1910  		{
  1911  			name: "ConfigMapList",
  1912  			sample: func() interface{} {
  1913  				return v1.ConfigMap{}
  1914  			},
  1915  			list: configMapList,
  1916  		},
  1917  	}
  1918  
  1919  	for _, tc := range tests {
  1920  		b.Run(tc.name, func(b *testing.B) {
  1921  
  1922  			sample := tc.sample()
  1923  			reflector := NewReflector(newPageTestLW(pageNum), &sample, store, 0)
  1924  			reflector.WatchListPageSize = fakeItemsNum
  1925  
  1926  			b.ResetTimer()
  1927  			for i := 0; i < b.N; i++ {
  1928  				err := reflector.list(ctx.Done())
  1929  				if err != nil {
  1930  					b.Fatalf("reflect list: %v", err)
  1931  				}
  1932  			}
  1933  			b.StopTimer()
  1934  		})
  1935  	}
  1936  }