k8s.io/kubernetes@v1.29.3/pkg/kubelet/util/manager/watch_based_manager_test.go (about) 1 /* 2 Copyright 2018 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 manager 18 19 import ( 20 "context" 21 "fmt" 22 "strings" 23 "testing" 24 "time" 25 26 v1 "k8s.io/api/core/v1" 27 apiequality "k8s.io/apimachinery/pkg/api/equality" 28 apierrors "k8s.io/apimachinery/pkg/api/errors" 29 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 30 "k8s.io/apimachinery/pkg/runtime" 31 "k8s.io/apimachinery/pkg/types" 32 "k8s.io/apimachinery/pkg/util/wait" 33 "k8s.io/apimachinery/pkg/watch" 34 35 clientset "k8s.io/client-go/kubernetes" 36 "k8s.io/client-go/kubernetes/fake" 37 core "k8s.io/client-go/testing" 38 39 corev1 "k8s.io/kubernetes/pkg/apis/core/v1" 40 41 "k8s.io/utils/clock" 42 testingclock "k8s.io/utils/clock/testing" 43 44 "github.com/stretchr/testify/assert" 45 ) 46 47 func listSecret(fakeClient clientset.Interface) listObjectFunc { 48 return func(namespace string, opts metav1.ListOptions) (runtime.Object, error) { 49 return fakeClient.CoreV1().Secrets(namespace).List(context.TODO(), opts) 50 } 51 } 52 53 func watchSecret(fakeClient clientset.Interface) watchObjectFunc { 54 return func(namespace string, opts metav1.ListOptions) (watch.Interface, error) { 55 return fakeClient.CoreV1().Secrets(namespace).Watch(context.TODO(), opts) 56 } 57 } 58 59 func isSecretImmutable(object runtime.Object) bool { 60 if secret, ok := object.(*v1.Secret); ok { 61 return secret.Immutable != nil && *secret.Immutable 62 } 63 return false 64 } 65 66 func newSecretCache(fakeClient clientset.Interface, fakeClock clock.Clock, maxIdleTime time.Duration) *objectCache { 67 return &objectCache{ 68 listObject: listSecret(fakeClient), 69 watchObject: watchSecret(fakeClient), 70 newObject: func() runtime.Object { return &v1.Secret{} }, 71 isImmutable: isSecretImmutable, 72 groupResource: corev1.Resource("secret"), 73 clock: fakeClock, 74 maxIdleTime: maxIdleTime, 75 items: make(map[objectKey]*objectCacheItem), 76 } 77 } 78 79 func TestSecretCache(t *testing.T) { 80 fakeClient := &fake.Clientset{} 81 82 listReactor := func(a core.Action) (bool, runtime.Object, error) { 83 result := &v1.SecretList{ 84 ListMeta: metav1.ListMeta{ 85 ResourceVersion: "123", 86 }, 87 } 88 return true, result, nil 89 } 90 fakeClient.AddReactor("list", "secrets", listReactor) 91 fakeWatch := watch.NewFake() 92 fakeClient.AddWatchReactor("secrets", core.DefaultWatchReactor(fakeWatch, nil)) 93 94 fakeClock := testingclock.NewFakeClock(time.Now()) 95 store := newSecretCache(fakeClient, fakeClock, time.Minute) 96 97 store.AddReference("ns", "name", "pod") 98 _, err := store.Get("ns", "name") 99 if !apierrors.IsNotFound(err) { 100 t.Errorf("Expected NotFound error, got: %v", err) 101 } 102 103 // Eventually we should be able to read added secret. 104 secret := &v1.Secret{ 105 ObjectMeta: metav1.ObjectMeta{Name: "name", Namespace: "ns", ResourceVersion: "125"}, 106 } 107 fakeWatch.Add(secret) 108 getFn := func() (bool, error) { 109 object, err := store.Get("ns", "name") 110 if err != nil { 111 if apierrors.IsNotFound(err) { 112 return false, nil 113 } 114 return false, err 115 } 116 secret := object.(*v1.Secret) 117 if secret == nil || secret.Name != "name" || secret.Namespace != "ns" { 118 return false, fmt.Errorf("unexpected secret: %v", secret) 119 } 120 return true, nil 121 } 122 if err := wait.PollImmediate(10*time.Millisecond, time.Second, getFn); err != nil { 123 t.Errorf("unexpected error: %v", err) 124 } 125 126 // Eventually we should observer secret deletion. 127 fakeWatch.Delete(secret) 128 getFn = func() (bool, error) { 129 _, err := store.Get("ns", "name") 130 if err != nil { 131 if apierrors.IsNotFound(err) { 132 return true, nil 133 } 134 return false, err 135 } 136 return false, nil 137 } 138 if err := wait.PollImmediate(10*time.Millisecond, time.Second, getFn); err != nil { 139 t.Errorf("unexpected error: %v", err) 140 } 141 142 store.DeleteReference("ns", "name", "pod") 143 _, err = store.Get("ns", "name") 144 if err == nil || !strings.Contains(err.Error(), "not registered") { 145 t.Errorf("unexpected error: %v", err) 146 } 147 } 148 149 func TestSecretCacheMultipleRegistrations(t *testing.T) { 150 fakeClient := &fake.Clientset{} 151 152 listReactor := func(a core.Action) (bool, runtime.Object, error) { 153 result := &v1.SecretList{ 154 ListMeta: metav1.ListMeta{ 155 ResourceVersion: "123", 156 }, 157 } 158 return true, result, nil 159 } 160 fakeClient.AddReactor("list", "secrets", listReactor) 161 fakeWatch := watch.NewFake() 162 fakeClient.AddWatchReactor("secrets", core.DefaultWatchReactor(fakeWatch, nil)) 163 164 fakeClock := testingclock.NewFakeClock(time.Now()) 165 store := newSecretCache(fakeClient, fakeClock, time.Minute) 166 167 store.AddReference("ns", "name", "pod") 168 // This should trigger List and Watch actions eventually. 169 actionsFn := func() (bool, error) { 170 actions := fakeClient.Actions() 171 if len(actions) > 2 { 172 return false, fmt.Errorf("too many actions: %v", actions) 173 } 174 if len(actions) < 2 { 175 return false, nil 176 } 177 if actions[0].GetVerb() != "list" || actions[1].GetVerb() != "watch" { 178 return false, fmt.Errorf("unexpected actions: %v", actions) 179 } 180 return true, nil 181 } 182 if err := wait.PollImmediate(10*time.Millisecond, time.Second, actionsFn); err != nil { 183 t.Errorf("unexpected error: %v", err) 184 } 185 186 // Next registrations shouldn't trigger any new actions. 187 for i := 0; i < 20; i++ { 188 store.AddReference("ns", "name", types.UID(fmt.Sprintf("pod-%d", i))) 189 store.DeleteReference("ns", "name", types.UID(fmt.Sprintf("pod-%d", i))) 190 } 191 actions := fakeClient.Actions() 192 assert.Equal(t, 2, len(actions), "unexpected actions: %#v", actions) 193 194 // Final delete also doesn't trigger any action. 195 store.DeleteReference("ns", "name", "pod") 196 _, err := store.Get("ns", "name") 197 if err == nil || !strings.Contains(err.Error(), "not registered") { 198 t.Errorf("unexpected error: %v", err) 199 } 200 actions = fakeClient.Actions() 201 assert.Equal(t, 2, len(actions), "unexpected actions: %#v", actions) 202 } 203 204 func TestImmutableSecretStopsTheReflector(t *testing.T) { 205 secret := func(rv string, immutable bool) *v1.Secret { 206 result := &v1.Secret{ 207 ObjectMeta: metav1.ObjectMeta{ 208 Name: "name", 209 Namespace: "ns", 210 ResourceVersion: rv, 211 }, 212 } 213 if immutable { 214 trueVal := true 215 result.Immutable = &trueVal 216 } 217 return result 218 } 219 220 tests := []struct { 221 desc string 222 initial *v1.Secret 223 eventual *v1.Secret 224 }{ 225 { 226 desc: "secret doesn't exist, created as mutable", 227 initial: nil, 228 eventual: secret("200", false), 229 }, 230 { 231 desc: "secret doesn't exist, created as immutable", 232 initial: nil, 233 eventual: secret("200", true), 234 }, 235 { 236 desc: "mutable secret modified to mutable", 237 initial: secret("100", false), 238 eventual: secret("200", false), 239 }, 240 { 241 desc: "mutable secret modified to immutable", 242 initial: secret("100", false), 243 eventual: secret("200", true), 244 }, 245 { 246 desc: "immutable secret", 247 initial: secret("100", true), 248 eventual: nil, 249 }, 250 } 251 252 for _, tc := range tests { 253 t.Run(tc.desc, func(t *testing.T) { 254 fakeClient := &fake.Clientset{} 255 listReactor := func(a core.Action) (bool, runtime.Object, error) { 256 result := &v1.SecretList{ 257 ListMeta: metav1.ListMeta{ 258 ResourceVersion: "100", 259 }, 260 } 261 if tc.initial != nil { 262 result.Items = []v1.Secret{*tc.initial} 263 } 264 return true, result, nil 265 } 266 fakeClient.AddReactor("list", "secrets", listReactor) 267 fakeWatch := watch.NewFake() 268 fakeClient.AddWatchReactor("secrets", core.DefaultWatchReactor(fakeWatch, nil)) 269 270 fakeClock := testingclock.NewFakeClock(time.Now()) 271 store := newSecretCache(fakeClient, fakeClock, time.Minute) 272 273 key := objectKey{namespace: "ns", name: "name"} 274 itemExists := func() (bool, error) { 275 store.lock.Lock() 276 defer store.lock.Unlock() 277 _, ok := store.items[key] 278 return ok, nil 279 } 280 reflectorRunning := func() bool { 281 store.lock.Lock() 282 defer store.lock.Unlock() 283 item := store.items[key] 284 285 item.lock.Lock() 286 defer item.lock.Unlock() 287 return !item.stopped 288 } 289 290 // AddReference should start reflector. 291 store.AddReference("ns", "name", "pod") 292 if err := wait.Poll(10*time.Millisecond, time.Second, itemExists); err != nil { 293 t.Errorf("item wasn't added to cache") 294 } 295 296 obj, err := store.Get("ns", "name") 297 if tc.initial != nil { 298 assert.True(t, apiequality.Semantic.DeepEqual(tc.initial, obj)) 299 } else { 300 assert.True(t, apierrors.IsNotFound(err)) 301 } 302 303 // Reflector should already be stopped for immutable secrets. 304 assert.Equal(t, tc.initial == nil || !isSecretImmutable(tc.initial), reflectorRunning()) 305 306 if tc.eventual == nil { 307 return 308 } 309 fakeWatch.Add(tc.eventual) 310 311 // Eventually Get should return that secret. 312 getFn := func() (bool, error) { 313 object, err := store.Get("ns", "name") 314 if err != nil { 315 if apierrors.IsNotFound(err) { 316 return false, nil 317 } 318 return false, err 319 } 320 secret := object.(*v1.Secret) 321 return apiequality.Semantic.DeepEqual(tc.eventual, secret), nil 322 } 323 if err := wait.PollImmediate(10*time.Millisecond, time.Second, getFn); err != nil { 324 t.Errorf("unexpected error: %v", err) 325 } 326 327 // Reflector should already be stopped for immutable secrets. 328 assert.Equal(t, tc.eventual == nil || !isSecretImmutable(tc.eventual), reflectorRunning()) 329 }) 330 } 331 } 332 333 func TestMaxIdleTimeStopsTheReflector(t *testing.T) { 334 secret := &v1.Secret{ 335 ObjectMeta: metav1.ObjectMeta{ 336 Name: "name", 337 Namespace: "ns", 338 ResourceVersion: "200", 339 }, 340 } 341 342 fakeClient := &fake.Clientset{} 343 listReactor := func(a core.Action) (bool, runtime.Object, error) { 344 result := &v1.SecretList{ 345 ListMeta: metav1.ListMeta{ 346 ResourceVersion: "200", 347 }, 348 Items: []v1.Secret{*secret}, 349 } 350 351 return true, result, nil 352 } 353 354 fakeClient.AddReactor("list", "secrets", listReactor) 355 fakeWatch := watch.NewFake() 356 fakeClient.AddWatchReactor("secrets", core.DefaultWatchReactor(fakeWatch, nil)) 357 fakeClock := testingclock.NewFakeClock(time.Now()) 358 store := newSecretCache(fakeClient, fakeClock, time.Minute) 359 360 key := objectKey{namespace: "ns", name: "name"} 361 itemExists := func() (bool, error) { 362 store.lock.Lock() 363 defer store.lock.Unlock() 364 _, ok := store.items[key] 365 return ok, nil 366 } 367 368 reflectorRunning := func() bool { 369 store.lock.Lock() 370 defer store.lock.Unlock() 371 item := store.items[key] 372 373 item.lock.Lock() 374 defer item.lock.Unlock() 375 return !item.stopped 376 } 377 378 // AddReference should start reflector. 379 store.AddReference("ns", "name", "pod") 380 if err := wait.Poll(10*time.Millisecond, 10*time.Second, itemExists); err != nil { 381 t.Errorf("item wasn't added to cache") 382 } 383 384 obj, _ := store.Get("ns", "name") 385 assert.True(t, apiequality.Semantic.DeepEqual(secret, obj)) 386 387 assert.True(t, reflectorRunning()) 388 389 fakeClock.Step(90 * time.Second) 390 store.startRecycleIdleWatch() 391 392 // Reflector should already be stopped for maxIdleTime exceeded. 393 assert.False(t, reflectorRunning()) 394 395 obj, _ = store.Get("ns", "name") 396 assert.True(t, apiequality.Semantic.DeepEqual(secret, obj)) 397 // Reflector should reRun after get secret again. 398 assert.True(t, reflectorRunning()) 399 400 fakeClock.Step(20 * time.Second) 401 _, _ = store.Get("ns", "name") 402 fakeClock.Step(20 * time.Second) 403 _, _ = store.Get("ns", "name") 404 fakeClock.Step(20 * time.Second) 405 _, _ = store.Get("ns", "name") 406 store.startRecycleIdleWatch() 407 408 // Reflector should be running when the get function is called periodically. 409 assert.True(t, reflectorRunning()) 410 } 411 412 func TestReflectorNotStoppedOnSlowInitialization(t *testing.T) { 413 secret := &v1.Secret{ 414 ObjectMeta: metav1.ObjectMeta{ 415 Name: "name", 416 Namespace: "ns", 417 ResourceVersion: "200", 418 }, 419 } 420 421 fakeClock := testingclock.NewFakeClock(time.Now()) 422 423 fakeClient := &fake.Clientset{} 424 listReactor := func(a core.Action) (bool, runtime.Object, error) { 425 <-fakeClock.After(120 * time.Second) 426 427 result := &v1.SecretList{ 428 ListMeta: metav1.ListMeta{ 429 ResourceVersion: "200", 430 }, 431 Items: []v1.Secret{*secret}, 432 } 433 434 return true, result, nil 435 } 436 437 fakeClient.AddReactor("list", "secrets", listReactor) 438 fakeWatch := watch.NewFake() 439 fakeClient.AddWatchReactor("secrets", core.DefaultWatchReactor(fakeWatch, nil)) 440 store := newSecretCache(fakeClient, fakeClock, time.Minute) 441 442 key := objectKey{namespace: "ns", name: "name"} 443 itemExists := func() (bool, error) { 444 store.lock.Lock() 445 defer store.lock.Unlock() 446 _, ok := store.items[key] 447 return ok, nil 448 } 449 450 reflectorRunning := func() bool { 451 store.lock.Lock() 452 defer store.lock.Unlock() 453 item := store.items[key] 454 455 item.lock.Lock() 456 defer item.lock.Unlock() 457 return !item.stopped 458 } 459 460 reflectorInitialized := func() (bool, error) { 461 store.lock.Lock() 462 defer store.lock.Unlock() 463 item := store.items[key] 464 465 item.lock.Lock() 466 defer item.lock.Unlock() 467 return item.store.hasSynced(), nil 468 } 469 470 // AddReference should start reflector. 471 store.AddReference("ns", "name", "pod") 472 if err := wait.Poll(10*time.Millisecond, 10*time.Second, itemExists); err != nil { 473 t.Errorf("item wasn't added to cache") 474 } 475 476 fakeClock.Step(90 * time.Second) 477 store.startRecycleIdleWatch() 478 479 // Reflector didn't yet initialize, so it shouldn't be stopped. 480 // However, Get should still be failing. 481 assert.True(t, reflectorRunning()) 482 initialized, _ := reflectorInitialized() 483 assert.False(t, initialized) 484 _, err := store.Get("ns", "name") 485 if err == nil || !strings.Contains(err.Error(), "failed to sync") { 486 t.Errorf("Expected failed to sync error, got: %v", err) 487 } 488 489 // Initialization should successfully finish. 490 fakeClock.Step(30 * time.Second) 491 if err := wait.Poll(10*time.Millisecond, time.Second, reflectorInitialized); err != nil { 492 t.Errorf("reflector didn't iniailize correctly") 493 } 494 495 // recycling shouldn't stop the reflector because it was accessed within last minute. 496 store.startRecycleIdleWatch() 497 assert.True(t, reflectorRunning()) 498 499 obj, _ := store.Get("ns", "name") 500 assert.True(t, apiequality.Semantic.DeepEqual(secret, obj)) 501 } 502 503 func TestRefMapHandlesReferencesCorrectly(t *testing.T) { 504 secret1 := &v1.Secret{ 505 ObjectMeta: metav1.ObjectMeta{ 506 Name: "secret1", 507 Namespace: "ns1", 508 }, 509 } 510 type step struct { 511 action string 512 ns string 513 name string 514 referencedFrom types.UID 515 } 516 type expect struct { 517 ns string 518 name string 519 referencedFrom types.UID 520 expectCount int 521 } 522 tests := []struct { 523 desc string 524 steps []step 525 expects []expect 526 }{ 527 { 528 desc: "adding and deleting should works", 529 steps: []step{ 530 {"add", "ns1", "secret1", "pod1"}, 531 {"add", "ns1", "secret1", "pod1"}, 532 {"delete", "ns1", "secret1", "pod1"}, 533 {"delete", "ns1", "secret1", "pod1"}, 534 }, 535 expects: []expect{ 536 {"ns1", "secret1", "pod1", 1}, 537 {"ns1", "secret1", "pod1", 2}, 538 {"ns1", "secret1", "pod1", 1}, 539 {"ns1", "secret1", "pod1", 0}, 540 }, 541 }, 542 { 543 desc: "deleting a non-existent reference should have no effect", 544 steps: []step{ 545 {"delete", "ns1", "secret1", "pod1"}, 546 }, 547 expects: []expect{ 548 {"ns1", "secret1", "pod1", 0}, 549 }, 550 }, 551 { 552 desc: "deleting more than adding should not lead to negative refcount", 553 steps: []step{ 554 {"add", "ns1", "secret1", "pod1"}, 555 {"delete", "ns1", "secret1", "pod1"}, 556 {"delete", "ns1", "secret1", "pod1"}, 557 }, 558 expects: []expect{ 559 {"ns1", "secret1", "pod1", 1}, 560 {"ns1", "secret1", "pod1", 0}, 561 {"ns1", "secret1", "pod1", 0}, 562 }, 563 }, 564 { 565 desc: "deleting should not affect refcount of other objects or referencedFrom", 566 steps: []step{ 567 {"add", "ns1", "secret1", "pod1"}, 568 {"delete", "ns1", "secret1", "pod2"}, 569 {"delete", "ns1", "secret2", "pod1"}, 570 {"delete", "ns2", "secret1", "pod1"}, 571 }, 572 expects: []expect{ 573 {"ns1", "secret1", "pod1", 1}, 574 {"ns1", "secret1", "pod1", 1}, 575 {"ns1", "secret1", "pod1", 1}, 576 {"ns1", "secret1", "pod1", 1}, 577 }, 578 }, 579 } 580 for _, tc := range tests { 581 t.Run(tc.desc, func(t *testing.T) { 582 fakeClient := &fake.Clientset{} 583 listReactor := func(a core.Action) (bool, runtime.Object, error) { 584 result := &v1.SecretList{ 585 ListMeta: metav1.ListMeta{ 586 ResourceVersion: "200", 587 }, 588 Items: []v1.Secret{*secret1}, 589 } 590 return true, result, nil 591 } 592 fakeClient.AddReactor("list", "secrets", listReactor) 593 fakeWatch := watch.NewFake() 594 fakeClient.AddWatchReactor("secrets", core.DefaultWatchReactor(fakeWatch, nil)) 595 fakeClock := testingclock.NewFakeClock(time.Now()) 596 store := newSecretCache(fakeClient, fakeClock, time.Minute) 597 598 for i, step := range tc.steps { 599 expect := tc.expects[i] 600 switch step.action { 601 case "add": 602 store.AddReference(step.ns, step.name, step.referencedFrom) 603 case "delete": 604 store.DeleteReference(step.ns, step.name, step.referencedFrom) 605 default: 606 t.Errorf("unrecognized action of testcase %v", tc.desc) 607 } 608 609 key := objectKey{namespace: expect.ns, name: expect.name} 610 item, exists := store.items[key] 611 if !exists { 612 if tc.expects[i].expectCount != 0 { 613 t.Errorf("reference to %v/%v from %v should exists", expect.ns, expect.name, expect.referencedFrom) 614 } 615 } else if item.refMap[expect.referencedFrom] != expect.expectCount { 616 t.Errorf("expects %v but got %v", expect.expectCount, item.refMap[expect.referencedFrom]) 617 } 618 } 619 }) 620 } 621 }