k8s.io/kubernetes@v1.29.3/pkg/controller/deployment/deployment_controller_test.go (about) 1 /* 2 Copyright 2015 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 deployment 18 19 import ( 20 "context" 21 "fmt" 22 "strconv" 23 "testing" 24 25 apps "k8s.io/api/apps/v1" 26 v1 "k8s.io/api/core/v1" 27 extensions "k8s.io/api/extensions/v1beta1" 28 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 29 "k8s.io/apimachinery/pkg/runtime" 30 "k8s.io/apimachinery/pkg/runtime/schema" 31 "k8s.io/apimachinery/pkg/util/intstr" 32 "k8s.io/apimachinery/pkg/util/uuid" 33 "k8s.io/client-go/informers" 34 "k8s.io/client-go/kubernetes/fake" 35 core "k8s.io/client-go/testing" 36 "k8s.io/client-go/tools/record" 37 "k8s.io/klog/v2" 38 "k8s.io/klog/v2/ktesting" 39 _ "k8s.io/kubernetes/pkg/apis/apps/install" 40 _ "k8s.io/kubernetes/pkg/apis/authentication/install" 41 _ "k8s.io/kubernetes/pkg/apis/authorization/install" 42 _ "k8s.io/kubernetes/pkg/apis/autoscaling/install" 43 _ "k8s.io/kubernetes/pkg/apis/batch/install" 44 _ "k8s.io/kubernetes/pkg/apis/certificates/install" 45 _ "k8s.io/kubernetes/pkg/apis/core/install" 46 _ "k8s.io/kubernetes/pkg/apis/policy/install" 47 _ "k8s.io/kubernetes/pkg/apis/rbac/install" 48 _ "k8s.io/kubernetes/pkg/apis/storage/install" 49 "k8s.io/kubernetes/pkg/controller" 50 "k8s.io/kubernetes/pkg/controller/deployment/util" 51 "k8s.io/kubernetes/pkg/controller/testutil" 52 "k8s.io/utils/ptr" 53 ) 54 55 var ( 56 alwaysReady = func() bool { return true } 57 noTimestamp = metav1.Time{} 58 ) 59 60 func rs(name string, replicas int32, selector map[string]string, timestamp metav1.Time) *apps.ReplicaSet { 61 return &apps.ReplicaSet{ 62 ObjectMeta: metav1.ObjectMeta{ 63 Name: name, 64 CreationTimestamp: timestamp, 65 Namespace: metav1.NamespaceDefault, 66 }, 67 Spec: apps.ReplicaSetSpec{ 68 Replicas: ptr.To(replicas), 69 Selector: &metav1.LabelSelector{MatchLabels: selector}, 70 Template: v1.PodTemplateSpec{}, 71 }, 72 } 73 } 74 75 func newRSWithStatus(name string, specReplicas, statusReplicas int32, selector map[string]string) *apps.ReplicaSet { 76 rs := rs(name, specReplicas, selector, noTimestamp) 77 rs.Status = apps.ReplicaSetStatus{ 78 Replicas: statusReplicas, 79 } 80 return rs 81 } 82 83 func newDeployment(name string, replicas int32, revisionHistoryLimit *int32, maxSurge, maxUnavailable *intstr.IntOrString, selector map[string]string) *apps.Deployment { 84 d := apps.Deployment{ 85 TypeMeta: metav1.TypeMeta{APIVersion: "apps/v1", Kind: "Deployment"}, 86 ObjectMeta: metav1.ObjectMeta{ 87 UID: uuid.NewUUID(), 88 Name: name, 89 Namespace: metav1.NamespaceDefault, 90 Annotations: make(map[string]string), 91 }, 92 Spec: apps.DeploymentSpec{ 93 Strategy: apps.DeploymentStrategy{ 94 Type: apps.RollingUpdateDeploymentStrategyType, 95 RollingUpdate: &apps.RollingUpdateDeployment{ 96 MaxUnavailable: ptr.To(intstr.FromInt32(0)), 97 MaxSurge: ptr.To(intstr.FromInt32(0)), 98 }, 99 }, 100 Replicas: ptr.To(replicas), 101 Selector: &metav1.LabelSelector{MatchLabels: selector}, 102 Template: v1.PodTemplateSpec{ 103 ObjectMeta: metav1.ObjectMeta{ 104 Labels: selector, 105 }, 106 Spec: v1.PodSpec{ 107 Containers: []v1.Container{ 108 { 109 Image: "foo/bar", 110 }, 111 }, 112 }, 113 }, 114 RevisionHistoryLimit: revisionHistoryLimit, 115 }, 116 } 117 if maxSurge != nil { 118 d.Spec.Strategy.RollingUpdate.MaxSurge = maxSurge 119 } 120 if maxUnavailable != nil { 121 d.Spec.Strategy.RollingUpdate.MaxUnavailable = maxUnavailable 122 } 123 return &d 124 } 125 126 func newReplicaSet(d *apps.Deployment, name string, replicas int32) *apps.ReplicaSet { 127 return &apps.ReplicaSet{ 128 TypeMeta: metav1.TypeMeta{Kind: "ReplicaSet"}, 129 ObjectMeta: metav1.ObjectMeta{ 130 Name: name, 131 UID: uuid.NewUUID(), 132 Namespace: metav1.NamespaceDefault, 133 Labels: d.Spec.Selector.MatchLabels, 134 OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(d, controllerKind)}, 135 }, 136 Spec: apps.ReplicaSetSpec{ 137 Selector: d.Spec.Selector, 138 Replicas: ptr.To(replicas), 139 Template: d.Spec.Template, 140 }, 141 } 142 } 143 144 type fixture struct { 145 t testing.TB 146 147 client *fake.Clientset 148 // Objects to put in the store. 149 dLister []*apps.Deployment 150 rsLister []*apps.ReplicaSet 151 podLister []*v1.Pod 152 153 // Actions expected to happen on the client. Objects from here are also 154 // preloaded into NewSimpleFake. 155 actions []core.Action 156 objects []runtime.Object 157 } 158 159 func (f *fixture) expectGetDeploymentAction(d *apps.Deployment) { 160 action := core.NewGetAction(schema.GroupVersionResource{Resource: "deployments"}, d.Namespace, d.Name) 161 f.actions = append(f.actions, action) 162 } 163 164 func (f *fixture) expectUpdateDeploymentStatusAction(d *apps.Deployment) { 165 action := core.NewUpdateAction(schema.GroupVersionResource{Resource: "deployments"}, d.Namespace, d) 166 action.Subresource = "status" 167 f.actions = append(f.actions, action) 168 } 169 170 func (f *fixture) expectUpdateDeploymentAction(d *apps.Deployment) { 171 action := core.NewUpdateAction(schema.GroupVersionResource{Resource: "deployments"}, d.Namespace, d) 172 f.actions = append(f.actions, action) 173 } 174 175 func (f *fixture) expectCreateRSAction(rs *apps.ReplicaSet) { 176 f.actions = append(f.actions, core.NewCreateAction(schema.GroupVersionResource{Resource: "replicasets"}, rs.Namespace, rs)) 177 } 178 179 func newFixture(t testing.TB) *fixture { 180 f := &fixture{} 181 f.t = t 182 f.objects = []runtime.Object{} 183 return f 184 } 185 186 func (f *fixture) newController(ctx context.Context) (*DeploymentController, informers.SharedInformerFactory, error) { 187 f.client = fake.NewSimpleClientset(f.objects...) 188 informers := informers.NewSharedInformerFactory(f.client, controller.NoResyncPeriodFunc()) 189 c, err := NewDeploymentController(ctx, informers.Apps().V1().Deployments(), informers.Apps().V1().ReplicaSets(), informers.Core().V1().Pods(), f.client) 190 if err != nil { 191 return nil, nil, err 192 } 193 c.eventRecorder = &record.FakeRecorder{} 194 c.dListerSynced = alwaysReady 195 c.rsListerSynced = alwaysReady 196 c.podListerSynced = alwaysReady 197 for _, d := range f.dLister { 198 informers.Apps().V1().Deployments().Informer().GetIndexer().Add(d) 199 } 200 for _, rs := range f.rsLister { 201 informers.Apps().V1().ReplicaSets().Informer().GetIndexer().Add(rs) 202 } 203 for _, pod := range f.podLister { 204 informers.Core().V1().Pods().Informer().GetIndexer().Add(pod) 205 } 206 return c, informers, nil 207 } 208 209 func (f *fixture) runExpectError(ctx context.Context, deploymentName string, startInformers bool) { 210 f.run_(ctx, deploymentName, startInformers, true) 211 } 212 213 func (f *fixture) run(ctx context.Context, deploymentName string) { 214 f.run_(ctx, deploymentName, true, false) 215 } 216 217 func (f *fixture) run_(ctx context.Context, deploymentName string, startInformers bool, expectError bool) { 218 c, informers, err := f.newController(ctx) 219 if err != nil { 220 f.t.Fatalf("error creating Deployment controller: %v", err) 221 } 222 if startInformers { 223 stopCh := make(chan struct{}) 224 defer close(stopCh) 225 informers.Start(stopCh) 226 } 227 228 err = c.syncDeployment(ctx, deploymentName) 229 if !expectError && err != nil { 230 f.t.Errorf("error syncing deployment: %v", err) 231 } else if expectError && err == nil { 232 f.t.Error("expected error syncing deployment, got nil") 233 } 234 235 actions := filterInformerActions(f.client.Actions()) 236 for i, action := range actions { 237 if len(f.actions) < i+1 { 238 f.t.Errorf("%d unexpected actions: %+v", len(actions)-len(f.actions), actions[i:]) 239 break 240 } 241 242 expectedAction := f.actions[i] 243 if !(expectedAction.Matches(action.GetVerb(), action.GetResource().Resource) && action.GetSubresource() == expectedAction.GetSubresource()) { 244 f.t.Errorf("Expected\n\t%#v\ngot\n\t%#v", expectedAction, action) 245 continue 246 } 247 } 248 249 if len(f.actions) > len(actions) { 250 f.t.Errorf("%d additional expected actions:%+v", len(f.actions)-len(actions), f.actions[len(actions):]) 251 } 252 } 253 254 func filterInformerActions(actions []core.Action) []core.Action { 255 ret := []core.Action{} 256 for _, action := range actions { 257 if len(action.GetNamespace()) == 0 && 258 (action.Matches("list", "pods") || 259 action.Matches("list", "deployments") || 260 action.Matches("list", "replicasets") || 261 action.Matches("watch", "pods") || 262 action.Matches("watch", "deployments") || 263 action.Matches("watch", "replicasets")) { 264 continue 265 } 266 ret = append(ret, action) 267 } 268 269 return ret 270 } 271 272 func TestSyncDeploymentCreatesReplicaSet(t *testing.T) { 273 _, ctx := ktesting.NewTestContext(t) 274 275 f := newFixture(t) 276 277 d := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 278 f.dLister = append(f.dLister, d) 279 f.objects = append(f.objects, d) 280 281 rs := newReplicaSet(d, "deploymentrs-4186632231", 1) 282 283 f.expectCreateRSAction(rs) 284 f.expectUpdateDeploymentStatusAction(d) 285 f.expectUpdateDeploymentStatusAction(d) 286 287 f.run(ctx, testutil.GetKey(d, t)) 288 } 289 290 func TestSyncDeploymentDontDoAnythingDuringDeletion(t *testing.T) { 291 _, ctx := ktesting.NewTestContext(t) 292 293 f := newFixture(t) 294 295 d := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 296 now := metav1.Now() 297 d.DeletionTimestamp = &now 298 f.dLister = append(f.dLister, d) 299 f.objects = append(f.objects, d) 300 301 f.expectUpdateDeploymentStatusAction(d) 302 f.run(ctx, testutil.GetKey(d, t)) 303 } 304 305 func TestSyncDeploymentDeletionRace(t *testing.T) { 306 _, ctx := ktesting.NewTestContext(t) 307 308 f := newFixture(t) 309 310 d := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 311 d2 := *d 312 // Lister (cache) says NOT deleted. 313 f.dLister = append(f.dLister, d) 314 // Bare client says it IS deleted. This should be presumed more up-to-date. 315 now := metav1.Now() 316 d2.DeletionTimestamp = &now 317 f.objects = append(f.objects, &d2) 318 319 // The recheck is only triggered if a matching orphan exists. 320 rs := newReplicaSet(d, "rs1", 1) 321 rs.OwnerReferences = nil 322 f.objects = append(f.objects, rs) 323 f.rsLister = append(f.rsLister, rs) 324 325 // Expect to only recheck DeletionTimestamp. 326 f.expectGetDeploymentAction(d) 327 // Sync should fail and requeue to let cache catch up. 328 // Don't start informers, since we don't want cache to catch up for this test. 329 f.runExpectError(ctx, testutil.GetKey(d, t), false) 330 } 331 332 // issue: https://github.com/kubernetes/kubernetes/issues/23218 333 func TestDontSyncDeploymentsWithEmptyPodSelector(t *testing.T) { 334 _, ctx := ktesting.NewTestContext(t) 335 ctx, cancel := context.WithCancel(ctx) 336 defer cancel() 337 338 f := newFixture(t) 339 340 d := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 341 d.Spec.Selector = &metav1.LabelSelector{} 342 f.dLister = append(f.dLister, d) 343 f.objects = append(f.objects, d) 344 345 // Normally there should be a status update to sync observedGeneration but the fake 346 // deployment has no generation set so there is no action happening here. 347 f.run(ctx, testutil.GetKey(d, t)) 348 } 349 350 func TestReentrantRollback(t *testing.T) { 351 _, ctx := ktesting.NewTestContext(t) 352 353 f := newFixture(t) 354 355 d := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 356 d.Annotations = map[string]string{util.RevisionAnnotation: "2"} 357 setRollbackTo(d, &extensions.RollbackConfig{Revision: 0}) 358 f.dLister = append(f.dLister, d) 359 360 rs1 := newReplicaSet(d, "deploymentrs-old", 0) 361 rs1.Annotations = map[string]string{util.RevisionAnnotation: "1"} 362 one := int64(1) 363 rs1.Spec.Template.Spec.TerminationGracePeriodSeconds = &one 364 rs1.Spec.Selector.MatchLabels[apps.DefaultDeploymentUniqueLabelKey] = "hash" 365 366 rs2 := newReplicaSet(d, "deploymentrs-new", 1) 367 rs2.Annotations = map[string]string{util.RevisionAnnotation: "2"} 368 rs2.Spec.Selector.MatchLabels[apps.DefaultDeploymentUniqueLabelKey] = "hash" 369 370 f.rsLister = append(f.rsLister, rs1, rs2) 371 f.objects = append(f.objects, d, rs1, rs2) 372 373 // Rollback is done here 374 f.expectUpdateDeploymentAction(d) 375 // Expect no update on replica sets though 376 f.run(ctx, testutil.GetKey(d, t)) 377 } 378 379 // TestPodDeletionEnqueuesRecreateDeployment ensures that the deletion of a pod 380 // will requeue a Recreate deployment iff there is no other pod returned from the 381 // client. 382 func TestPodDeletionEnqueuesRecreateDeployment(t *testing.T) { 383 logger, ctx := ktesting.NewTestContext(t) 384 385 f := newFixture(t) 386 387 foo := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 388 foo.Spec.Strategy.Type = apps.RecreateDeploymentStrategyType 389 rs := newReplicaSet(foo, "foo-1", 1) 390 pod := generatePodFromRS(rs) 391 392 f.dLister = append(f.dLister, foo) 393 f.rsLister = append(f.rsLister, rs) 394 f.objects = append(f.objects, foo, rs) 395 396 c, _, err := f.newController(ctx) 397 if err != nil { 398 t.Fatalf("error creating Deployment controller: %v", err) 399 } 400 enqueued := false 401 c.enqueueDeployment = func(d *apps.Deployment) { 402 if d.Name == "foo" { 403 enqueued = true 404 } 405 } 406 407 c.deletePod(logger, pod) 408 409 if !enqueued { 410 t.Errorf("expected deployment %q to be queued after pod deletion", foo.Name) 411 } 412 } 413 414 // TestPodDeletionDoesntEnqueueRecreateDeployment ensures that the deletion of a pod 415 // will not requeue a Recreate deployment iff there are other pods returned from the 416 // client. 417 func TestPodDeletionDoesntEnqueueRecreateDeployment(t *testing.T) { 418 logger, ctx := ktesting.NewTestContext(t) 419 420 f := newFixture(t) 421 422 foo := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 423 foo.Spec.Strategy.Type = apps.RecreateDeploymentStrategyType 424 rs1 := newReplicaSet(foo, "foo-1", 1) 425 rs2 := newReplicaSet(foo, "foo-1", 1) 426 pod1 := generatePodFromRS(rs1) 427 pod2 := generatePodFromRS(rs2) 428 429 f.dLister = append(f.dLister, foo) 430 // Let's pretend this is a different pod. The gist is that the pod lister needs to 431 // return a non-empty list. 432 f.podLister = append(f.podLister, pod1, pod2) 433 434 c, _, err := f.newController(ctx) 435 if err != nil { 436 t.Fatalf("error creating Deployment controller: %v", err) 437 } 438 enqueued := false 439 c.enqueueDeployment = func(d *apps.Deployment) { 440 if d.Name == "foo" { 441 enqueued = true 442 } 443 } 444 445 c.deletePod(logger, pod1) 446 447 if enqueued { 448 t.Errorf("expected deployment %q not to be queued after pod deletion", foo.Name) 449 } 450 } 451 452 // TestPodDeletionPartialReplicaSetOwnershipEnqueueRecreateDeployment ensures that 453 // the deletion of a pod will requeue a Recreate deployment iff there is no other 454 // pod returned from the client in the case where a deployment has multiple replica 455 // sets, some of which have empty owner references. 456 func TestPodDeletionPartialReplicaSetOwnershipEnqueueRecreateDeployment(t *testing.T) { 457 logger, ctx := ktesting.NewTestContext(t) 458 459 f := newFixture(t) 460 461 foo := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 462 foo.Spec.Strategy.Type = apps.RecreateDeploymentStrategyType 463 rs1 := newReplicaSet(foo, "foo-1", 1) 464 rs2 := newReplicaSet(foo, "foo-2", 2) 465 rs2.OwnerReferences = nil 466 pod := generatePodFromRS(rs1) 467 468 f.dLister = append(f.dLister, foo) 469 f.rsLister = append(f.rsLister, rs1, rs2) 470 f.objects = append(f.objects, foo, rs1, rs2) 471 472 c, _, err := f.newController(ctx) 473 if err != nil { 474 t.Fatalf("error creating Deployment controller: %v", err) 475 } 476 enqueued := false 477 c.enqueueDeployment = func(d *apps.Deployment) { 478 if d.Name == "foo" { 479 enqueued = true 480 } 481 } 482 483 c.deletePod(logger, pod) 484 485 if !enqueued { 486 t.Errorf("expected deployment %q to be queued after pod deletion", foo.Name) 487 } 488 } 489 490 // TestPodDeletionPartialReplicaSetOwnershipDoesntEnqueueRecreateDeployment that the 491 // deletion of a pod will not requeue a Recreate deployment iff there are other pods 492 // returned from the client in the case where a deployment has multiple replica sets, 493 // some of which have empty owner references. 494 func TestPodDeletionPartialReplicaSetOwnershipDoesntEnqueueRecreateDeployment(t *testing.T) { 495 logger, ctx := ktesting.NewTestContext(t) 496 497 f := newFixture(t) 498 499 foo := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 500 foo.Spec.Strategy.Type = apps.RecreateDeploymentStrategyType 501 rs1 := newReplicaSet(foo, "foo-1", 1) 502 rs2 := newReplicaSet(foo, "foo-2", 2) 503 rs2.OwnerReferences = nil 504 pod := generatePodFromRS(rs1) 505 506 f.dLister = append(f.dLister, foo) 507 f.rsLister = append(f.rsLister, rs1, rs2) 508 f.objects = append(f.objects, foo, rs1, rs2) 509 // Let's pretend this is a different pod. The gist is that the pod lister needs to 510 // return a non-empty list. 511 f.podLister = append(f.podLister, pod) 512 513 c, _, err := f.newController(ctx) 514 if err != nil { 515 t.Fatalf("error creating Deployment controller: %v", err) 516 } 517 enqueued := false 518 c.enqueueDeployment = func(d *apps.Deployment) { 519 if d.Name == "foo" { 520 enqueued = true 521 } 522 } 523 524 c.deletePod(logger, pod) 525 526 if enqueued { 527 t.Errorf("expected deployment %q not to be queued after pod deletion", foo.Name) 528 } 529 } 530 531 func TestGetReplicaSetsForDeployment(t *testing.T) { 532 _, ctx := ktesting.NewTestContext(t) 533 534 f := newFixture(t) 535 536 // Two Deployments with same labels. 537 d1 := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 538 d2 := newDeployment("bar", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 539 540 // Two ReplicaSets that match labels for both Deployments, 541 // but have ControllerRefs to make ownership explicit. 542 rs1 := newReplicaSet(d1, "rs1", 1) 543 rs2 := newReplicaSet(d2, "rs2", 1) 544 545 f.dLister = append(f.dLister, d1, d2) 546 f.rsLister = append(f.rsLister, rs1, rs2) 547 f.objects = append(f.objects, d1, d2, rs1, rs2) 548 549 // Start the fixture. 550 c, informers, err := f.newController(ctx) 551 if err != nil { 552 t.Fatalf("error creating Deployment controller: %v", err) 553 } 554 stopCh := make(chan struct{}) 555 defer close(stopCh) 556 informers.Start(stopCh) 557 558 rsList, err := c.getReplicaSetsForDeployment(ctx, d1) 559 if err != nil { 560 t.Fatalf("getReplicaSetsForDeployment() error: %v", err) 561 } 562 rsNames := []string{} 563 for _, rs := range rsList { 564 rsNames = append(rsNames, rs.Name) 565 } 566 if len(rsNames) != 1 || rsNames[0] != rs1.Name { 567 t.Errorf("getReplicaSetsForDeployment() = %v, want [%v]", rsNames, rs1.Name) 568 } 569 570 rsList, err = c.getReplicaSetsForDeployment(ctx, d2) 571 if err != nil { 572 t.Fatalf("getReplicaSetsForDeployment() error: %v", err) 573 } 574 rsNames = []string{} 575 for _, rs := range rsList { 576 rsNames = append(rsNames, rs.Name) 577 } 578 if len(rsNames) != 1 || rsNames[0] != rs2.Name { 579 t.Errorf("getReplicaSetsForDeployment() = %v, want [%v]", rsNames, rs2.Name) 580 } 581 } 582 583 func TestGetReplicaSetsForDeploymentAdoptRelease(t *testing.T) { 584 _, ctx := ktesting.NewTestContext(t) 585 586 f := newFixture(t) 587 588 d := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 589 590 // RS with matching labels, but orphaned. Should be adopted and returned. 591 rsAdopt := newReplicaSet(d, "rsAdopt", 1) 592 rsAdopt.OwnerReferences = nil 593 // RS with matching ControllerRef, but wrong labels. Should be released. 594 rsRelease := newReplicaSet(d, "rsRelease", 1) 595 rsRelease.Labels = map[string]string{"foo": "notbar"} 596 597 f.dLister = append(f.dLister, d) 598 f.rsLister = append(f.rsLister, rsAdopt, rsRelease) 599 f.objects = append(f.objects, d, rsAdopt, rsRelease) 600 601 // Start the fixture. 602 c, informers, err := f.newController(ctx) 603 if err != nil { 604 t.Fatalf("error creating Deployment controller: %v", err) 605 } 606 stopCh := make(chan struct{}) 607 defer close(stopCh) 608 informers.Start(stopCh) 609 610 rsList, err := c.getReplicaSetsForDeployment(ctx, d) 611 if err != nil { 612 t.Fatalf("getReplicaSetsForDeployment() error: %v", err) 613 } 614 rsNames := []string{} 615 for _, rs := range rsList { 616 rsNames = append(rsNames, rs.Name) 617 } 618 if len(rsNames) != 1 || rsNames[0] != rsAdopt.Name { 619 t.Errorf("getReplicaSetsForDeployment() = %v, want [%v]", rsNames, rsAdopt.Name) 620 } 621 } 622 623 func TestGetPodMapForReplicaSets(t *testing.T) { 624 _, ctx := ktesting.NewTestContext(t) 625 626 f := newFixture(t) 627 628 d := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 629 630 rs1 := newReplicaSet(d, "rs1", 1) 631 rs2 := newReplicaSet(d, "rs2", 1) 632 633 // Add a Pod for each ReplicaSet. 634 pod1 := generatePodFromRS(rs1) 635 pod2 := generatePodFromRS(rs2) 636 // Add a Pod that has matching labels, but no ControllerRef. 637 pod3 := generatePodFromRS(rs1) 638 pod3.Name = "pod3" 639 pod3.OwnerReferences = nil 640 // Add a Pod that has matching labels and ControllerRef, but is inactive. 641 pod4 := generatePodFromRS(rs1) 642 pod4.Name = "pod4" 643 pod4.Status.Phase = v1.PodFailed 644 645 f.dLister = append(f.dLister, d) 646 f.rsLister = append(f.rsLister, rs1, rs2) 647 f.podLister = append(f.podLister, pod1, pod2, pod3, pod4) 648 f.objects = append(f.objects, d, rs1, rs2, pod1, pod2, pod3, pod4) 649 650 // Start the fixture. 651 c, informers, err := f.newController(ctx) 652 if err != nil { 653 t.Fatalf("error creating Deployment controller: %v", err) 654 } 655 stopCh := make(chan struct{}) 656 defer close(stopCh) 657 informers.Start(stopCh) 658 659 podMap, err := c.getPodMapForDeployment(d, f.rsLister) 660 if err != nil { 661 t.Fatalf("getPodMapForDeployment() error: %v", err) 662 } 663 podCount := 0 664 for _, podList := range podMap { 665 podCount += len(podList) 666 } 667 if got, want := podCount, 3; got != want { 668 t.Errorf("podCount = %v, want %v", got, want) 669 } 670 671 if got, want := len(podMap), 2; got != want { 672 t.Errorf("len(podMap) = %v, want %v", got, want) 673 } 674 if got, want := len(podMap[rs1.UID]), 2; got != want { 675 t.Errorf("len(podMap[rs1]) = %v, want %v", got, want) 676 } 677 expect := map[string]struct{}{"rs1-pod": {}, "pod4": {}} 678 for _, pod := range podMap[rs1.UID] { 679 if _, ok := expect[pod.Name]; !ok { 680 t.Errorf("unexpected pod name for rs1: %s", pod.Name) 681 } 682 } 683 if got, want := len(podMap[rs2.UID]), 1; got != want { 684 t.Errorf("len(podMap[rs2]) = %v, want %v", got, want) 685 } 686 if got, want := podMap[rs2.UID][0].Name, "rs2-pod"; got != want { 687 t.Errorf("podMap[rs2] = [%v], want [%v]", got, want) 688 } 689 } 690 691 func TestAddReplicaSet(t *testing.T) { 692 logger, ctx := ktesting.NewTestContext(t) 693 694 f := newFixture(t) 695 696 d1 := newDeployment("d1", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 697 d2 := newDeployment("d2", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 698 699 // Two ReplicaSets that match labels for both Deployments, 700 // but have ControllerRefs to make ownership explicit. 701 rs1 := newReplicaSet(d1, "rs1", 1) 702 rs2 := newReplicaSet(d2, "rs2", 1) 703 704 f.dLister = append(f.dLister, d1, d2) 705 f.objects = append(f.objects, d1, d2, rs1, rs2) 706 707 // Create the fixture but don't start it, 708 // so nothing happens in the background. 709 dc, _, err := f.newController(ctx) 710 if err != nil { 711 t.Fatalf("error creating Deployment controller: %v", err) 712 } 713 714 dc.addReplicaSet(klog.FromContext(ctx), rs1) 715 if got, want := dc.queue.Len(), 1; got != want { 716 t.Fatalf("queue.Len() = %v, want %v", got, want) 717 } 718 key, done := dc.queue.Get() 719 if key == nil || done { 720 t.Fatalf("failed to enqueue controller for rs %v", rs1.Name) 721 } 722 expectedKey, _ := controller.KeyFunc(d1) 723 if got, want := key.(string), expectedKey; got != want { 724 t.Errorf("queue.Get() = %v, want %v", got, want) 725 } 726 727 dc.addReplicaSet(logger, rs2) 728 if got, want := dc.queue.Len(), 1; got != want { 729 t.Fatalf("queue.Len() = %v, want %v", got, want) 730 } 731 key, done = dc.queue.Get() 732 if key == nil || done { 733 t.Fatalf("failed to enqueue controller for rs %v", rs2.Name) 734 } 735 expectedKey, _ = controller.KeyFunc(d2) 736 if got, want := key.(string), expectedKey; got != want { 737 t.Errorf("queue.Get() = %v, want %v", got, want) 738 } 739 } 740 741 func TestAddReplicaSetOrphan(t *testing.T) { 742 logger, ctx := ktesting.NewTestContext(t) 743 744 f := newFixture(t) 745 746 // 2 will match the RS, 1 won't. 747 d1 := newDeployment("d1", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 748 d2 := newDeployment("d2", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 749 d3 := newDeployment("d3", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 750 d3.Spec.Selector.MatchLabels = map[string]string{"foo": "notbar"} 751 752 // Make the RS an orphan. Expect matching Deployments to be queued. 753 rs := newReplicaSet(d1, "rs1", 1) 754 rs.OwnerReferences = nil 755 756 f.dLister = append(f.dLister, d1, d2, d3) 757 f.objects = append(f.objects, d1, d2, d3) 758 759 // Create the fixture but don't start it, 760 // so nothing happens in the background. 761 dc, _, err := f.newController(ctx) 762 if err != nil { 763 t.Fatalf("error creating Deployment controller: %v", err) 764 } 765 766 dc.addReplicaSet(logger, rs) 767 if got, want := dc.queue.Len(), 2; got != want { 768 t.Fatalf("queue.Len() = %v, want %v", got, want) 769 } 770 } 771 772 func TestUpdateReplicaSet(t *testing.T) { 773 logger, ctx := ktesting.NewTestContext(t) 774 775 f := newFixture(t) 776 777 d1 := newDeployment("d1", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 778 d2 := newDeployment("d2", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 779 780 // Two ReplicaSets that match labels for both Deployments, 781 // but have ControllerRefs to make ownership explicit. 782 rs1 := newReplicaSet(d1, "rs1", 1) 783 rs2 := newReplicaSet(d2, "rs2", 1) 784 785 f.dLister = append(f.dLister, d1, d2) 786 f.rsLister = append(f.rsLister, rs1, rs2) 787 f.objects = append(f.objects, d1, d2, rs1, rs2) 788 789 // Create the fixture but don't start it, 790 // so nothing happens in the background. 791 dc, _, err := f.newController(ctx) 792 if err != nil { 793 t.Fatalf("error creating Deployment controller: %v", err) 794 } 795 796 prev := *rs1 797 next := *rs1 798 bumpResourceVersion(&next) 799 dc.updateReplicaSet(logger, &prev, &next) 800 if got, want := dc.queue.Len(), 1; got != want { 801 t.Fatalf("queue.Len() = %v, want %v", got, want) 802 } 803 key, done := dc.queue.Get() 804 if key == nil || done { 805 t.Fatalf("failed to enqueue controller for rs %v", rs1.Name) 806 } 807 expectedKey, _ := controller.KeyFunc(d1) 808 if got, want := key.(string), expectedKey; got != want { 809 t.Errorf("queue.Get() = %v, want %v", got, want) 810 } 811 812 prev = *rs2 813 next = *rs2 814 bumpResourceVersion(&next) 815 dc.updateReplicaSet(logger, &prev, &next) 816 if got, want := dc.queue.Len(), 1; got != want { 817 t.Fatalf("queue.Len() = %v, want %v", got, want) 818 } 819 key, done = dc.queue.Get() 820 if key == nil || done { 821 t.Fatalf("failed to enqueue controller for rs %v", rs2.Name) 822 } 823 expectedKey, _ = controller.KeyFunc(d2) 824 if got, want := key.(string), expectedKey; got != want { 825 t.Errorf("queue.Get() = %v, want %v", got, want) 826 } 827 } 828 829 func TestUpdateReplicaSetOrphanWithNewLabels(t *testing.T) { 830 logger, ctx := ktesting.NewTestContext(t) 831 832 f := newFixture(t) 833 834 d1 := newDeployment("d1", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 835 d2 := newDeployment("d2", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 836 837 // RS matches both, but is an orphan. 838 rs := newReplicaSet(d1, "rs1", 1) 839 rs.OwnerReferences = nil 840 841 f.dLister = append(f.dLister, d1, d2) 842 f.rsLister = append(f.rsLister, rs) 843 f.objects = append(f.objects, d1, d2, rs) 844 845 // Create the fixture but don't start it, 846 // so nothing happens in the background. 847 dc, _, err := f.newController(ctx) 848 if err != nil { 849 t.Fatalf("error creating Deployment controller: %v", err) 850 } 851 852 // Change labels and expect all matching controllers to queue. 853 prev := *rs 854 prev.Labels = map[string]string{"foo": "notbar"} 855 next := *rs 856 bumpResourceVersion(&next) 857 dc.updateReplicaSet(logger, &prev, &next) 858 if got, want := dc.queue.Len(), 2; got != want { 859 t.Fatalf("queue.Len() = %v, want %v", got, want) 860 } 861 } 862 863 func TestUpdateReplicaSetChangeControllerRef(t *testing.T) { 864 logger, ctx := ktesting.NewTestContext(t) 865 866 f := newFixture(t) 867 868 d1 := newDeployment("d1", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 869 d2 := newDeployment("d2", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 870 871 rs := newReplicaSet(d1, "rs1", 1) 872 873 f.dLister = append(f.dLister, d1, d2) 874 f.rsLister = append(f.rsLister, rs) 875 f.objects = append(f.objects, d1, d2, rs) 876 877 // Create the fixture but don't start it, 878 // so nothing happens in the background. 879 dc, _, err := f.newController(ctx) 880 if err != nil { 881 t.Fatalf("error creating Deployment controller: %v", err) 882 } 883 884 // Change ControllerRef and expect both old and new to queue. 885 prev := *rs 886 prev.OwnerReferences = []metav1.OwnerReference{*metav1.NewControllerRef(d2, controllerKind)} 887 next := *rs 888 bumpResourceVersion(&next) 889 dc.updateReplicaSet(logger, &prev, &next) 890 if got, want := dc.queue.Len(), 2; got != want { 891 t.Fatalf("queue.Len() = %v, want %v", got, want) 892 } 893 } 894 895 func TestUpdateReplicaSetRelease(t *testing.T) { 896 logger, ctx := ktesting.NewTestContext(t) 897 898 f := newFixture(t) 899 900 d1 := newDeployment("d1", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 901 d2 := newDeployment("d2", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 902 903 rs := newReplicaSet(d1, "rs1", 1) 904 905 f.dLister = append(f.dLister, d1, d2) 906 f.rsLister = append(f.rsLister, rs) 907 f.objects = append(f.objects, d1, d2, rs) 908 909 // Create the fixture but don't start it, 910 // so nothing happens in the background. 911 dc, _, err := f.newController(ctx) 912 if err != nil { 913 t.Fatalf("error creating Deployment controller: %v", err) 914 } 915 916 // Remove ControllerRef and expect all matching controller to sync orphan. 917 prev := *rs 918 next := *rs 919 next.OwnerReferences = nil 920 bumpResourceVersion(&next) 921 dc.updateReplicaSet(logger, &prev, &next) 922 if got, want := dc.queue.Len(), 2; got != want { 923 t.Fatalf("queue.Len() = %v, want %v", got, want) 924 } 925 } 926 927 func TestDeleteReplicaSet(t *testing.T) { 928 logger, ctx := ktesting.NewTestContext(t) 929 930 f := newFixture(t) 931 932 d1 := newDeployment("d1", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 933 d2 := newDeployment("d2", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 934 935 // Two ReplicaSets that match labels for both Deployments, 936 // but have ControllerRefs to make ownership explicit. 937 rs1 := newReplicaSet(d1, "rs1", 1) 938 rs2 := newReplicaSet(d2, "rs2", 1) 939 940 f.dLister = append(f.dLister, d1, d2) 941 f.rsLister = append(f.rsLister, rs1, rs2) 942 f.objects = append(f.objects, d1, d2, rs1, rs2) 943 944 // Create the fixture but don't start it, 945 // so nothing happens in the background. 946 dc, _, err := f.newController(ctx) 947 if err != nil { 948 t.Fatalf("error creating Deployment controller: %v", err) 949 } 950 951 dc.deleteReplicaSet(logger, rs1) 952 if got, want := dc.queue.Len(), 1; got != want { 953 t.Fatalf("queue.Len() = %v, want %v", got, want) 954 } 955 key, done := dc.queue.Get() 956 if key == nil || done { 957 t.Fatalf("failed to enqueue controller for rs %v", rs1.Name) 958 } 959 expectedKey, _ := controller.KeyFunc(d1) 960 if got, want := key.(string), expectedKey; got != want { 961 t.Errorf("queue.Get() = %v, want %v", got, want) 962 } 963 964 dc.deleteReplicaSet(logger, rs2) 965 if got, want := dc.queue.Len(), 1; got != want { 966 t.Fatalf("queue.Len() = %v, want %v", got, want) 967 } 968 key, done = dc.queue.Get() 969 if key == nil || done { 970 t.Fatalf("failed to enqueue controller for rs %v", rs2.Name) 971 } 972 expectedKey, _ = controller.KeyFunc(d2) 973 if got, want := key.(string), expectedKey; got != want { 974 t.Errorf("queue.Get() = %v, want %v", got, want) 975 } 976 } 977 978 func TestDeleteReplicaSetOrphan(t *testing.T) { 979 logger, ctx := ktesting.NewTestContext(t) 980 981 f := newFixture(t) 982 983 d1 := newDeployment("d1", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 984 d2 := newDeployment("d2", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 985 986 // Make the RS an orphan. Expect matching Deployments to be queued. 987 rs := newReplicaSet(d1, "rs1", 1) 988 rs.OwnerReferences = nil 989 990 f.dLister = append(f.dLister, d1, d2) 991 f.rsLister = append(f.rsLister, rs) 992 f.objects = append(f.objects, d1, d2, rs) 993 994 // Create the fixture but don't start it, 995 // so nothing happens in the background. 996 dc, _, err := f.newController(ctx) 997 if err != nil { 998 t.Fatalf("error creating Deployment controller: %v", err) 999 } 1000 1001 dc.deleteReplicaSet(logger, rs) 1002 if got, want := dc.queue.Len(), 0; got != want { 1003 t.Fatalf("queue.Len() = %v, want %v", got, want) 1004 } 1005 } 1006 1007 func BenchmarkGetPodMapForDeployment(b *testing.B) { 1008 _, ctx := ktesting.NewTestContext(b) 1009 1010 f := newFixture(b) 1011 1012 d := newDeployment("foo", 1, nil, nil, nil, map[string]string{"foo": "bar"}) 1013 1014 rs1 := newReplicaSet(d, "rs1", 1) 1015 rs2 := newReplicaSet(d, "rs2", 1) 1016 1017 var pods []*v1.Pod 1018 var objects []runtime.Object 1019 for i := 0; i < 100; i++ { 1020 p1, p2 := generatePodFromRS(rs1), generatePodFromRS(rs2) 1021 p1.Name, p2.Name = p1.Name+fmt.Sprintf("-%d", i), p2.Name+fmt.Sprintf("-%d", i) 1022 pods = append(pods, p1, p2) 1023 objects = append(objects, p1, p2) 1024 } 1025 1026 f.dLister = append(f.dLister, d) 1027 f.rsLister = append(f.rsLister, rs1, rs2) 1028 f.podLister = append(f.podLister, pods...) 1029 f.objects = append(f.objects, d, rs1, rs2) 1030 f.objects = append(f.objects, objects...) 1031 1032 // Start the fixture. 1033 c, informers, err := f.newController(ctx) 1034 if err != nil { 1035 b.Fatalf("error creating Deployment controller: %v", err) 1036 } 1037 stopCh := make(chan struct{}) 1038 defer close(stopCh) 1039 informers.Start(stopCh) 1040 1041 b.ReportAllocs() 1042 b.ResetTimer() 1043 for n := 0; n < b.N; n++ { 1044 m, err := c.getPodMapForDeployment(d, f.rsLister) 1045 if err != nil { 1046 b.Fatalf("getPodMapForDeployment() error: %v", err) 1047 } 1048 if len(m) != 2 { 1049 b.Errorf("Invalid map size, expected 2, got: %d", len(m)) 1050 } 1051 } 1052 } 1053 1054 func bumpResourceVersion(obj metav1.Object) { 1055 ver, _ := strconv.ParseInt(obj.GetResourceVersion(), 10, 32) 1056 obj.SetResourceVersion(strconv.FormatInt(ver+1, 10)) 1057 } 1058 1059 // generatePodFromRS creates a pod, with the input ReplicaSet's selector and its template 1060 func generatePodFromRS(rs *apps.ReplicaSet) *v1.Pod { 1061 trueVar := true 1062 return &v1.Pod{ 1063 ObjectMeta: metav1.ObjectMeta{ 1064 Name: rs.Name + "-pod", 1065 Namespace: rs.Namespace, 1066 Labels: rs.Spec.Selector.MatchLabels, 1067 OwnerReferences: []metav1.OwnerReference{ 1068 {UID: rs.UID, APIVersion: "v1beta1", Kind: "ReplicaSet", Name: rs.Name, Controller: &trueVar}, 1069 }, 1070 }, 1071 Spec: rs.Spec.Template.Spec, 1072 } 1073 }