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 }