1
16
17 package statefulset
18
19 import (
20 "testing"
21
22 "github.com/google/go-cmp/cmp"
23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24 "k8s.io/apimachinery/pkg/util/intstr"
25 genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
26 utilfeature "k8s.io/apiserver/pkg/util/feature"
27 featuregatetesting "k8s.io/component-base/featuregate/testing"
28 "k8s.io/kubernetes/pkg/apis/apps"
29 api "k8s.io/kubernetes/pkg/apis/core"
30 "k8s.io/kubernetes/pkg/features"
31 )
32
33 func TestStatefulSetStrategy(t *testing.T) {
34 ctx := genericapirequest.NewDefaultContext()
35 if !Strategy.NamespaceScoped() {
36 t.Errorf("StatefulSet must be namespace scoped")
37 }
38 if Strategy.AllowCreateOnUpdate() {
39 t.Errorf("StatefulSet should not allow create on update")
40 }
41
42 validSelector := map[string]string{"a": "b"}
43 validPodTemplate := api.PodTemplate{
44 Template: api.PodTemplateSpec{
45 ObjectMeta: metav1.ObjectMeta{
46 Labels: validSelector,
47 },
48 Spec: api.PodSpec{
49 RestartPolicy: api.RestartPolicyAlways,
50 DNSPolicy: api.DNSClusterFirst,
51 Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
52 },
53 },
54 }
55 ps := &apps.StatefulSet{
56 ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
57 Spec: apps.StatefulSetSpec{
58 PodManagementPolicy: apps.OrderedReadyPodManagement,
59 Selector: &metav1.LabelSelector{MatchLabels: validSelector},
60 Template: validPodTemplate.Template,
61 UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
62 },
63 Status: apps.StatefulSetStatus{Replicas: 3},
64 }
65
66 Strategy.PrepareForCreate(ctx, ps)
67 if ps.Status.Replicas != 0 {
68 t.Error("StatefulSet should not allow setting status.replicas on create")
69 }
70 errs := Strategy.Validate(ctx, ps)
71 if len(errs) != 0 {
72 t.Errorf("unexpected error validating %v", errs)
73 }
74 newMinReadySeconds := int32(50)
75
76 validPs := &apps.StatefulSet{
77 ObjectMeta: metav1.ObjectMeta{Name: ps.Name, Namespace: ps.Namespace, ResourceVersion: "1", Generation: 1},
78 Spec: apps.StatefulSetSpec{
79 PodManagementPolicy: apps.OrderedReadyPodManagement,
80 Selector: ps.Spec.Selector,
81 Template: validPodTemplate.Template,
82 UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
83 MinReadySeconds: newMinReadySeconds,
84 },
85 Status: apps.StatefulSetStatus{Replicas: 4},
86 }
87 Strategy.PrepareForUpdate(ctx, validPs, ps)
88 t.Run("StatefulSet minReadySeconds field validations on creation and updation", func(t *testing.T) {
89
90 ps := &apps.StatefulSet{
91 ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
92 Spec: apps.StatefulSetSpec{
93 PodManagementPolicy: apps.OrderedReadyPodManagement,
94 Selector: &metav1.LabelSelector{MatchLabels: validSelector},
95 Template: validPodTemplate.Template,
96 UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
97 MinReadySeconds: int32(-1),
98 },
99 }
100 Strategy.PrepareForCreate(ctx, ps)
101 errs := Strategy.Validate(ctx, ps)
102 if len(errs) == 0 {
103 t.Errorf("expected failure when MinReadySeconds is not positive number but got no error %v", errs)
104 }
105 expectedCreateErrorString := "spec.minReadySeconds: Invalid value: -1: must be greater than or equal to 0"
106 if errs[0].Error() != expectedCreateErrorString {
107 t.Errorf("mismatched error string %v", errs[0].Error())
108 }
109
110 newMinReadySeconds := int32(50)
111
112 validPs := &apps.StatefulSet{
113 ObjectMeta: metav1.ObjectMeta{Name: ps.Name, Namespace: ps.Namespace, ResourceVersion: "1", Generation: 1},
114 Spec: apps.StatefulSetSpec{
115 PodManagementPolicy: apps.OrderedReadyPodManagement,
116 Selector: ps.Spec.Selector,
117 Template: validPodTemplate.Template,
118 UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
119 MinReadySeconds: newMinReadySeconds,
120 },
121 Status: apps.StatefulSetStatus{Replicas: 4},
122 }
123 Strategy.PrepareForUpdate(ctx, validPs, ps)
124 errs = Strategy.ValidateUpdate(ctx, validPs, ps)
125 if len(errs) != 0 {
126 t.Errorf("updating spec.Replicas and minReadySeconds is allowed on a statefulset: %v", errs)
127 }
128 invalidPs := ps
129 invalidPs.Spec.MinReadySeconds = int32(-1)
130 Strategy.PrepareForUpdate(ctx, validPs, invalidPs)
131 errs = Strategy.ValidateUpdate(ctx, validPs, ps)
132 if len(errs) != 0 {
133 t.Errorf("updating spec.Replicas and minReadySeconds is allowed on a statefulset: %v", errs)
134 }
135 if validPs.Spec.MinReadySeconds != newMinReadySeconds {
136 t.Errorf("expected minReadySeconds to not be changed %v", errs)
137 }
138 })
139
140 validPs = &apps.StatefulSet{
141 ObjectMeta: metav1.ObjectMeta{Name: ps.Name, Namespace: ps.Namespace, ResourceVersion: "1", Generation: 1},
142 Spec: apps.StatefulSetSpec{
143 PodManagementPolicy: apps.OrderedReadyPodManagement,
144 Selector: ps.Spec.Selector,
145 Template: validPodTemplate.Template,
146 UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
147 PersistentVolumeClaimRetentionPolicy: &apps.StatefulSetPersistentVolumeClaimRetentionPolicy{
148 WhenDeleted: apps.RetainPersistentVolumeClaimRetentionPolicyType,
149 WhenScaled: apps.DeletePersistentVolumeClaimRetentionPolicyType,
150 },
151 },
152 Status: apps.StatefulSetStatus{Replicas: 4},
153 }
154
155 t.Run("when StatefulSetAutoDeletePVC feature gate is enabled, PersistentVolumeClaimRetentionPolicy should be updated", func(t *testing.T) {
156 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StatefulSetAutoDeletePVC, true)()
157
158 ps := &apps.StatefulSet{
159 ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
160 Spec: apps.StatefulSetSpec{
161 PodManagementPolicy: apps.OrderedReadyPodManagement,
162 Selector: ps.Spec.Selector,
163 Template: validPodTemplate.Template,
164 UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
165 PersistentVolumeClaimRetentionPolicy: &apps.StatefulSetPersistentVolumeClaimRetentionPolicy{
166 WhenDeleted: apps.PersistentVolumeClaimRetentionPolicyType("invalid policy"),
167 },
168 },
169 }
170 Strategy.PrepareForCreate(ctx, ps)
171 errs := Strategy.Validate(ctx, ps)
172 if len(errs) == 0 {
173 t.Errorf("expected failure when PersistentVolumeClaimRetentionPolicy is invalid")
174 }
175 expectedCreateErrorString := "spec.persistentVolumeClaimRetentionPolicy.whenDeleted: Unsupported value: \"invalid policy\": supported values: \"Retain\", \"Delete\""
176 if errs[0].Error() != expectedCreateErrorString {
177 t.Errorf("mismatched error string %v (expected %v)", errs[0].Error(), expectedCreateErrorString)
178 }
179 Strategy.PrepareForUpdate(ctx, validPs, ps)
180 errs = Strategy.ValidateUpdate(ctx, validPs, ps)
181 if len(errs) != 0 {
182 t.Errorf("updates to PersistentVolumeClaimRetentionPolicy should be allowed: %v", errs)
183 }
184 invalidPs := ps
185 invalidPs.Spec.PersistentVolumeClaimRetentionPolicy.WhenDeleted = apps.PersistentVolumeClaimRetentionPolicyType("invalid type")
186 Strategy.PrepareForUpdate(ctx, validPs, invalidPs)
187 errs = Strategy.ValidateUpdate(ctx, validPs, ps)
188 if len(errs) != 0 {
189 t.Errorf("invalid updates to PersistentVolumeClaimRetentionPolicyType should be allowed: %v", errs)
190 }
191 if validPs.Spec.PersistentVolumeClaimRetentionPolicy.WhenDeleted != apps.RetainPersistentVolumeClaimRetentionPolicyType || validPs.Spec.PersistentVolumeClaimRetentionPolicy.WhenScaled != apps.DeletePersistentVolumeClaimRetentionPolicyType {
192 t.Errorf("expected PersistentVolumeClaimRetentionPolicy to be updated: %v", errs)
193 }
194 })
195 t.Run("when StatefulSetAutoDeletePVC feature gate is disabled, PersistentVolumeClaimRetentionPolicy should not be updated", func(t *testing.T) {
196 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StatefulSetAutoDeletePVC, true)()
197
198 ps := &apps.StatefulSet{
199 ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault},
200 Spec: apps.StatefulSetSpec{
201 PodManagementPolicy: apps.OrderedReadyPodManagement,
202 Selector: ps.Spec.Selector,
203 Template: validPodTemplate.Template,
204 UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
205 PersistentVolumeClaimRetentionPolicy: &apps.StatefulSetPersistentVolumeClaimRetentionPolicy{
206 WhenDeleted: apps.RetainPersistentVolumeClaimRetentionPolicyType,
207 WhenScaled: apps.DeletePersistentVolumeClaimRetentionPolicyType,
208 },
209 },
210 }
211 Strategy.PrepareForCreate(ctx, ps)
212 errs := Strategy.Validate(ctx, ps)
213 if len(errs) != 0 {
214 t.Errorf("unexpected failure with PersistentVolumeClaimRetentionPolicy: %v", errs)
215 }
216 if ps.Spec.PersistentVolumeClaimRetentionPolicy.WhenDeleted != apps.RetainPersistentVolumeClaimRetentionPolicyType || ps.Spec.PersistentVolumeClaimRetentionPolicy.WhenScaled != apps.DeletePersistentVolumeClaimRetentionPolicyType {
217 t.Errorf("expected invalid PersistentVolumeClaimRetentionPolicy to be defaulted to Retain, but got %v", ps.Spec.PersistentVolumeClaimRetentionPolicy)
218 }
219 Strategy.PrepareForUpdate(ctx, validPs, ps)
220 errs = Strategy.ValidateUpdate(ctx, validPs, ps)
221 if len(errs) != 0 {
222 t.Errorf("updates to PersistentVolumeClaimRetentionPolicy should be allowed: %v", errs)
223 }
224 invalidPs := ps
225 invalidPs.Spec.PersistentVolumeClaimRetentionPolicy.WhenDeleted = apps.PersistentVolumeClaimRetentionPolicyType("invalid type")
226 Strategy.PrepareForUpdate(ctx, validPs, invalidPs)
227 errs = Strategy.ValidateUpdate(ctx, validPs, ps)
228 if len(errs) != 0 {
229 t.Errorf("should ignore updates to PersistentVolumeClaimRetentionPolicyType")
230 }
231 })
232
233 validPs.Spec.Selector = &metav1.LabelSelector{MatchLabels: map[string]string{"a": "bar"}}
234 Strategy.PrepareForUpdate(ctx, validPs, ps)
235 errs = Strategy.ValidateUpdate(ctx, validPs, ps)
236 if len(errs) == 0 {
237 t.Errorf("expected a validation error since updates are disallowed on statefulsets.")
238 }
239 }
240
241 func TestStatefulSetStatusStrategy(t *testing.T) {
242 ctx := genericapirequest.NewDefaultContext()
243 if !StatusStrategy.NamespaceScoped() {
244 t.Errorf("StatefulSet must be namespace scoped")
245 }
246 if StatusStrategy.AllowCreateOnUpdate() {
247 t.Errorf("StatefulSet should not allow create on update")
248 }
249 validSelector := map[string]string{"a": "b"}
250 validPodTemplate := api.PodTemplate{
251 Template: api.PodTemplateSpec{
252 ObjectMeta: metav1.ObjectMeta{
253 Labels: validSelector,
254 },
255 Spec: api.PodSpec{
256 RestartPolicy: api.RestartPolicyAlways,
257 DNSPolicy: api.DNSClusterFirst,
258 Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
259 },
260 },
261 }
262 oldPS := &apps.StatefulSet{
263 ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault, ResourceVersion: "10"},
264 Spec: apps.StatefulSetSpec{
265 Replicas: 3,
266 Selector: &metav1.LabelSelector{MatchLabels: validSelector},
267 Template: validPodTemplate.Template,
268 UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
269 },
270 Status: apps.StatefulSetStatus{
271 Replicas: 1,
272 },
273 }
274 newPS := &apps.StatefulSet{
275 ObjectMeta: metav1.ObjectMeta{Name: "abc", Namespace: metav1.NamespaceDefault, ResourceVersion: "9"},
276 Spec: apps.StatefulSetSpec{
277 Replicas: 1,
278 Selector: &metav1.LabelSelector{MatchLabels: validSelector},
279 Template: validPodTemplate.Template,
280 UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
281 },
282 Status: apps.StatefulSetStatus{
283 Replicas: 2,
284 },
285 }
286 StatusStrategy.PrepareForUpdate(ctx, newPS, oldPS)
287 if newPS.Status.Replicas != 2 {
288 t.Errorf("StatefulSet status updates should allow change of pods: %v", newPS.Status.Replicas)
289 }
290 if newPS.Spec.Replicas != 3 {
291 t.Errorf("StatefulSet status updates should not clobber spec: %v", newPS.Spec)
292 }
293 errs := StatusStrategy.ValidateUpdate(ctx, newPS, oldPS)
294 if len(errs) != 0 {
295 t.Errorf("unexpected error %v", errs)
296 }
297 }
298
299
300 func generateStatefulSetWithMinReadySeconds(minReadySeconds int32) *apps.StatefulSet {
301 return &apps.StatefulSet{
302 Spec: apps.StatefulSetSpec{
303 MinReadySeconds: minReadySeconds,
304 },
305 }
306 }
307
308 func makeStatefulSetWithMaxUnavailable(maxUnavailable *int) *apps.StatefulSet {
309 rollingUpdate := apps.RollingUpdateStatefulSetStrategy{}
310 if maxUnavailable != nil {
311 maxUnavailableIntStr := intstr.FromInt32(int32(*maxUnavailable))
312 rollingUpdate = apps.RollingUpdateStatefulSetStrategy{
313 MaxUnavailable: &maxUnavailableIntStr,
314 }
315 }
316
317 return &apps.StatefulSet{
318 Spec: apps.StatefulSetSpec{
319 UpdateStrategy: apps.StatefulSetUpdateStrategy{
320 Type: apps.RollingUpdateStatefulSetStrategyType,
321 RollingUpdate: &rollingUpdate,
322 },
323 },
324 }
325 }
326
327 func getMaxUnavailable(maxUnavailable int) *int {
328 return &maxUnavailable
329 }
330
331 func createOrdinalsWithStart(start int) *apps.StatefulSetOrdinals {
332 return &apps.StatefulSetOrdinals{
333 Start: int32(start),
334 }
335 }
336
337 func makeStatefulSetWithStatefulSetOrdinals(ordinals *apps.StatefulSetOrdinals) *apps.StatefulSet {
338 validSelector := map[string]string{"a": "b"}
339 validPodTemplate := api.PodTemplate{
340 Template: api.PodTemplateSpec{
341 ObjectMeta: metav1.ObjectMeta{
342 Labels: validSelector,
343 },
344 Spec: api.PodSpec{
345 RestartPolicy: api.RestartPolicyAlways,
346 DNSPolicy: api.DNSClusterFirst,
347 Containers: []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"}},
348 },
349 },
350 }
351 return &apps.StatefulSet{
352 ObjectMeta: metav1.ObjectMeta{Name: "ss", Namespace: metav1.NamespaceDefault},
353 Spec: apps.StatefulSetSpec{
354 Ordinals: ordinals,
355 Selector: &metav1.LabelSelector{MatchLabels: validSelector},
356 Template: validPodTemplate.Template,
357 UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
358 PodManagementPolicy: apps.OrderedReadyPodManagement,
359 },
360 }
361 }
362
363
364 func TestDropStatefulSetDisabledFields(t *testing.T) {
365 testCases := []struct {
366 name string
367 enableMaxUnavailable bool
368 enableStatefulSetStartOrdinal bool
369 ss *apps.StatefulSet
370 oldSS *apps.StatefulSet
371 expectedSS *apps.StatefulSet
372 }{
373 {
374 name: "set minReadySeconds, no update",
375 ss: generateStatefulSetWithMinReadySeconds(10),
376 oldSS: generateStatefulSetWithMinReadySeconds(20),
377 expectedSS: generateStatefulSetWithMinReadySeconds(10),
378 },
379 {
380 name: "set minReadySeconds, oldSS field set to nil",
381 ss: generateStatefulSetWithMinReadySeconds(10),
382 oldSS: nil,
383 expectedSS: generateStatefulSetWithMinReadySeconds(10),
384 },
385 {
386 name: "set minReadySeconds, oldSS field is set to 0",
387 ss: generateStatefulSetWithMinReadySeconds(10),
388 oldSS: generateStatefulSetWithMinReadySeconds(0),
389 expectedSS: generateStatefulSetWithMinReadySeconds(10),
390 },
391 {
392 name: "MaxUnavailable not enabled, field not used",
393 ss: makeStatefulSetWithMaxUnavailable(nil),
394 oldSS: nil,
395 expectedSS: makeStatefulSetWithMaxUnavailable(nil),
396 },
397 {
398 name: "MaxUnavailable not enabled, field used in new, not in old",
399 enableMaxUnavailable: false,
400 ss: makeStatefulSetWithMaxUnavailable(getMaxUnavailable(3)),
401 oldSS: nil,
402 expectedSS: makeStatefulSetWithMaxUnavailable(nil),
403 },
404 {
405 name: "MaxUnavailable not enabled, field used in old and new",
406 enableMaxUnavailable: false,
407 ss: makeStatefulSetWithMaxUnavailable(getMaxUnavailable(3)),
408 oldSS: makeStatefulSetWithMaxUnavailable(getMaxUnavailable(3)),
409 expectedSS: makeStatefulSetWithMaxUnavailable(getMaxUnavailable(3)),
410 },
411 {
412 name: "MaxUnavailable enabled, field used in new only",
413 enableMaxUnavailable: true,
414 ss: makeStatefulSetWithMaxUnavailable(getMaxUnavailable(3)),
415 oldSS: nil,
416 expectedSS: makeStatefulSetWithMaxUnavailable(getMaxUnavailable(3)),
417 },
418 {
419 name: "MaxUnavailable enabled, field used in both old and new",
420 enableMaxUnavailable: true,
421 ss: makeStatefulSetWithMaxUnavailable(getMaxUnavailable(1)),
422 oldSS: makeStatefulSetWithMaxUnavailable(getMaxUnavailable(3)),
423 expectedSS: makeStatefulSetWithMaxUnavailable(getMaxUnavailable(1)),
424 }, {
425 name: "StatefulSetStartOrdinal disabled, ordinals in use in new only",
426 enableStatefulSetStartOrdinal: false,
427 ss: makeStatefulSetWithStatefulSetOrdinals(createOrdinalsWithStart(2)),
428 oldSS: nil,
429 expectedSS: makeStatefulSetWithStatefulSetOrdinals(nil),
430 },
431 {
432 name: "StatefulSetStartOrdinal disabled, ordinals in use in both old and new",
433 enableStatefulSetStartOrdinal: false,
434 ss: makeStatefulSetWithStatefulSetOrdinals(createOrdinalsWithStart(2)),
435 oldSS: makeStatefulSetWithStatefulSetOrdinals(createOrdinalsWithStart(1)),
436 expectedSS: makeStatefulSetWithStatefulSetOrdinals(createOrdinalsWithStart(2)),
437 },
438 {
439 name: "StatefulSetStartOrdinal enabled, ordinals in use in new only",
440 enableStatefulSetStartOrdinal: true,
441 ss: makeStatefulSetWithStatefulSetOrdinals(createOrdinalsWithStart(2)),
442 oldSS: nil,
443 expectedSS: makeStatefulSetWithStatefulSetOrdinals(createOrdinalsWithStart(2)),
444 },
445 {
446 name: "StatefulSetStartOrdinal enabled, ordinals in use in both old and new",
447 enableStatefulSetStartOrdinal: true,
448 ss: makeStatefulSetWithStatefulSetOrdinals(createOrdinalsWithStart(2)),
449 oldSS: makeStatefulSetWithStatefulSetOrdinals(createOrdinalsWithStart(1)),
450 expectedSS: makeStatefulSetWithStatefulSetOrdinals(createOrdinalsWithStart(2)),
451 },
452 }
453 for _, tc := range testCases {
454 t.Run(tc.name, func(t *testing.T) {
455 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MaxUnavailableStatefulSet, tc.enableMaxUnavailable)()
456 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StatefulSetStartOrdinal, tc.enableStatefulSetStartOrdinal)()
457 old := tc.oldSS.DeepCopy()
458
459 dropStatefulSetDisabledFields(tc.ss, tc.oldSS)
460
461
462 if diff := cmp.Diff(tc.oldSS, old); diff != "" {
463 t.Fatalf("%v: old statefulSet changed: %v", tc.name, diff)
464 }
465
466 if diff := cmp.Diff(tc.expectedSS, tc.ss); diff != "" {
467 t.Fatalf("%v: unexpected statefulSet spec: %v, want %v, got %v", tc.name, diff, tc.expectedSS, tc.ss)
468 }
469 })
470 }
471 }
472
473 func TestStatefulSetStartOrdinalEnablement(t *testing.T) {
474 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StatefulSetStartOrdinal, true)()
475 ss := makeStatefulSetWithStatefulSetOrdinals(createOrdinalsWithStart(2))
476 expectedSS := makeStatefulSetWithStatefulSetOrdinals(createOrdinalsWithStart(2))
477 ss.Spec.Replicas = 1
478
479 ctx := genericapirequest.NewDefaultContext()
480 Strategy.PrepareForCreate(ctx, ss)
481
482 if diff := cmp.Diff(expectedSS.Spec.Ordinals, ss.Spec.Ordinals); diff != "" {
483 t.Fatalf("Strategy.PrepareForCreate(%v) unexpected .spec.ordinals change: (-want, +got):\n%v", ss, diff)
484 }
485
486 errs := Strategy.Validate(ctx, ss)
487 if len(errs) != 0 {
488 t.Errorf("Strategy.Validate(%v) returned error: %v", ss, errs)
489 }
490
491 if ss.Generation != 1 {
492 t.Errorf("Generation = %v, want = 1 for StatefulSet: %v", ss.Generation, ss)
493 }
494
495
496 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StatefulSetStartOrdinal, false)()
497 ssWhenDisabled := makeStatefulSetWithStatefulSetOrdinals(createOrdinalsWithStart(2))
498 ssWhenDisabled.Spec.Replicas = 2
499
500 Strategy.PrepareForUpdate(ctx, ssWhenDisabled, ss)
501
502 if diff := cmp.Diff(expectedSS.Spec.Ordinals, ssWhenDisabled.Spec.Ordinals); diff != "" {
503 t.Fatalf("Strategy.PrepareForUpdate(%v) unexpected .spec.ordinals change: (-want, +got):\n%v", ssWhenDisabled, diff)
504 }
505
506 errs = Strategy.Validate(ctx, ssWhenDisabled)
507 if len(errs) != 0 {
508 t.Errorf("Strategy.Validate(%v) returned error: %v", ssWhenDisabled, errs)
509 }
510
511 if ssWhenDisabled.Generation != 2 {
512 t.Errorf("Generation = %v, want = 2 for StatefulSet: %v", ssWhenDisabled.Generation, ssWhenDisabled)
513 }
514
515
516 defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.StatefulSetStartOrdinal, false)()
517 ssWhenEnabled := makeStatefulSetWithStatefulSetOrdinals(createOrdinalsWithStart(2))
518 ssWhenEnabled.Spec.Replicas = 3
519
520 Strategy.PrepareForUpdate(ctx, ssWhenEnabled, ssWhenDisabled)
521
522 if diff := cmp.Diff(expectedSS.Spec.Ordinals, ssWhenEnabled.Spec.Ordinals); diff != "" {
523 t.Fatalf("Strategy.PrepareForUpdate(%v) unexpected .spec.ordinals change: (-want, +got):\n%v", ssWhenEnabled, diff)
524 }
525
526 errs = Strategy.Validate(ctx, ssWhenEnabled)
527 if len(errs) != 0 {
528 t.Errorf("Strategy.Validate(%v) returned error: %v", ssWhenEnabled, errs)
529 }
530
531 if ssWhenEnabled.Generation != 3 {
532 t.Errorf("Generation = %v, want = 3 for StatefulSet: %v", ssWhenEnabled.Generation, ssWhenEnabled)
533 }
534 }
535
View as plain text