1
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 {
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 {
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 {
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 {
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 }
898
View as plain text