k8s.io/client-go@v0.22.2/tools/cache/shared_informer_test.go (about)

     1  /*
     2  Copyright 2017 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  	"fmt"
    21  	"strings"
    22  	"sync"
    23  	"testing"
    24  	"time"
    25  
    26  	"k8s.io/api/core/v1"
    27  	"k8s.io/apimachinery/pkg/api/meta"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/util/clock"
    30  	"k8s.io/apimachinery/pkg/util/sets"
    31  	"k8s.io/apimachinery/pkg/util/wait"
    32  	fcache "k8s.io/client-go/tools/cache/testing"
    33  )
    34  
    35  type testListener struct {
    36  	lock              sync.RWMutex
    37  	resyncPeriod      time.Duration
    38  	expectedItemNames sets.String
    39  	receivedItemNames []string
    40  	name              string
    41  }
    42  
    43  func newTestListener(name string, resyncPeriod time.Duration, expected ...string) *testListener {
    44  	l := &testListener{
    45  		resyncPeriod:      resyncPeriod,
    46  		expectedItemNames: sets.NewString(expected...),
    47  		name:              name,
    48  	}
    49  	return l
    50  }
    51  
    52  func (l *testListener) OnAdd(obj interface{}) {
    53  	l.handle(obj)
    54  }
    55  
    56  func (l *testListener) OnUpdate(old, new interface{}) {
    57  	l.handle(new)
    58  }
    59  
    60  func (l *testListener) OnDelete(obj interface{}) {
    61  }
    62  
    63  func (l *testListener) handle(obj interface{}) {
    64  	key, _ := MetaNamespaceKeyFunc(obj)
    65  	fmt.Printf("%s: handle: %v\n", l.name, key)
    66  	l.lock.Lock()
    67  	defer l.lock.Unlock()
    68  
    69  	objectMeta, _ := meta.Accessor(obj)
    70  	l.receivedItemNames = append(l.receivedItemNames, objectMeta.GetName())
    71  }
    72  
    73  func (l *testListener) ok() bool {
    74  	fmt.Println("polling")
    75  	err := wait.PollImmediate(100*time.Millisecond, 2*time.Second, func() (bool, error) {
    76  		if l.satisfiedExpectations() {
    77  			return true, nil
    78  		}
    79  		return false, nil
    80  	})
    81  	if err != nil {
    82  		return false
    83  	}
    84  
    85  	// wait just a bit to allow any unexpected stragglers to come in
    86  	fmt.Println("sleeping")
    87  	time.Sleep(1 * time.Second)
    88  	fmt.Println("final check")
    89  	return l.satisfiedExpectations()
    90  }
    91  
    92  func (l *testListener) satisfiedExpectations() bool {
    93  	l.lock.RLock()
    94  	defer l.lock.RUnlock()
    95  
    96  	return sets.NewString(l.receivedItemNames...).Equal(l.expectedItemNames)
    97  }
    98  
    99  func TestListenerResyncPeriods(t *testing.T) {
   100  	// source simulates an apiserver object endpoint.
   101  	source := fcache.NewFakeControllerSource()
   102  	source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1"}})
   103  	source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod2"}})
   104  
   105  	// create the shared informer and resync every 1s
   106  	informer := NewSharedInformer(source, &v1.Pod{}, 1*time.Second).(*sharedIndexInformer)
   107  
   108  	clock := clock.NewFakeClock(time.Now())
   109  	informer.clock = clock
   110  	informer.processor.clock = clock
   111  
   112  	// listener 1, never resync
   113  	listener1 := newTestListener("listener1", 0, "pod1", "pod2")
   114  	informer.AddEventHandlerWithResyncPeriod(listener1, listener1.resyncPeriod)
   115  
   116  	// listener 2, resync every 2s
   117  	listener2 := newTestListener("listener2", 2*time.Second, "pod1", "pod2")
   118  	informer.AddEventHandlerWithResyncPeriod(listener2, listener2.resyncPeriod)
   119  
   120  	// listener 3, resync every 3s
   121  	listener3 := newTestListener("listener3", 3*time.Second, "pod1", "pod2")
   122  	informer.AddEventHandlerWithResyncPeriod(listener3, listener3.resyncPeriod)
   123  	listeners := []*testListener{listener1, listener2, listener3}
   124  
   125  	stop := make(chan struct{})
   126  	defer close(stop)
   127  
   128  	go informer.Run(stop)
   129  
   130  	// ensure all listeners got the initial List
   131  	for _, listener := range listeners {
   132  		if !listener.ok() {
   133  			t.Errorf("%s: expected %v, got %v", listener.name, listener.expectedItemNames, listener.receivedItemNames)
   134  		}
   135  	}
   136  
   137  	// reset
   138  	for _, listener := range listeners {
   139  		listener.receivedItemNames = []string{}
   140  	}
   141  
   142  	// advance so listener2 gets a resync
   143  	clock.Step(2 * time.Second)
   144  
   145  	// make sure listener2 got the resync
   146  	if !listener2.ok() {
   147  		t.Errorf("%s: expected %v, got %v", listener2.name, listener2.expectedItemNames, listener2.receivedItemNames)
   148  	}
   149  
   150  	// wait a bit to give errant items a chance to go to 1 and 3
   151  	time.Sleep(1 * time.Second)
   152  
   153  	// make sure listeners 1 and 3 got nothing
   154  	if len(listener1.receivedItemNames) != 0 {
   155  		t.Errorf("listener1: should not have resynced (got %d)", len(listener1.receivedItemNames))
   156  	}
   157  	if len(listener3.receivedItemNames) != 0 {
   158  		t.Errorf("listener3: should not have resynced (got %d)", len(listener3.receivedItemNames))
   159  	}
   160  
   161  	// reset
   162  	for _, listener := range listeners {
   163  		listener.receivedItemNames = []string{}
   164  	}
   165  
   166  	// advance so listener3 gets a resync
   167  	clock.Step(1 * time.Second)
   168  
   169  	// make sure listener3 got the resync
   170  	if !listener3.ok() {
   171  		t.Errorf("%s: expected %v, got %v", listener3.name, listener3.expectedItemNames, listener3.receivedItemNames)
   172  	}
   173  
   174  	// wait a bit to give errant items a chance to go to 1 and 2
   175  	time.Sleep(1 * time.Second)
   176  
   177  	// make sure listeners 1 and 2 got nothing
   178  	if len(listener1.receivedItemNames) != 0 {
   179  		t.Errorf("listener1: should not have resynced (got %d)", len(listener1.receivedItemNames))
   180  	}
   181  	if len(listener2.receivedItemNames) != 0 {
   182  		t.Errorf("listener2: should not have resynced (got %d)", len(listener2.receivedItemNames))
   183  	}
   184  }
   185  
   186  func TestResyncCheckPeriod(t *testing.T) {
   187  	// source simulates an apiserver object endpoint.
   188  	source := fcache.NewFakeControllerSource()
   189  
   190  	// create the shared informer and resync every 12 hours
   191  	informer := NewSharedInformer(source, &v1.Pod{}, 12*time.Hour).(*sharedIndexInformer)
   192  
   193  	clock := clock.NewFakeClock(time.Now())
   194  	informer.clock = clock
   195  	informer.processor.clock = clock
   196  
   197  	// listener 1, never resync
   198  	listener1 := newTestListener("listener1", 0)
   199  	informer.AddEventHandlerWithResyncPeriod(listener1, listener1.resyncPeriod)
   200  	if e, a := 12*time.Hour, informer.resyncCheckPeriod; e != a {
   201  		t.Errorf("expected %d, got %d", e, a)
   202  	}
   203  	if e, a := time.Duration(0), informer.processor.listeners[0].resyncPeriod; e != a {
   204  		t.Errorf("expected %d, got %d", e, a)
   205  	}
   206  
   207  	// listener 2, resync every minute
   208  	listener2 := newTestListener("listener2", 1*time.Minute)
   209  	informer.AddEventHandlerWithResyncPeriod(listener2, listener2.resyncPeriod)
   210  	if e, a := 1*time.Minute, informer.resyncCheckPeriod; e != a {
   211  		t.Errorf("expected %d, got %d", e, a)
   212  	}
   213  	if e, a := time.Duration(0), informer.processor.listeners[0].resyncPeriod; e != a {
   214  		t.Errorf("expected %d, got %d", e, a)
   215  	}
   216  	if e, a := 1*time.Minute, informer.processor.listeners[1].resyncPeriod; e != a {
   217  		t.Errorf("expected %d, got %d", e, a)
   218  	}
   219  
   220  	// listener 3, resync every 55 seconds
   221  	listener3 := newTestListener("listener3", 55*time.Second)
   222  	informer.AddEventHandlerWithResyncPeriod(listener3, listener3.resyncPeriod)
   223  	if e, a := 55*time.Second, informer.resyncCheckPeriod; e != a {
   224  		t.Errorf("expected %d, got %d", e, a)
   225  	}
   226  	if e, a := time.Duration(0), informer.processor.listeners[0].resyncPeriod; e != a {
   227  		t.Errorf("expected %d, got %d", e, a)
   228  	}
   229  	if e, a := 1*time.Minute, informer.processor.listeners[1].resyncPeriod; e != a {
   230  		t.Errorf("expected %d, got %d", e, a)
   231  	}
   232  	if e, a := 55*time.Second, informer.processor.listeners[2].resyncPeriod; e != a {
   233  		t.Errorf("expected %d, got %d", e, a)
   234  	}
   235  
   236  	// listener 4, resync every 5 seconds
   237  	listener4 := newTestListener("listener4", 5*time.Second)
   238  	informer.AddEventHandlerWithResyncPeriod(listener4, listener4.resyncPeriod)
   239  	if e, a := 5*time.Second, informer.resyncCheckPeriod; e != a {
   240  		t.Errorf("expected %d, got %d", e, a)
   241  	}
   242  	if e, a := time.Duration(0), informer.processor.listeners[0].resyncPeriod; e != a {
   243  		t.Errorf("expected %d, got %d", e, a)
   244  	}
   245  	if e, a := 1*time.Minute, informer.processor.listeners[1].resyncPeriod; e != a {
   246  		t.Errorf("expected %d, got %d", e, a)
   247  	}
   248  	if e, a := 55*time.Second, informer.processor.listeners[2].resyncPeriod; e != a {
   249  		t.Errorf("expected %d, got %d", e, a)
   250  	}
   251  	if e, a := 5*time.Second, informer.processor.listeners[3].resyncPeriod; e != a {
   252  		t.Errorf("expected %d, got %d", e, a)
   253  	}
   254  }
   255  
   256  // verify that https://github.com/kubernetes/kubernetes/issues/59822 is fixed
   257  func TestSharedInformerInitializationRace(t *testing.T) {
   258  	source := fcache.NewFakeControllerSource()
   259  	informer := NewSharedInformer(source, &v1.Pod{}, 1*time.Second).(*sharedIndexInformer)
   260  	listener := newTestListener("raceListener", 0)
   261  
   262  	stop := make(chan struct{})
   263  	go informer.AddEventHandlerWithResyncPeriod(listener, listener.resyncPeriod)
   264  	go informer.Run(stop)
   265  	close(stop)
   266  }
   267  
   268  // TestSharedInformerWatchDisruption simulates a watch that was closed
   269  // with updates to the store during that time. We ensure that handlers with
   270  // resync and no resync see the expected state.
   271  func TestSharedInformerWatchDisruption(t *testing.T) {
   272  	// source simulates an apiserver object endpoint.
   273  	source := fcache.NewFakeControllerSource()
   274  
   275  	source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1", UID: "pod1", ResourceVersion: "1"}})
   276  	source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod2", UID: "pod2", ResourceVersion: "2"}})
   277  
   278  	// create the shared informer and resync every 1s
   279  	informer := NewSharedInformer(source, &v1.Pod{}, 1*time.Second).(*sharedIndexInformer)
   280  
   281  	clock := clock.NewFakeClock(time.Now())
   282  	informer.clock = clock
   283  	informer.processor.clock = clock
   284  
   285  	// listener, never resync
   286  	listenerNoResync := newTestListener("listenerNoResync", 0, "pod1", "pod2")
   287  	informer.AddEventHandlerWithResyncPeriod(listenerNoResync, listenerNoResync.resyncPeriod)
   288  
   289  	listenerResync := newTestListener("listenerResync", 1*time.Second, "pod1", "pod2")
   290  	informer.AddEventHandlerWithResyncPeriod(listenerResync, listenerResync.resyncPeriod)
   291  	listeners := []*testListener{listenerNoResync, listenerResync}
   292  
   293  	stop := make(chan struct{})
   294  	defer close(stop)
   295  
   296  	go informer.Run(stop)
   297  
   298  	for _, listener := range listeners {
   299  		if !listener.ok() {
   300  			t.Errorf("%s: expected %v, got %v", listener.name, listener.expectedItemNames, listener.receivedItemNames)
   301  		}
   302  	}
   303  
   304  	// Add pod3, bump pod2 but don't broadcast it, so that the change will be seen only on relist
   305  	source.AddDropWatch(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod3", UID: "pod3", ResourceVersion: "3"}})
   306  	source.ModifyDropWatch(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod2", UID: "pod2", ResourceVersion: "4"}})
   307  
   308  	// Ensure that nobody saw any changes
   309  	for _, listener := range listeners {
   310  		if !listener.ok() {
   311  			t.Errorf("%s: expected %v, got %v", listener.name, listener.expectedItemNames, listener.receivedItemNames)
   312  		}
   313  	}
   314  
   315  	for _, listener := range listeners {
   316  		listener.receivedItemNames = []string{}
   317  	}
   318  
   319  	listenerNoResync.expectedItemNames = sets.NewString("pod2", "pod3")
   320  	listenerResync.expectedItemNames = sets.NewString("pod1", "pod2", "pod3")
   321  
   322  	// This calls shouldSync, which deletes noResync from the list of syncingListeners
   323  	clock.Step(1 * time.Second)
   324  
   325  	// Simulate a connection loss (or even just a too-old-watch)
   326  	source.ResetWatch()
   327  
   328  	for _, listener := range listeners {
   329  		if !listener.ok() {
   330  			t.Errorf("%s: expected %v, got %v", listener.name, listener.expectedItemNames, listener.receivedItemNames)
   331  		}
   332  	}
   333  }
   334  
   335  func TestSharedInformerErrorHandling(t *testing.T) {
   336  	source := fcache.NewFakeControllerSource()
   337  	source.Add(&v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "pod1"}})
   338  	source.ListError = fmt.Errorf("Access Denied")
   339  
   340  	informer := NewSharedInformer(source, &v1.Pod{}, 1*time.Second).(*sharedIndexInformer)
   341  
   342  	errCh := make(chan error)
   343  	_ = informer.SetWatchErrorHandler(func(_ *Reflector, err error) {
   344  		errCh <- err
   345  	})
   346  
   347  	stop := make(chan struct{})
   348  	go informer.Run(stop)
   349  
   350  	select {
   351  	case err := <-errCh:
   352  		if !strings.Contains(err.Error(), "Access Denied") {
   353  			t.Errorf("Expected 'Access Denied' error. Actual: %v", err)
   354  		}
   355  	case <-time.After(time.Second):
   356  		t.Errorf("Timeout waiting for error handler call")
   357  	}
   358  	close(stop)
   359  }