k8s.io/kubernetes@v1.29.3/pkg/apis/apps/v1/defaults_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 v1_test 18 19 import ( 20 "reflect" 21 "testing" 22 23 appsv1 "k8s.io/api/apps/v1" 24 v1 "k8s.io/api/core/v1" 25 apiequality "k8s.io/apimachinery/pkg/api/equality" 26 "k8s.io/apimachinery/pkg/api/resource" 27 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 28 "k8s.io/apimachinery/pkg/runtime" 29 "k8s.io/apimachinery/pkg/util/intstr" 30 utilfeature "k8s.io/apiserver/pkg/util/feature" 31 featuregatetesting "k8s.io/component-base/featuregate/testing" 32 "k8s.io/kubernetes/pkg/api/legacyscheme" 33 _ "k8s.io/kubernetes/pkg/apis/apps/install" 34 . "k8s.io/kubernetes/pkg/apis/apps/v1" 35 _ "k8s.io/kubernetes/pkg/apis/core/install" 36 "k8s.io/kubernetes/pkg/features" 37 "k8s.io/utils/ptr" 38 ) 39 40 func TestSetDefaultDaemonSetSpec(t *testing.T) { 41 defaultLabels := map[string]string{"foo": "bar"} 42 maxUnavailable := intstr.FromInt32(1) 43 maxSurge := intstr.FromInt32(0) 44 period := int64(v1.DefaultTerminationGracePeriodSeconds) 45 defaultTemplate := v1.PodTemplateSpec{ 46 Spec: v1.PodSpec{ 47 DNSPolicy: v1.DNSClusterFirst, 48 RestartPolicy: v1.RestartPolicyAlways, 49 SecurityContext: &v1.PodSecurityContext{}, 50 TerminationGracePeriodSeconds: &period, 51 SchedulerName: v1.DefaultSchedulerName, 52 }, 53 ObjectMeta: metav1.ObjectMeta{ 54 Labels: defaultLabels, 55 }, 56 } 57 templateNoLabel := v1.PodTemplateSpec{ 58 Spec: v1.PodSpec{ 59 DNSPolicy: v1.DNSClusterFirst, 60 RestartPolicy: v1.RestartPolicyAlways, 61 SecurityContext: &v1.PodSecurityContext{}, 62 TerminationGracePeriodSeconds: &period, 63 SchedulerName: v1.DefaultSchedulerName, 64 }, 65 } 66 tests := []struct { 67 original *appsv1.DaemonSet 68 expected *appsv1.DaemonSet 69 }{ 70 { // Labels change/defaulting test. 71 original: &appsv1.DaemonSet{ 72 Spec: appsv1.DaemonSetSpec{ 73 Template: defaultTemplate, 74 }, 75 }, 76 expected: &appsv1.DaemonSet{ 77 ObjectMeta: metav1.ObjectMeta{ 78 Labels: defaultLabels, 79 }, 80 Spec: appsv1.DaemonSetSpec{ 81 Template: defaultTemplate, 82 UpdateStrategy: appsv1.DaemonSetUpdateStrategy{ 83 Type: appsv1.RollingUpdateDaemonSetStrategyType, 84 RollingUpdate: &appsv1.RollingUpdateDaemonSet{ 85 MaxUnavailable: &maxUnavailable, 86 MaxSurge: &maxSurge, 87 }, 88 }, 89 RevisionHistoryLimit: ptr.To[int32](10), 90 }, 91 }, 92 }, 93 { // Labels change/defaulting test. 94 original: &appsv1.DaemonSet{ 95 ObjectMeta: metav1.ObjectMeta{ 96 Labels: map[string]string{ 97 "bar": "foo", 98 }, 99 }, 100 Spec: appsv1.DaemonSetSpec{ 101 Template: defaultTemplate, 102 RevisionHistoryLimit: ptr.To[int32](1), 103 }, 104 }, 105 expected: &appsv1.DaemonSet{ 106 ObjectMeta: metav1.ObjectMeta{ 107 Labels: map[string]string{ 108 "bar": "foo", 109 }, 110 }, 111 Spec: appsv1.DaemonSetSpec{ 112 Template: defaultTemplate, 113 UpdateStrategy: appsv1.DaemonSetUpdateStrategy{ 114 Type: appsv1.RollingUpdateDaemonSetStrategyType, 115 RollingUpdate: &appsv1.RollingUpdateDaemonSet{ 116 MaxUnavailable: &maxUnavailable, 117 MaxSurge: &maxSurge, 118 }, 119 }, 120 RevisionHistoryLimit: ptr.To[int32](1), 121 }, 122 }, 123 }, 124 { // OnDeleteDaemonSetStrategyType update strategy. 125 original: &appsv1.DaemonSet{ 126 Spec: appsv1.DaemonSetSpec{ 127 Template: templateNoLabel, 128 UpdateStrategy: appsv1.DaemonSetUpdateStrategy{ 129 Type: appsv1.OnDeleteDaemonSetStrategyType, 130 }, 131 }, 132 }, 133 expected: &appsv1.DaemonSet{ 134 Spec: appsv1.DaemonSetSpec{ 135 Template: templateNoLabel, 136 UpdateStrategy: appsv1.DaemonSetUpdateStrategy{ 137 Type: appsv1.OnDeleteDaemonSetStrategyType, 138 }, 139 RevisionHistoryLimit: ptr.To[int32](10), 140 }, 141 }, 142 }, 143 { // Custom unique label key. 144 original: &appsv1.DaemonSet{ 145 Spec: appsv1.DaemonSetSpec{}, 146 }, 147 expected: &appsv1.DaemonSet{ 148 Spec: appsv1.DaemonSetSpec{ 149 Template: templateNoLabel, 150 UpdateStrategy: appsv1.DaemonSetUpdateStrategy{ 151 Type: appsv1.RollingUpdateDaemonSetStrategyType, 152 RollingUpdate: &appsv1.RollingUpdateDaemonSet{ 153 MaxUnavailable: &maxUnavailable, 154 MaxSurge: &maxSurge, 155 }, 156 }, 157 RevisionHistoryLimit: ptr.To[int32](10), 158 }, 159 }, 160 }, 161 } 162 163 for i, test := range tests { 164 original := test.original 165 expected := test.expected 166 obj2 := roundTrip(t, runtime.Object(original)) 167 got, ok := obj2.(*appsv1.DaemonSet) 168 if !ok { 169 t.Errorf("(%d) unexpected object: %v", i, got) 170 t.FailNow() 171 } 172 if !apiequality.Semantic.DeepEqual(got.Spec, expected.Spec) { 173 t.Errorf("(%d) got different than expected\ngot:\n\t%+v\nexpected:\n\t%+v", i, got.Spec, expected.Spec) 174 } 175 } 176 } 177 178 func TestSetDefaultStatefulSet(t *testing.T) { 179 defaultLabels := map[string]string{"foo": "bar"} 180 var defaultPartition int32 = 0 181 var defaultReplicas int32 = 1 182 var notTheDefaultPartition int32 = 42 183 184 period := int64(v1.DefaultTerminationGracePeriodSeconds) 185 defaultTemplate := v1.PodTemplateSpec{ 186 Spec: v1.PodSpec{ 187 DNSPolicy: v1.DNSClusterFirst, 188 RestartPolicy: v1.RestartPolicyAlways, 189 SecurityContext: &v1.PodSecurityContext{}, 190 TerminationGracePeriodSeconds: &period, 191 SchedulerName: v1.DefaultSchedulerName, 192 }, 193 ObjectMeta: metav1.ObjectMeta{ 194 Labels: defaultLabels, 195 }, 196 } 197 198 tests := []struct { 199 name string 200 original *appsv1.StatefulSet 201 expected *appsv1.StatefulSet 202 enablePVCDeletionPolicy bool 203 enableMaxUnavailablePolicy bool 204 }{ 205 { 206 name: "labels and default update strategy", 207 original: &appsv1.StatefulSet{ 208 Spec: appsv1.StatefulSetSpec{ 209 Template: defaultTemplate, 210 }, 211 }, 212 expected: &appsv1.StatefulSet{ 213 ObjectMeta: metav1.ObjectMeta{ 214 Labels: defaultLabels, 215 }, 216 Spec: appsv1.StatefulSetSpec{ 217 Replicas: &defaultReplicas, 218 MinReadySeconds: int32(0), 219 Template: defaultTemplate, 220 PodManagementPolicy: appsv1.OrderedReadyPodManagement, 221 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 222 Type: appsv1.RollingUpdateStatefulSetStrategyType, 223 RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ 224 Partition: &defaultPartition, 225 }, 226 }, 227 RevisionHistoryLimit: ptr.To[int32](10), 228 }, 229 }, 230 }, 231 { 232 name: "Alternate update strategy", 233 original: &appsv1.StatefulSet{ 234 Spec: appsv1.StatefulSetSpec{ 235 Template: defaultTemplate, 236 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 237 Type: appsv1.OnDeleteStatefulSetStrategyType, 238 }, 239 }, 240 }, 241 expected: &appsv1.StatefulSet{ 242 ObjectMeta: metav1.ObjectMeta{ 243 Labels: defaultLabels, 244 }, 245 Spec: appsv1.StatefulSetSpec{ 246 Replicas: &defaultReplicas, 247 MinReadySeconds: int32(0), 248 Template: defaultTemplate, 249 PodManagementPolicy: appsv1.OrderedReadyPodManagement, 250 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 251 Type: appsv1.OnDeleteStatefulSetStrategyType, 252 }, 253 RevisionHistoryLimit: ptr.To[int32](10), 254 }, 255 }, 256 }, 257 { 258 name: "Parallel pod management policy", 259 original: &appsv1.StatefulSet{ 260 Spec: appsv1.StatefulSetSpec{ 261 Template: defaultTemplate, 262 PodManagementPolicy: appsv1.ParallelPodManagement, 263 }, 264 }, 265 expected: &appsv1.StatefulSet{ 266 ObjectMeta: metav1.ObjectMeta{ 267 Labels: defaultLabels, 268 }, 269 Spec: appsv1.StatefulSetSpec{ 270 Replicas: &defaultReplicas, 271 MinReadySeconds: int32(0), 272 Template: defaultTemplate, 273 PodManagementPolicy: appsv1.ParallelPodManagement, 274 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 275 Type: appsv1.RollingUpdateStatefulSetStrategyType, 276 RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ 277 Partition: &defaultPartition, 278 }, 279 }, 280 RevisionHistoryLimit: ptr.To[int32](10), 281 }, 282 }, 283 }, 284 { 285 name: "UpdateStrategy.RollingUpdate.Partition is not lost when UpdateStrategy.Type is not set", 286 original: &appsv1.StatefulSet{ 287 Spec: appsv1.StatefulSetSpec{ 288 Template: defaultTemplate, 289 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 290 RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ 291 Partition: ¬TheDefaultPartition, 292 }, 293 }, 294 }, 295 }, 296 expected: &appsv1.StatefulSet{ 297 ObjectMeta: metav1.ObjectMeta{ 298 Labels: defaultLabels, 299 }, 300 Spec: appsv1.StatefulSetSpec{ 301 Replicas: &defaultReplicas, 302 MinReadySeconds: int32(0), 303 Template: defaultTemplate, 304 PodManagementPolicy: appsv1.OrderedReadyPodManagement, 305 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 306 Type: appsv1.RollingUpdateStatefulSetStrategyType, 307 RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ 308 Partition: ¬TheDefaultPartition, 309 }, 310 }, 311 RevisionHistoryLimit: ptr.To[int32](10), 312 }, 313 }, 314 }, 315 { 316 name: "PVC delete policy enabled, no policy specified", 317 original: &appsv1.StatefulSet{ 318 Spec: appsv1.StatefulSetSpec{ 319 Template: defaultTemplate, 320 }, 321 }, 322 expected: &appsv1.StatefulSet{ 323 ObjectMeta: metav1.ObjectMeta{ 324 Labels: defaultLabels, 325 }, 326 Spec: appsv1.StatefulSetSpec{ 327 Replicas: &defaultReplicas, 328 Template: defaultTemplate, 329 PodManagementPolicy: appsv1.OrderedReadyPodManagement, 330 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 331 Type: appsv1.RollingUpdateStatefulSetStrategyType, 332 RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ 333 Partition: &defaultPartition, 334 }, 335 }, 336 PersistentVolumeClaimRetentionPolicy: &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{ 337 WhenDeleted: appsv1.RetainPersistentVolumeClaimRetentionPolicyType, 338 WhenScaled: appsv1.RetainPersistentVolumeClaimRetentionPolicyType, 339 }, 340 RevisionHistoryLimit: ptr.To[int32](10), 341 }, 342 }, 343 enablePVCDeletionPolicy: true, 344 }, 345 { 346 name: "PVC delete policy enabled, with scaledown policy specified", 347 original: &appsv1.StatefulSet{ 348 Spec: appsv1.StatefulSetSpec{ 349 Template: defaultTemplate, 350 PersistentVolumeClaimRetentionPolicy: &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{ 351 WhenScaled: appsv1.DeletePersistentVolumeClaimRetentionPolicyType, 352 }, 353 }, 354 }, 355 expected: &appsv1.StatefulSet{ 356 ObjectMeta: metav1.ObjectMeta{ 357 Labels: defaultLabels, 358 }, 359 Spec: appsv1.StatefulSetSpec{ 360 Replicas: &defaultReplicas, 361 Template: defaultTemplate, 362 PodManagementPolicy: appsv1.OrderedReadyPodManagement, 363 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 364 Type: appsv1.RollingUpdateStatefulSetStrategyType, 365 RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ 366 Partition: &defaultPartition, 367 }, 368 }, 369 PersistentVolumeClaimRetentionPolicy: &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{ 370 WhenDeleted: appsv1.RetainPersistentVolumeClaimRetentionPolicyType, 371 WhenScaled: appsv1.DeletePersistentVolumeClaimRetentionPolicyType, 372 }, 373 RevisionHistoryLimit: ptr.To[int32](10), 374 }, 375 }, 376 enablePVCDeletionPolicy: true, 377 }, 378 { 379 name: "PVC delete policy disabled, with set deletion policy specified", 380 original: &appsv1.StatefulSet{ 381 Spec: appsv1.StatefulSetSpec{ 382 Template: defaultTemplate, 383 PersistentVolumeClaimRetentionPolicy: &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{ 384 WhenDeleted: appsv1.DeletePersistentVolumeClaimRetentionPolicyType, 385 }, 386 }, 387 }, 388 expected: &appsv1.StatefulSet{ 389 ObjectMeta: metav1.ObjectMeta{ 390 Labels: defaultLabels, 391 }, 392 Spec: appsv1.StatefulSetSpec{ 393 Replicas: &defaultReplicas, 394 Template: defaultTemplate, 395 PodManagementPolicy: appsv1.OrderedReadyPodManagement, 396 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 397 Type: appsv1.RollingUpdateStatefulSetStrategyType, 398 RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ 399 Partition: &defaultPartition, 400 }, 401 }, 402 PersistentVolumeClaimRetentionPolicy: &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{ 403 WhenDeleted: appsv1.DeletePersistentVolumeClaimRetentionPolicyType, 404 WhenScaled: appsv1.RetainPersistentVolumeClaimRetentionPolicyType, 405 }, 406 RevisionHistoryLimit: ptr.To[int32](10), 407 }, 408 }, 409 enablePVCDeletionPolicy: true, 410 }, 411 { 412 name: "PVC delete policy disabled, with policy specified", 413 original: &appsv1.StatefulSet{ 414 Spec: appsv1.StatefulSetSpec{ 415 Template: defaultTemplate, 416 PersistentVolumeClaimRetentionPolicy: &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{ 417 WhenScaled: appsv1.DeletePersistentVolumeClaimRetentionPolicyType, 418 }, 419 }, 420 }, 421 expected: &appsv1.StatefulSet{ 422 ObjectMeta: metav1.ObjectMeta{ 423 Labels: defaultLabels, 424 }, 425 Spec: appsv1.StatefulSetSpec{ 426 Replicas: &defaultReplicas, 427 Template: defaultTemplate, 428 PodManagementPolicy: appsv1.OrderedReadyPodManagement, 429 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 430 Type: appsv1.RollingUpdateStatefulSetStrategyType, 431 RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ 432 Partition: &defaultPartition, 433 }, 434 }, 435 PersistentVolumeClaimRetentionPolicy: &appsv1.StatefulSetPersistentVolumeClaimRetentionPolicy{ 436 WhenScaled: appsv1.DeletePersistentVolumeClaimRetentionPolicyType, 437 }, 438 RevisionHistoryLimit: ptr.To[int32](10), 439 }, 440 }, 441 enablePVCDeletionPolicy: false, 442 }, 443 { 444 name: "MaxUnavailable disabled, with maxUnavailable not specified", 445 original: &appsv1.StatefulSet{ 446 Spec: appsv1.StatefulSetSpec{ 447 Template: defaultTemplate, 448 }, 449 }, 450 expected: &appsv1.StatefulSet{ 451 ObjectMeta: metav1.ObjectMeta{ 452 Labels: defaultLabels, 453 }, 454 Spec: appsv1.StatefulSetSpec{ 455 Replicas: &defaultReplicas, 456 Template: defaultTemplate, 457 PodManagementPolicy: appsv1.OrderedReadyPodManagement, 458 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 459 Type: appsv1.RollingUpdateStatefulSetStrategyType, 460 RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ 461 Partition: ptr.To[int32](0), 462 }, 463 }, 464 RevisionHistoryLimit: ptr.To[int32](10), 465 }, 466 }, 467 enableMaxUnavailablePolicy: false, 468 }, 469 { 470 name: "MaxUnavailable disabled, with default maxUnavailable specified", 471 original: &appsv1.StatefulSet{ 472 Spec: appsv1.StatefulSetSpec{ 473 Template: defaultTemplate, 474 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 475 RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ 476 Partition: &defaultPartition, 477 MaxUnavailable: ptr.To(intstr.FromInt32(1)), 478 }, 479 }, 480 }, 481 }, 482 expected: &appsv1.StatefulSet{ 483 ObjectMeta: metav1.ObjectMeta{ 484 Labels: defaultLabels, 485 }, 486 Spec: appsv1.StatefulSetSpec{ 487 Replicas: &defaultReplicas, 488 Template: defaultTemplate, 489 PodManagementPolicy: appsv1.OrderedReadyPodManagement, 490 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 491 Type: appsv1.RollingUpdateStatefulSetStrategyType, 492 RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ 493 Partition: ptr.To[int32](0), 494 MaxUnavailable: ptr.To(intstr.FromInt32(1)), 495 }, 496 }, 497 RevisionHistoryLimit: ptr.To[int32](10), 498 }, 499 }, 500 enableMaxUnavailablePolicy: false, 501 }, 502 { 503 name: "MaxUnavailable disabled, with non default maxUnavailable specified", 504 original: &appsv1.StatefulSet{ 505 Spec: appsv1.StatefulSetSpec{ 506 Template: defaultTemplate, 507 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 508 RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ 509 Partition: ¬TheDefaultPartition, 510 MaxUnavailable: ptr.To(intstr.FromInt32(3)), 511 }, 512 }, 513 }, 514 }, 515 expected: &appsv1.StatefulSet{ 516 ObjectMeta: metav1.ObjectMeta{ 517 Labels: defaultLabels, 518 }, 519 Spec: appsv1.StatefulSetSpec{ 520 Replicas: &defaultReplicas, 521 Template: defaultTemplate, 522 PodManagementPolicy: appsv1.OrderedReadyPodManagement, 523 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 524 Type: appsv1.RollingUpdateStatefulSetStrategyType, 525 RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ 526 Partition: ptr.To[int32](42), 527 MaxUnavailable: ptr.To(intstr.FromInt32(3)), 528 }, 529 }, 530 RevisionHistoryLimit: ptr.To[int32](10), 531 }, 532 }, 533 enableMaxUnavailablePolicy: false, 534 }, 535 { 536 name: "MaxUnavailable enabled, with no maxUnavailable specified", 537 original: &appsv1.StatefulSet{ 538 Spec: appsv1.StatefulSetSpec{ 539 Template: defaultTemplate, 540 }, 541 }, 542 expected: &appsv1.StatefulSet{ 543 ObjectMeta: metav1.ObjectMeta{ 544 Labels: defaultLabels, 545 }, 546 Spec: appsv1.StatefulSetSpec{ 547 Replicas: &defaultReplicas, 548 Template: defaultTemplate, 549 PodManagementPolicy: appsv1.OrderedReadyPodManagement, 550 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 551 Type: appsv1.RollingUpdateStatefulSetStrategyType, 552 RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ 553 Partition: ptr.To[int32](0), 554 MaxUnavailable: ptr.To(intstr.FromInt32(1)), 555 }, 556 }, 557 RevisionHistoryLimit: ptr.To[int32](10), 558 }, 559 }, 560 enableMaxUnavailablePolicy: true, 561 }, 562 { 563 name: "MaxUnavailable enabled, with non default maxUnavailable specified", 564 original: &appsv1.StatefulSet{ 565 Spec: appsv1.StatefulSetSpec{ 566 Template: defaultTemplate, 567 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 568 RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ 569 Partition: ¬TheDefaultPartition, 570 MaxUnavailable: ptr.To(intstr.FromInt32(3)), 571 }, 572 }, 573 }, 574 }, 575 expected: &appsv1.StatefulSet{ 576 ObjectMeta: metav1.ObjectMeta{ 577 Labels: defaultLabels, 578 }, 579 Spec: appsv1.StatefulSetSpec{ 580 Replicas: &defaultReplicas, 581 Template: defaultTemplate, 582 PodManagementPolicy: appsv1.OrderedReadyPodManagement, 583 UpdateStrategy: appsv1.StatefulSetUpdateStrategy{ 584 Type: appsv1.RollingUpdateStatefulSetStrategyType, 585 RollingUpdate: &appsv1.RollingUpdateStatefulSetStrategy{ 586 Partition: ptr.To[int32](42), 587 MaxUnavailable: ptr.To(intstr.FromInt32(3)), 588 }, 589 }, 590 RevisionHistoryLimit: ptr.To[int32](10), 591 }, 592 }, 593 enableMaxUnavailablePolicy: true, 594 }, 595 } 596 597 for _, test := range tests { 598 test := test 599 t.Run(test.name, func(t *testing.T) { 600 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StatefulSetAutoDeletePVC, test.enablePVCDeletionPolicy)() 601 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MaxUnavailableStatefulSet, test.enableMaxUnavailablePolicy)() 602 603 obj2 := roundTrip(t, runtime.Object(test.original)) 604 got, ok := obj2.(*appsv1.StatefulSet) 605 if !ok { 606 t.Errorf("unexpected object: %v", got) 607 t.FailNow() 608 } 609 if !apiequality.Semantic.DeepEqual(got.Spec, test.expected.Spec) { 610 t.Errorf("got different than expected\ngot:\n\t%+v\nexpected:\n\t%+v", got.Spec, test.expected.Spec) 611 } 612 }) 613 } 614 } 615 616 func TestSetDefaultDeployment(t *testing.T) { 617 defaultIntOrString := intstr.FromString("25%") 618 differentIntOrString := intstr.FromInt32(5) 619 period := int64(v1.DefaultTerminationGracePeriodSeconds) 620 defaultTemplate := v1.PodTemplateSpec{ 621 Spec: v1.PodSpec{ 622 DNSPolicy: v1.DNSClusterFirst, 623 RestartPolicy: v1.RestartPolicyAlways, 624 SecurityContext: &v1.PodSecurityContext{}, 625 TerminationGracePeriodSeconds: &period, 626 SchedulerName: v1.DefaultSchedulerName, 627 }, 628 } 629 tests := []struct { 630 original *appsv1.Deployment 631 expected *appsv1.Deployment 632 }{ 633 { 634 original: &appsv1.Deployment{}, 635 expected: &appsv1.Deployment{ 636 Spec: appsv1.DeploymentSpec{ 637 Replicas: ptr.To[int32](1), 638 Strategy: appsv1.DeploymentStrategy{ 639 Type: appsv1.RollingUpdateDeploymentStrategyType, 640 RollingUpdate: &appsv1.RollingUpdateDeployment{ 641 MaxSurge: &defaultIntOrString, 642 MaxUnavailable: &defaultIntOrString, 643 }, 644 }, 645 RevisionHistoryLimit: ptr.To[int32](10), 646 ProgressDeadlineSeconds: ptr.To[int32](600), 647 Template: defaultTemplate, 648 }, 649 }, 650 }, 651 { 652 original: &appsv1.Deployment{ 653 Spec: appsv1.DeploymentSpec{ 654 Replicas: ptr.To[int32](5), 655 Strategy: appsv1.DeploymentStrategy{ 656 RollingUpdate: &appsv1.RollingUpdateDeployment{ 657 MaxSurge: &differentIntOrString, 658 }, 659 }, 660 }, 661 }, 662 expected: &appsv1.Deployment{ 663 Spec: appsv1.DeploymentSpec{ 664 Replicas: ptr.To[int32](5), 665 Strategy: appsv1.DeploymentStrategy{ 666 Type: appsv1.RollingUpdateDeploymentStrategyType, 667 RollingUpdate: &appsv1.RollingUpdateDeployment{ 668 MaxSurge: &differentIntOrString, 669 MaxUnavailable: &defaultIntOrString, 670 }, 671 }, 672 RevisionHistoryLimit: ptr.To[int32](10), 673 ProgressDeadlineSeconds: ptr.To[int32](600), 674 Template: defaultTemplate, 675 }, 676 }, 677 }, 678 { 679 original: &appsv1.Deployment{ 680 Spec: appsv1.DeploymentSpec{ 681 Replicas: ptr.To[int32](3), 682 Strategy: appsv1.DeploymentStrategy{ 683 Type: appsv1.RollingUpdateDeploymentStrategyType, 684 RollingUpdate: nil, 685 }, 686 }, 687 }, 688 expected: &appsv1.Deployment{ 689 Spec: appsv1.DeploymentSpec{ 690 Replicas: ptr.To[int32](3), 691 Strategy: appsv1.DeploymentStrategy{ 692 Type: appsv1.RollingUpdateDeploymentStrategyType, 693 RollingUpdate: &appsv1.RollingUpdateDeployment{ 694 MaxSurge: &defaultIntOrString, 695 MaxUnavailable: &defaultIntOrString, 696 }, 697 }, 698 RevisionHistoryLimit: ptr.To[int32](10), 699 ProgressDeadlineSeconds: ptr.To[int32](600), 700 Template: defaultTemplate, 701 }, 702 }, 703 }, 704 { 705 original: &appsv1.Deployment{ 706 Spec: appsv1.DeploymentSpec{ 707 Replicas: ptr.To[int32](5), 708 Strategy: appsv1.DeploymentStrategy{ 709 Type: appsv1.RecreateDeploymentStrategyType, 710 }, 711 RevisionHistoryLimit: ptr.To[int32](0), 712 }, 713 }, 714 expected: &appsv1.Deployment{ 715 Spec: appsv1.DeploymentSpec{ 716 Replicas: ptr.To[int32](5), 717 Strategy: appsv1.DeploymentStrategy{ 718 Type: appsv1.RecreateDeploymentStrategyType, 719 }, 720 RevisionHistoryLimit: ptr.To[int32](0), 721 ProgressDeadlineSeconds: ptr.To[int32](600), 722 Template: defaultTemplate, 723 }, 724 }, 725 }, 726 { 727 original: &appsv1.Deployment{ 728 Spec: appsv1.DeploymentSpec{ 729 Replicas: ptr.To[int32](5), 730 Strategy: appsv1.DeploymentStrategy{ 731 Type: appsv1.RecreateDeploymentStrategyType, 732 }, 733 ProgressDeadlineSeconds: ptr.To[int32](30), 734 RevisionHistoryLimit: ptr.To[int32](2), 735 }, 736 }, 737 expected: &appsv1.Deployment{ 738 Spec: appsv1.DeploymentSpec{ 739 Replicas: ptr.To[int32](5), 740 Strategy: appsv1.DeploymentStrategy{ 741 Type: appsv1.RecreateDeploymentStrategyType, 742 }, 743 ProgressDeadlineSeconds: ptr.To[int32](30), 744 RevisionHistoryLimit: ptr.To[int32](2), 745 Template: defaultTemplate, 746 }, 747 }, 748 }, 749 } 750 751 for _, test := range tests { 752 original := test.original 753 expected := test.expected 754 obj2 := roundTrip(t, runtime.Object(original)) 755 got, ok := obj2.(*appsv1.Deployment) 756 if !ok { 757 t.Errorf("unexpected object: %v", got) 758 t.FailNow() 759 } 760 if !apiequality.Semantic.DeepEqual(got.Spec, expected.Spec) { 761 t.Errorf("object mismatch!\nexpected:\n\t%+v\ngot:\n\t%+v", got.Spec, expected.Spec) 762 } 763 } 764 } 765 766 func TestDefaultDeploymentAvailability(t *testing.T) { 767 d := roundTrip(t, runtime.Object(&appsv1.Deployment{})).(*appsv1.Deployment) 768 769 maxUnavailable, err := intstr.GetScaledValueFromIntOrPercent(d.Spec.Strategy.RollingUpdate.MaxUnavailable, int(*(d.Spec.Replicas)), false) 770 if err != nil { 771 t.Fatalf("unexpected error: %v", err) 772 } 773 774 if *(d.Spec.Replicas)-int32(maxUnavailable) <= 0 { 775 t.Fatalf("the default value of maxUnavailable can lead to no active replicas during rolling update") 776 } 777 } 778 779 func TestSetDefaultReplicaSetReplicas(t *testing.T) { 780 tests := []struct { 781 rs appsv1.ReplicaSet 782 expectReplicas int32 783 }{ 784 { 785 rs: appsv1.ReplicaSet{ 786 Spec: appsv1.ReplicaSetSpec{ 787 Template: v1.PodTemplateSpec{ 788 ObjectMeta: metav1.ObjectMeta{ 789 Labels: map[string]string{ 790 "foo": "bar", 791 }, 792 }, 793 }, 794 }, 795 }, 796 expectReplicas: 1, 797 }, 798 { 799 rs: appsv1.ReplicaSet{ 800 Spec: appsv1.ReplicaSetSpec{ 801 Replicas: ptr.To[int32](0), 802 Template: v1.PodTemplateSpec{ 803 ObjectMeta: metav1.ObjectMeta{ 804 Labels: map[string]string{ 805 "foo": "bar", 806 }, 807 }, 808 }, 809 }, 810 }, 811 expectReplicas: 0, 812 }, 813 { 814 rs: appsv1.ReplicaSet{ 815 Spec: appsv1.ReplicaSetSpec{ 816 Replicas: ptr.To[int32](3), 817 Template: v1.PodTemplateSpec{ 818 ObjectMeta: metav1.ObjectMeta{ 819 Labels: map[string]string{ 820 "foo": "bar", 821 }, 822 }, 823 }, 824 }, 825 }, 826 expectReplicas: 3, 827 }, 828 } 829 830 for _, test := range tests { 831 rs := &test.rs 832 obj2 := roundTrip(t, runtime.Object(rs)) 833 rs2, ok := obj2.(*appsv1.ReplicaSet) 834 if !ok { 835 t.Errorf("unexpected object: %v", rs2) 836 t.FailNow() 837 } 838 if rs2.Spec.Replicas == nil { 839 t.Errorf("unexpected nil Replicas") 840 } else if test.expectReplicas != *rs2.Spec.Replicas { 841 t.Errorf("expected: %d replicas, got: %d", test.expectReplicas, *rs2.Spec.Replicas) 842 } 843 } 844 } 845 846 func TestDefaultRequestIsNotSetForReplicaSet(t *testing.T) { 847 s := v1.PodSpec{} 848 s.Containers = []v1.Container{ 849 { 850 Resources: v1.ResourceRequirements{ 851 Limits: v1.ResourceList{ 852 v1.ResourceCPU: resource.MustParse("100m"), 853 }, 854 }, 855 }, 856 } 857 rs := &appsv1.ReplicaSet{ 858 Spec: appsv1.ReplicaSetSpec{ 859 Replicas: ptr.To[int32](3), 860 Template: v1.PodTemplateSpec{ 861 ObjectMeta: metav1.ObjectMeta{ 862 Labels: map[string]string{ 863 "foo": "bar", 864 }, 865 }, 866 Spec: s, 867 }, 868 }, 869 } 870 output := roundTrip(t, runtime.Object(rs)) 871 rs2 := output.(*appsv1.ReplicaSet) 872 defaultRequest := rs2.Spec.Template.Spec.Containers[0].Resources.Requests 873 requestValue := defaultRequest[v1.ResourceCPU] 874 if requestValue.String() != "0" { 875 t.Errorf("Expected 0 request value, got: %s", requestValue.String()) 876 } 877 } 878 879 func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { 880 data, err := runtime.Encode(legacyscheme.Codecs.LegacyCodec(SchemeGroupVersion), obj) 881 if err != nil { 882 t.Errorf("%v\n %#v", err, obj) 883 return nil 884 } 885 obj2, err := runtime.Decode(legacyscheme.Codecs.UniversalDecoder(), data) 886 if err != nil { 887 t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj) 888 return nil 889 } 890 obj3 := reflect.New(reflect.TypeOf(obj).Elem()).Interface().(runtime.Object) 891 err = legacyscheme.Scheme.Convert(obj2, obj3, nil) 892 if err != nil { 893 t.Errorf("%v\nSource: %#v", err, obj2) 894 return nil 895 } 896 return obj3 897 }