k8s.io/kubernetes@v1.29.3/pkg/controller/deployment/sync_test.go (about) 1 /* 2 Copyright 2016 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 "math" 21 "testing" 22 "time" 23 24 apps "k8s.io/api/apps/v1" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/util/intstr" 27 "k8s.io/apimachinery/pkg/util/sets" 28 "k8s.io/client-go/informers" 29 "k8s.io/client-go/kubernetes/fake" 30 testclient "k8s.io/client-go/testing" 31 "k8s.io/client-go/tools/record" 32 "k8s.io/klog/v2/ktesting" 33 "k8s.io/kubernetes/pkg/controller" 34 deploymentutil "k8s.io/kubernetes/pkg/controller/deployment/util" 35 "k8s.io/utils/ptr" 36 ) 37 38 func TestScale(t *testing.T) { 39 newTimestamp := metav1.Date(2016, 5, 20, 2, 0, 0, 0, time.UTC) 40 oldTimestamp := metav1.Date(2016, 5, 20, 1, 0, 0, 0, time.UTC) 41 olderTimestamp := metav1.Date(2016, 5, 20, 0, 0, 0, 0, time.UTC) 42 43 var updatedTemplate = func(replicas int32) *apps.Deployment { 44 d := newDeployment("foo", replicas, nil, nil, nil, map[string]string{"foo": "bar"}) 45 d.Spec.Template.Labels["another"] = "label" 46 return d 47 } 48 49 tests := []struct { 50 name string 51 deployment *apps.Deployment 52 oldDeployment *apps.Deployment 53 54 newRS *apps.ReplicaSet 55 oldRSs []*apps.ReplicaSet 56 57 expectedNew *apps.ReplicaSet 58 expectedOld []*apps.ReplicaSet 59 wasntUpdated map[string]bool 60 61 desiredReplicasAnnotations map[string]int32 62 }{ 63 { 64 name: "normal scaling event: 10 -> 12", 65 deployment: newDeployment("foo", 12, nil, nil, nil, nil), 66 oldDeployment: newDeployment("foo", 10, nil, nil, nil, nil), 67 68 newRS: rs("foo-v1", 10, nil, newTimestamp), 69 oldRSs: []*apps.ReplicaSet{}, 70 71 expectedNew: rs("foo-v1", 12, nil, newTimestamp), 72 expectedOld: []*apps.ReplicaSet{}, 73 }, 74 { 75 name: "normal scaling event: 10 -> 5", 76 deployment: newDeployment("foo", 5, nil, nil, nil, nil), 77 oldDeployment: newDeployment("foo", 10, nil, nil, nil, nil), 78 79 newRS: rs("foo-v1", 10, nil, newTimestamp), 80 oldRSs: []*apps.ReplicaSet{}, 81 82 expectedNew: rs("foo-v1", 5, nil, newTimestamp), 83 expectedOld: []*apps.ReplicaSet{}, 84 }, 85 { 86 name: "proportional scaling: 5 -> 10", 87 deployment: newDeployment("foo", 10, nil, nil, nil, nil), 88 oldDeployment: newDeployment("foo", 5, nil, nil, nil, nil), 89 90 newRS: rs("foo-v2", 2, nil, newTimestamp), 91 oldRSs: []*apps.ReplicaSet{rs("foo-v1", 3, nil, oldTimestamp)}, 92 93 expectedNew: rs("foo-v2", 4, nil, newTimestamp), 94 expectedOld: []*apps.ReplicaSet{rs("foo-v1", 6, nil, oldTimestamp)}, 95 }, 96 { 97 name: "proportional scaling: 5 -> 3", 98 deployment: newDeployment("foo", 3, nil, nil, nil, nil), 99 oldDeployment: newDeployment("foo", 5, nil, nil, nil, nil), 100 101 newRS: rs("foo-v2", 2, nil, newTimestamp), 102 oldRSs: []*apps.ReplicaSet{rs("foo-v1", 3, nil, oldTimestamp)}, 103 104 expectedNew: rs("foo-v2", 1, nil, newTimestamp), 105 expectedOld: []*apps.ReplicaSet{rs("foo-v1", 2, nil, oldTimestamp)}, 106 }, 107 { 108 name: "proportional scaling: 9 -> 4", 109 deployment: newDeployment("foo", 4, nil, nil, nil, nil), 110 oldDeployment: newDeployment("foo", 9, nil, nil, nil, nil), 111 112 newRS: rs("foo-v2", 8, nil, newTimestamp), 113 oldRSs: []*apps.ReplicaSet{rs("foo-v1", 1, nil, oldTimestamp)}, 114 115 expectedNew: rs("foo-v2", 4, nil, newTimestamp), 116 expectedOld: []*apps.ReplicaSet{rs("foo-v1", 0, nil, oldTimestamp)}, 117 }, 118 { 119 name: "proportional scaling: 7 -> 10", 120 deployment: newDeployment("foo", 10, nil, nil, nil, nil), 121 oldDeployment: newDeployment("foo", 7, nil, nil, nil, nil), 122 123 newRS: rs("foo-v3", 2, nil, newTimestamp), 124 oldRSs: []*apps.ReplicaSet{rs("foo-v2", 3, nil, oldTimestamp), rs("foo-v1", 2, nil, olderTimestamp)}, 125 126 expectedNew: rs("foo-v3", 3, nil, newTimestamp), 127 expectedOld: []*apps.ReplicaSet{rs("foo-v2", 4, nil, oldTimestamp), rs("foo-v1", 3, nil, olderTimestamp)}, 128 }, 129 { 130 name: "proportional scaling: 13 -> 8", 131 deployment: newDeployment("foo", 8, nil, nil, nil, nil), 132 oldDeployment: newDeployment("foo", 13, nil, nil, nil, nil), 133 134 newRS: rs("foo-v3", 2, nil, newTimestamp), 135 oldRSs: []*apps.ReplicaSet{rs("foo-v2", 8, nil, oldTimestamp), rs("foo-v1", 3, nil, olderTimestamp)}, 136 137 expectedNew: rs("foo-v3", 1, nil, newTimestamp), 138 expectedOld: []*apps.ReplicaSet{rs("foo-v2", 5, nil, oldTimestamp), rs("foo-v1", 2, nil, olderTimestamp)}, 139 }, 140 // Scales up the new replica set. 141 { 142 name: "leftover distribution: 3 -> 4", 143 deployment: newDeployment("foo", 4, nil, nil, nil, nil), 144 oldDeployment: newDeployment("foo", 3, nil, nil, nil, nil), 145 146 newRS: rs("foo-v3", 1, nil, newTimestamp), 147 oldRSs: []*apps.ReplicaSet{rs("foo-v2", 1, nil, oldTimestamp), rs("foo-v1", 1, nil, olderTimestamp)}, 148 149 expectedNew: rs("foo-v3", 2, nil, newTimestamp), 150 expectedOld: []*apps.ReplicaSet{rs("foo-v2", 1, nil, oldTimestamp), rs("foo-v1", 1, nil, olderTimestamp)}, 151 }, 152 // Scales down the older replica set. 153 { 154 name: "leftover distribution: 3 -> 2", 155 deployment: newDeployment("foo", 2, nil, nil, nil, nil), 156 oldDeployment: newDeployment("foo", 3, nil, nil, nil, nil), 157 158 newRS: rs("foo-v3", 1, nil, newTimestamp), 159 oldRSs: []*apps.ReplicaSet{rs("foo-v2", 1, nil, oldTimestamp), rs("foo-v1", 1, nil, olderTimestamp)}, 160 161 expectedNew: rs("foo-v3", 1, nil, newTimestamp), 162 expectedOld: []*apps.ReplicaSet{rs("foo-v2", 1, nil, oldTimestamp), rs("foo-v1", 0, nil, olderTimestamp)}, 163 }, 164 // Scales up the latest replica set first. 165 { 166 name: "proportional scaling (no new rs): 4 -> 5", 167 deployment: newDeployment("foo", 5, nil, nil, nil, nil), 168 oldDeployment: newDeployment("foo", 4, nil, nil, nil, nil), 169 170 newRS: nil, 171 oldRSs: []*apps.ReplicaSet{rs("foo-v2", 2, nil, oldTimestamp), rs("foo-v1", 2, nil, olderTimestamp)}, 172 173 expectedNew: nil, 174 expectedOld: []*apps.ReplicaSet{rs("foo-v2", 3, nil, oldTimestamp), rs("foo-v1", 2, nil, olderTimestamp)}, 175 }, 176 // Scales down to zero 177 { 178 name: "proportional scaling: 6 -> 0", 179 deployment: newDeployment("foo", 0, nil, nil, nil, nil), 180 oldDeployment: newDeployment("foo", 6, nil, nil, nil, nil), 181 182 newRS: rs("foo-v3", 3, nil, newTimestamp), 183 oldRSs: []*apps.ReplicaSet{rs("foo-v2", 2, nil, oldTimestamp), rs("foo-v1", 1, nil, olderTimestamp)}, 184 185 expectedNew: rs("foo-v3", 0, nil, newTimestamp), 186 expectedOld: []*apps.ReplicaSet{rs("foo-v2", 0, nil, oldTimestamp), rs("foo-v1", 0, nil, olderTimestamp)}, 187 }, 188 // Scales up from zero 189 { 190 name: "proportional scaling: 0 -> 6", 191 deployment: newDeployment("foo", 6, nil, nil, nil, nil), 192 oldDeployment: newDeployment("foo", 6, nil, nil, nil, nil), 193 194 newRS: rs("foo-v3", 0, nil, newTimestamp), 195 oldRSs: []*apps.ReplicaSet{rs("foo-v2", 0, nil, oldTimestamp), rs("foo-v1", 0, nil, olderTimestamp)}, 196 197 expectedNew: rs("foo-v3", 6, nil, newTimestamp), 198 expectedOld: []*apps.ReplicaSet{rs("foo-v2", 0, nil, oldTimestamp), rs("foo-v1", 0, nil, olderTimestamp)}, 199 wasntUpdated: map[string]bool{"foo-v2": true, "foo-v1": true}, 200 }, 201 // Scenario: deployment.spec.replicas == 3 ( foo-v1.spec.replicas == foo-v2.spec.replicas == foo-v3.spec.replicas == 1 ) 202 // Deployment is scaled to 5. foo-v3.spec.replicas and foo-v2.spec.replicas should increment by 1 but foo-v2 fails to 203 // update. 204 { 205 name: "failed rs update", 206 deployment: newDeployment("foo", 5, nil, nil, nil, nil), 207 oldDeployment: newDeployment("foo", 5, nil, nil, nil, nil), 208 209 newRS: rs("foo-v3", 2, nil, newTimestamp), 210 oldRSs: []*apps.ReplicaSet{rs("foo-v2", 1, nil, oldTimestamp), rs("foo-v1", 1, nil, olderTimestamp)}, 211 212 expectedNew: rs("foo-v3", 2, nil, newTimestamp), 213 expectedOld: []*apps.ReplicaSet{rs("foo-v2", 2, nil, oldTimestamp), rs("foo-v1", 1, nil, olderTimestamp)}, 214 wasntUpdated: map[string]bool{"foo-v3": true, "foo-v1": true}, 215 216 desiredReplicasAnnotations: map[string]int32{"foo-v2": int32(3)}, 217 }, 218 { 219 name: "deployment with surge pods", 220 deployment: newDeployment("foo", 20, nil, ptr.To(intstr.FromInt32(2)), nil, nil), 221 oldDeployment: newDeployment("foo", 10, nil, ptr.To(intstr.FromInt32(2)), nil, nil), 222 223 newRS: rs("foo-v2", 6, nil, newTimestamp), 224 oldRSs: []*apps.ReplicaSet{rs("foo-v1", 6, nil, oldTimestamp)}, 225 226 expectedNew: rs("foo-v2", 11, nil, newTimestamp), 227 expectedOld: []*apps.ReplicaSet{rs("foo-v1", 11, nil, oldTimestamp)}, 228 }, 229 { 230 name: "change both surge and size", 231 deployment: newDeployment("foo", 50, nil, ptr.To(intstr.FromInt32(6)), nil, nil), 232 oldDeployment: newDeployment("foo", 10, nil, ptr.To(intstr.FromInt32(3)), nil, nil), 233 234 newRS: rs("foo-v2", 5, nil, newTimestamp), 235 oldRSs: []*apps.ReplicaSet{rs("foo-v1", 8, nil, oldTimestamp)}, 236 237 expectedNew: rs("foo-v2", 22, nil, newTimestamp), 238 expectedOld: []*apps.ReplicaSet{rs("foo-v1", 34, nil, oldTimestamp)}, 239 }, 240 { 241 name: "change both size and template", 242 deployment: updatedTemplate(14), 243 oldDeployment: newDeployment("foo", 10, nil, nil, nil, map[string]string{"foo": "bar"}), 244 245 newRS: nil, 246 oldRSs: []*apps.ReplicaSet{rs("foo-v2", 7, nil, newTimestamp), rs("foo-v1", 3, nil, oldTimestamp)}, 247 248 expectedNew: nil, 249 expectedOld: []*apps.ReplicaSet{rs("foo-v2", 10, nil, newTimestamp), rs("foo-v1", 4, nil, oldTimestamp)}, 250 }, 251 { 252 name: "saturated but broken new replica set does not affect old pods", 253 deployment: newDeployment("foo", 2, nil, ptr.To(intstr.FromInt32(1)), ptr.To(intstr.FromInt32(1)), nil), 254 oldDeployment: newDeployment("foo", 2, nil, ptr.To(intstr.FromInt32(1)), ptr.To(intstr.FromInt32(1)), nil), 255 256 newRS: func() *apps.ReplicaSet { 257 rs := rs("foo-v2", 2, nil, newTimestamp) 258 rs.Status.AvailableReplicas = 0 259 return rs 260 }(), 261 oldRSs: []*apps.ReplicaSet{rs("foo-v1", 1, nil, oldTimestamp)}, 262 263 expectedNew: rs("foo-v2", 2, nil, newTimestamp), 264 expectedOld: []*apps.ReplicaSet{rs("foo-v1", 1, nil, oldTimestamp)}, 265 }, 266 } 267 268 for _, test := range tests { 269 t.Run(test.name, func(t *testing.T) { 270 _ = olderTimestamp 271 t.Log(test.name) 272 fake := fake.Clientset{} 273 dc := &DeploymentController{ 274 client: &fake, 275 eventRecorder: &record.FakeRecorder{}, 276 } 277 278 if test.newRS != nil { 279 desiredReplicas := *(test.oldDeployment.Spec.Replicas) 280 if desired, ok := test.desiredReplicasAnnotations[test.newRS.Name]; ok { 281 desiredReplicas = desired 282 } 283 deploymentutil.SetReplicasAnnotations(test.newRS, desiredReplicas, desiredReplicas+deploymentutil.MaxSurge(*test.oldDeployment)) 284 } 285 for i := range test.oldRSs { 286 rs := test.oldRSs[i] 287 if rs == nil { 288 continue 289 } 290 desiredReplicas := *(test.oldDeployment.Spec.Replicas) 291 if desired, ok := test.desiredReplicasAnnotations[rs.Name]; ok { 292 desiredReplicas = desired 293 } 294 deploymentutil.SetReplicasAnnotations(rs, desiredReplicas, desiredReplicas+deploymentutil.MaxSurge(*test.oldDeployment)) 295 } 296 297 _, ctx := ktesting.NewTestContext(t) 298 299 if err := dc.scale(ctx, test.deployment, test.newRS, test.oldRSs); err != nil { 300 t.Errorf("%s: unexpected error: %v", test.name, err) 301 return 302 } 303 304 // Construct the nameToSize map that will hold all the sizes we got our of tests 305 // Skip updating the map if the replica set wasn't updated since there will be 306 // no update action for it. 307 nameToSize := make(map[string]int32) 308 if test.newRS != nil { 309 nameToSize[test.newRS.Name] = *(test.newRS.Spec.Replicas) 310 } 311 for i := range test.oldRSs { 312 rs := test.oldRSs[i] 313 nameToSize[rs.Name] = *(rs.Spec.Replicas) 314 } 315 // Get all the UPDATE actions and update nameToSize with all the updated sizes. 316 for _, action := range fake.Actions() { 317 rs := action.(testclient.UpdateAction).GetObject().(*apps.ReplicaSet) 318 if !test.wasntUpdated[rs.Name] { 319 nameToSize[rs.Name] = *(rs.Spec.Replicas) 320 } 321 } 322 323 if test.expectedNew != nil && test.newRS != nil && *(test.expectedNew.Spec.Replicas) != nameToSize[test.newRS.Name] { 324 t.Errorf("%s: expected new replicas: %d, got: %d", test.name, *(test.expectedNew.Spec.Replicas), nameToSize[test.newRS.Name]) 325 return 326 } 327 if len(test.expectedOld) != len(test.oldRSs) { 328 t.Errorf("%s: expected %d old replica sets, got %d", test.name, len(test.expectedOld), len(test.oldRSs)) 329 return 330 } 331 for n := range test.oldRSs { 332 rs := test.oldRSs[n] 333 expected := test.expectedOld[n] 334 if *(expected.Spec.Replicas) != nameToSize[rs.Name] { 335 t.Errorf("%s: expected old (%s) replicas: %d, got: %d", test.name, rs.Name, *(expected.Spec.Replicas), nameToSize[rs.Name]) 336 } 337 } 338 }) 339 } 340 } 341 342 func TestDeploymentController_cleanupDeployment(t *testing.T) { 343 selector := map[string]string{"foo": "bar"} 344 alreadyDeleted := newRSWithStatus("foo-1", 0, 0, selector) 345 now := metav1.Now() 346 alreadyDeleted.DeletionTimestamp = &now 347 348 tests := []struct { 349 oldRSs []*apps.ReplicaSet 350 revisionHistoryLimit int32 351 expectedDeletions int 352 }{ 353 { 354 oldRSs: []*apps.ReplicaSet{ 355 newRSWithStatus("foo-1", 0, 0, selector), 356 newRSWithStatus("foo-2", 0, 0, selector), 357 newRSWithStatus("foo-3", 0, 0, selector), 358 }, 359 revisionHistoryLimit: 1, 360 expectedDeletions: 2, 361 }, 362 { 363 // Only delete the replica set with Spec.Replicas = Status.Replicas = 0. 364 oldRSs: []*apps.ReplicaSet{ 365 newRSWithStatus("foo-1", 0, 0, selector), 366 newRSWithStatus("foo-2", 0, 1, selector), 367 newRSWithStatus("foo-3", 1, 0, selector), 368 newRSWithStatus("foo-4", 1, 1, selector), 369 }, 370 revisionHistoryLimit: 0, 371 expectedDeletions: 1, 372 }, 373 374 { 375 oldRSs: []*apps.ReplicaSet{ 376 newRSWithStatus("foo-1", 0, 0, selector), 377 newRSWithStatus("foo-2", 0, 0, selector), 378 }, 379 revisionHistoryLimit: 0, 380 expectedDeletions: 2, 381 }, 382 { 383 oldRSs: []*apps.ReplicaSet{ 384 newRSWithStatus("foo-1", 1, 1, selector), 385 newRSWithStatus("foo-2", 1, 1, selector), 386 }, 387 revisionHistoryLimit: 0, 388 expectedDeletions: 0, 389 }, 390 { 391 oldRSs: []*apps.ReplicaSet{ 392 alreadyDeleted, 393 }, 394 revisionHistoryLimit: 0, 395 expectedDeletions: 0, 396 }, 397 { 398 // with unlimited revisionHistoryLimit 399 oldRSs: []*apps.ReplicaSet{ 400 newRSWithStatus("foo-1", 0, 0, selector), 401 newRSWithStatus("foo-2", 0, 0, selector), 402 newRSWithStatus("foo-3", 0, 0, selector), 403 }, 404 revisionHistoryLimit: math.MaxInt32, 405 expectedDeletions: 0, 406 }, 407 } 408 409 for i := range tests { 410 test := tests[i] 411 t.Logf("scenario %d", i) 412 413 _, ctx := ktesting.NewTestContext(t) 414 415 fake := &fake.Clientset{} 416 informers := informers.NewSharedInformerFactory(fake, controller.NoResyncPeriodFunc()) 417 controller, err := NewDeploymentController(ctx, informers.Apps().V1().Deployments(), informers.Apps().V1().ReplicaSets(), informers.Core().V1().Pods(), fake) 418 if err != nil { 419 t.Fatalf("error creating Deployment controller: %v", err) 420 } 421 422 controller.eventRecorder = &record.FakeRecorder{} 423 controller.dListerSynced = alwaysReady 424 controller.rsListerSynced = alwaysReady 425 controller.podListerSynced = alwaysReady 426 for _, rs := range test.oldRSs { 427 informers.Apps().V1().ReplicaSets().Informer().GetIndexer().Add(rs) 428 } 429 430 stopCh := make(chan struct{}) 431 defer close(stopCh) 432 informers.Start(stopCh) 433 informers.WaitForCacheSync(stopCh) 434 435 t.Logf(" &test.revisionHistoryLimit: %d", test.revisionHistoryLimit) 436 d := newDeployment("foo", 1, &test.revisionHistoryLimit, nil, nil, map[string]string{"foo": "bar"}) 437 controller.cleanupDeployment(ctx, test.oldRSs, d) 438 439 gotDeletions := 0 440 for _, action := range fake.Actions() { 441 if action.GetVerb() == "delete" { 442 gotDeletions++ 443 } 444 } 445 if gotDeletions != test.expectedDeletions { 446 t.Errorf("expect %v old replica sets been deleted, but got %v", test.expectedDeletions, gotDeletions) 447 continue 448 } 449 } 450 } 451 452 func TestDeploymentController_cleanupDeploymentOrder(t *testing.T) { 453 selector := map[string]string{"foo": "bar"} 454 now := metav1.Now() 455 duration := time.Minute 456 457 newRSWithRevisionAndCreationTimestamp := func(name string, replicas int32, selector map[string]string, timestamp time.Time, revision string) *apps.ReplicaSet { 458 rs := rs(name, replicas, selector, metav1.NewTime(timestamp)) 459 if revision != "" { 460 rs.Annotations = map[string]string{ 461 deploymentutil.RevisionAnnotation: revision, 462 } 463 } 464 rs.Status = apps.ReplicaSetStatus{ 465 Replicas: int32(replicas), 466 } 467 return rs 468 } 469 470 // for all test cases, creationTimestamp order keeps as: rs1 < rs2 < rs3 < r4 471 tests := []struct { 472 oldRSs []*apps.ReplicaSet 473 revisionHistoryLimit int32 474 expectedDeletedRSs sets.String 475 }{ 476 { 477 // revision order: rs1 < rs2, delete rs1 478 oldRSs: []*apps.ReplicaSet{ 479 newRSWithRevisionAndCreationTimestamp("foo-1", 0, selector, now.Add(-1*duration), "1"), 480 newRSWithRevisionAndCreationTimestamp("foo-2", 0, selector, now.Time, "2"), 481 }, 482 revisionHistoryLimit: 1, 483 expectedDeletedRSs: sets.NewString("foo-1"), 484 }, 485 { 486 // revision order: rs2 < rs1, delete rs2 487 oldRSs: []*apps.ReplicaSet{ 488 newRSWithRevisionAndCreationTimestamp("foo-1", 0, selector, now.Add(-1*duration), "2"), 489 newRSWithRevisionAndCreationTimestamp("foo-2", 0, selector, now.Time, "1"), 490 }, 491 revisionHistoryLimit: 1, 492 expectedDeletedRSs: sets.NewString("foo-2"), 493 }, 494 { 495 // rs1 has revision but rs2 doesn't have revision, delete rs2 496 oldRSs: []*apps.ReplicaSet{ 497 newRSWithRevisionAndCreationTimestamp("foo-1", 0, selector, now.Add(-1*duration), "1"), 498 newRSWithRevisionAndCreationTimestamp("foo-2", 0, selector, now.Time, ""), 499 }, 500 revisionHistoryLimit: 1, 501 expectedDeletedRSs: sets.NewString("foo-2"), 502 }, 503 { 504 // rs1 doesn't have revision while rs2 has revision, delete rs1 505 oldRSs: []*apps.ReplicaSet{ 506 newRSWithRevisionAndCreationTimestamp("foo-1", 0, selector, now.Add(-1*duration), ""), 507 newRSWithRevisionAndCreationTimestamp("foo-2", 0, selector, now.Time, "2"), 508 }, 509 revisionHistoryLimit: 1, 510 expectedDeletedRSs: sets.NewString("foo-1"), 511 }, 512 { 513 // revision order: rs1 < rs2 < r3, but rs1 has replicas, delete rs2 514 oldRSs: []*apps.ReplicaSet{ 515 newRSWithRevisionAndCreationTimestamp("foo-1", 1, selector, now.Add(-1*duration), "1"), 516 newRSWithRevisionAndCreationTimestamp("foo-2", 0, selector, now.Time, "2"), 517 newRSWithRevisionAndCreationTimestamp("foo-3", 0, selector, now.Add(duration), "3"), 518 }, 519 revisionHistoryLimit: 1, 520 expectedDeletedRSs: sets.NewString("foo-2"), 521 }, 522 { 523 // revision order: rs1 < rs2 < r3, both rs1 && rs2 have replicas, don't delete 524 oldRSs: []*apps.ReplicaSet{ 525 newRSWithRevisionAndCreationTimestamp("foo-1", 1, selector, now.Add(-1*duration), "1"), 526 newRSWithRevisionAndCreationTimestamp("foo-2", 1, selector, now.Time, "2"), 527 newRSWithRevisionAndCreationTimestamp("foo-3", 0, selector, now.Add(duration), "3"), 528 }, 529 revisionHistoryLimit: 1, 530 expectedDeletedRSs: sets.NewString(), 531 }, 532 { 533 // revision order: rs2 < rs4 < rs1 < rs3, delete rs2 && rs4 534 oldRSs: []*apps.ReplicaSet{ 535 newRSWithRevisionAndCreationTimestamp("foo-1", 0, selector, now.Add(-1*duration), "3"), 536 newRSWithRevisionAndCreationTimestamp("foo-2", 0, selector, now.Time, "1"), 537 newRSWithRevisionAndCreationTimestamp("foo-3", 0, selector, now.Add(duration), "4"), 538 newRSWithRevisionAndCreationTimestamp("foo-4", 0, selector, now.Add(2*duration), "2"), 539 }, 540 revisionHistoryLimit: 2, 541 expectedDeletedRSs: sets.NewString("foo-2", "foo-4"), 542 }, 543 } 544 545 for i := range tests { 546 test := tests[i] 547 t.Logf("scenario %d", i) 548 549 _, ctx := ktesting.NewTestContext(t) 550 551 fake := &fake.Clientset{} 552 informers := informers.NewSharedInformerFactory(fake, controller.NoResyncPeriodFunc()) 553 controller, err := NewDeploymentController(ctx, informers.Apps().V1().Deployments(), informers.Apps().V1().ReplicaSets(), informers.Core().V1().Pods(), fake) 554 if err != nil { 555 t.Fatalf("error creating Deployment controller: %v", err) 556 } 557 558 controller.eventRecorder = &record.FakeRecorder{} 559 controller.dListerSynced = alwaysReady 560 controller.rsListerSynced = alwaysReady 561 controller.podListerSynced = alwaysReady 562 for _, rs := range test.oldRSs { 563 informers.Apps().V1().ReplicaSets().Informer().GetIndexer().Add(rs) 564 } 565 566 stopCh := make(chan struct{}) 567 defer close(stopCh) 568 informers.Start(stopCh) 569 570 d := newDeployment("foo", 1, &test.revisionHistoryLimit, nil, nil, map[string]string{"foo": "bar"}) 571 controller.cleanupDeployment(ctx, test.oldRSs, d) 572 573 deletedRSs := sets.String{} 574 for _, action := range fake.Actions() { 575 deleteAction, ok := action.(testclient.DeleteActionImpl) 576 if !ok { 577 t.Logf("Found not-delete action with verb %v. Ignoring.", action.GetVerb()) 578 continue 579 } 580 581 if deleteAction.GetResource().Resource != "replicasets" { 582 continue 583 } 584 585 deletedRSs.Insert(deleteAction.GetName()) 586 } 587 t.Logf("&test.revisionHistoryLimit: %d, &test.deletedReplicaSets: %v", test.revisionHistoryLimit, deletedRSs) 588 589 if !test.expectedDeletedRSs.Equal(deletedRSs) { 590 t.Errorf("expect to delete old replica sets %v, but got %v", test.expectedDeletedRSs, deletedRSs) 591 continue 592 } 593 } 594 }