1
16
17 package polymorphichelpers
18
19 import (
20 "fmt"
21 "testing"
22
23 apps "k8s.io/api/apps/v1"
24 api "k8s.io/api/core/v1"
25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
26 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
27 "k8s.io/apimachinery/pkg/runtime"
28 )
29
30 func TestDeploymentStatusViewerStatus(t *testing.T) {
31 tests := []struct {
32 name string
33 generation int64
34 specReplicas int32
35 status apps.DeploymentStatus
36 msg string
37 done bool
38 }{
39 {
40 name: "test1",
41 generation: 0,
42 specReplicas: 1,
43 status: apps.DeploymentStatus{
44 ObservedGeneration: 1,
45 Replicas: 1,
46 UpdatedReplicas: 0,
47 AvailableReplicas: 1,
48 UnavailableReplicas: 0,
49 },
50
51 msg: "Waiting for deployment \"foo\" rollout to finish: 0 out of 1 new replicas have been updated...\n",
52 done: false,
53 },
54 {
55 name: "test2",
56 generation: 1,
57 specReplicas: 1,
58 status: apps.DeploymentStatus{
59 ObservedGeneration: 1,
60 Replicas: 2,
61 UpdatedReplicas: 1,
62 AvailableReplicas: 2,
63 UnavailableReplicas: 0,
64 },
65
66 msg: "Waiting for deployment \"foo\" rollout to finish: 1 old replicas are pending termination...\n",
67 done: false,
68 },
69 {
70 name: "test3",
71 generation: 1,
72 specReplicas: 2,
73 status: apps.DeploymentStatus{
74 ObservedGeneration: 1,
75 Replicas: 2,
76 UpdatedReplicas: 2,
77 AvailableReplicas: 1,
78 UnavailableReplicas: 1,
79 },
80
81 msg: "Waiting for deployment \"foo\" rollout to finish: 1 of 2 updated replicas are available...\n",
82 done: false,
83 },
84 {
85 name: "test4",
86 generation: 1,
87 specReplicas: 2,
88 status: apps.DeploymentStatus{
89 ObservedGeneration: 1,
90 Replicas: 2,
91 UpdatedReplicas: 2,
92 AvailableReplicas: 2,
93 UnavailableReplicas: 0,
94 },
95
96 msg: "deployment \"foo\" successfully rolled out\n",
97 done: true,
98 },
99 {
100 name: "test5",
101 generation: 2,
102 specReplicas: 2,
103 status: apps.DeploymentStatus{
104 ObservedGeneration: 1,
105 Replicas: 2,
106 UpdatedReplicas: 2,
107 AvailableReplicas: 2,
108 UnavailableReplicas: 0,
109 },
110
111 msg: "Waiting for deployment spec update to be observed...\n",
112 done: false,
113 },
114 }
115
116 for _, test := range tests {
117 t.Run(test.name, func(t *testing.T) {
118 d := &apps.Deployment{
119 ObjectMeta: metav1.ObjectMeta{
120 Namespace: "bar",
121 Name: "foo",
122 UID: "8764ae47-9092-11e4-8393-42010af018ff",
123 Generation: test.generation,
124 },
125 Spec: apps.DeploymentSpec{
126 Replicas: &test.specReplicas,
127 },
128 Status: test.status,
129 }
130 unstructuredD := &unstructured.Unstructured{}
131 var err error
132 unstructuredD.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(d)
133 if err != nil {
134 t.Fatal(err)
135 }
136
137 dsv := &DeploymentStatusViewer{}
138 msg, done, err := dsv.Status(unstructuredD, 0)
139 if err != nil {
140 t.Fatalf("DeploymentStatusViewer.Status(): %v", err)
141 }
142 if done != test.done || msg != test.msg {
143 t.Errorf("DeploymentStatusViewer.Status() for deployment with generation %d, %d replicas specified, and status %+v returned %q, %t, want %q, %t",
144 test.generation,
145 test.specReplicas,
146 test.status,
147 msg,
148 done,
149 test.msg,
150 test.done,
151 )
152 }
153 })
154 }
155 }
156
157 func TestDaemonSetStatusViewerStatus(t *testing.T) {
158 tests := []struct {
159 name string
160 generation int64
161 status apps.DaemonSetStatus
162 msg string
163 done bool
164 }{
165 {
166 name: "test1",
167 generation: 0,
168 status: apps.DaemonSetStatus{
169 ObservedGeneration: 1,
170 UpdatedNumberScheduled: 0,
171 DesiredNumberScheduled: 1,
172 NumberAvailable: 0,
173 },
174
175 msg: "Waiting for daemon set \"foo\" rollout to finish: 0 out of 1 new pods have been updated...\n",
176 done: false,
177 },
178 {
179 name: "test2",
180 generation: 1,
181 status: apps.DaemonSetStatus{
182 ObservedGeneration: 1,
183 UpdatedNumberScheduled: 2,
184 DesiredNumberScheduled: 2,
185 NumberAvailable: 1,
186 },
187
188 msg: "Waiting for daemon set \"foo\" rollout to finish: 1 of 2 updated pods are available...\n",
189 done: false,
190 },
191 {
192 name: "test3",
193 generation: 1,
194 status: apps.DaemonSetStatus{
195 ObservedGeneration: 1,
196 UpdatedNumberScheduled: 2,
197 DesiredNumberScheduled: 2,
198 NumberAvailable: 2,
199 },
200
201 msg: "daemon set \"foo\" successfully rolled out\n",
202 done: true,
203 },
204 {
205 name: "test4",
206 generation: 2,
207 status: apps.DaemonSetStatus{
208 ObservedGeneration: 1,
209 UpdatedNumberScheduled: 2,
210 DesiredNumberScheduled: 2,
211 NumberAvailable: 2,
212 },
213
214 msg: "Waiting for daemon set spec update to be observed...\n",
215 done: false,
216 },
217 }
218
219 for _, test := range tests {
220 t.Run(test.name, func(t *testing.T) {
221 d := &apps.DaemonSet{
222 ObjectMeta: metav1.ObjectMeta{
223 Namespace: "bar",
224 Name: "foo",
225 UID: "8764ae47-9092-11e4-8393-42010af018ff",
226 Generation: test.generation,
227 },
228 Spec: apps.DaemonSetSpec{
229 UpdateStrategy: apps.DaemonSetUpdateStrategy{
230 Type: apps.RollingUpdateDaemonSetStrategyType,
231 },
232 },
233 Status: test.status,
234 }
235
236 unstructuredD := &unstructured.Unstructured{}
237 var err error
238 unstructuredD.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(d)
239 if err != nil {
240 t.Fatal(err)
241 }
242
243 dsv := &DaemonSetStatusViewer{}
244 msg, done, err := dsv.Status(unstructuredD, 0)
245 if err != nil {
246 t.Fatalf("unexpected error: %v", err)
247 }
248 if done != test.done || msg != test.msg {
249 t.Errorf("daemon set with generation %d, %d pods specified, and status:\n%+v\nreturned:\n%q, %t\nwant:\n%q, %t",
250 test.generation,
251 d.Status.DesiredNumberScheduled,
252 test.status,
253 msg,
254 done,
255 test.msg,
256 test.done,
257 )
258 }
259 })
260 }
261 }
262
263 func TestStatefulSetStatusViewerStatus(t *testing.T) {
264 tests := []struct {
265 name string
266 generation int64
267 strategy apps.StatefulSetUpdateStrategy
268 status apps.StatefulSetStatus
269 msg string
270 done bool
271 err bool
272 }{
273 {
274 name: "on delete returns an error",
275 generation: 1,
276 strategy: apps.StatefulSetUpdateStrategy{Type: apps.OnDeleteStatefulSetStrategyType},
277 status: apps.StatefulSetStatus{
278 ObservedGeneration: 1,
279 Replicas: 0,
280 ReadyReplicas: 1,
281 CurrentReplicas: 0,
282 UpdatedReplicas: 0,
283 },
284
285 msg: "",
286 done: true,
287 err: true,
288 },
289 {
290 name: "unobserved update is not complete",
291 generation: 2,
292 strategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
293 status: apps.StatefulSetStatus{
294 ObservedGeneration: 1,
295 Replicas: 3,
296 ReadyReplicas: 3,
297 CurrentReplicas: 3,
298 UpdatedReplicas: 0,
299 },
300
301 msg: "Waiting for statefulset spec update to be observed...\n",
302 done: false,
303 err: false,
304 },
305 {
306 name: "if all pods are not ready the update is not complete",
307 generation: 1,
308 strategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
309 status: apps.StatefulSetStatus{
310 ObservedGeneration: 2,
311 Replicas: 3,
312 ReadyReplicas: 2,
313 CurrentReplicas: 3,
314 UpdatedReplicas: 0,
315 },
316
317 msg: fmt.Sprintf("Waiting for %d pods to be ready...\n", 1),
318 done: false,
319 err: false,
320 },
321 {
322 name: "partition update completes when all replicas above the partition are updated",
323 generation: 1,
324 strategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType,
325 RollingUpdate: func() *apps.RollingUpdateStatefulSetStrategy {
326 partition := int32(2)
327 return &apps.RollingUpdateStatefulSetStrategy{Partition: &partition}
328 }()},
329 status: apps.StatefulSetStatus{
330 ObservedGeneration: 2,
331 Replicas: 3,
332 ReadyReplicas: 3,
333 CurrentReplicas: 2,
334 UpdatedReplicas: 1,
335 },
336
337 msg: fmt.Sprintf("partitioned roll out complete: %d new pods have been updated...\n", 1),
338 done: true,
339 err: false,
340 },
341 {
342 name: "partition update is in progress if all pods above the partition have not been updated",
343 generation: 1,
344 strategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType,
345 RollingUpdate: func() *apps.RollingUpdateStatefulSetStrategy {
346 partition := int32(2)
347 return &apps.RollingUpdateStatefulSetStrategy{Partition: &partition}
348 }()},
349 status: apps.StatefulSetStatus{
350 ObservedGeneration: 2,
351 Replicas: 3,
352 ReadyReplicas: 3,
353 CurrentReplicas: 3,
354 UpdatedReplicas: 0,
355 },
356
357 msg: fmt.Sprintf("Waiting for partitioned roll out to finish: %d out of %d new pods have been updated...\n", 0, 1),
358 done: true,
359 err: false,
360 },
361 {
362 name: "update completes when all replicas are current",
363 generation: 1,
364 strategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
365 status: apps.StatefulSetStatus{
366 ObservedGeneration: 2,
367 Replicas: 3,
368 ReadyReplicas: 3,
369 CurrentReplicas: 3,
370 UpdatedReplicas: 3,
371 CurrentRevision: "foo",
372 UpdateRevision: "foo",
373 },
374
375 msg: fmt.Sprintf("statefulset rolling update complete %d pods at revision %s...\n", 3, "foo"),
376 done: true,
377 err: false,
378 },
379 }
380
381 for _, test := range tests {
382 t.Run(test.name, func(t *testing.T) {
383 s := newStatefulSet(3)
384 s.Status = test.status
385 s.Spec.UpdateStrategy = test.strategy
386 s.Generation = test.generation
387
388 unstructuredS := &unstructured.Unstructured{}
389 var err error
390 unstructuredS.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(s)
391 if err != nil {
392 t.Fatal(err)
393 }
394
395 dsv := &StatefulSetStatusViewer{}
396 msg, done, err := dsv.Status(unstructuredS, 0)
397 if test.err && err == nil {
398 t.Fatalf("%s: expected error", test.name)
399 }
400 if !test.err && err != nil {
401 t.Fatalf("%s: %s", test.name, err)
402 }
403 if done && !test.done {
404 t.Errorf("%s: want done %v got %v", test.name, done, test.done)
405 }
406 if msg != test.msg {
407 t.Errorf("%s: want message %s got %s", test.name, test.msg, msg)
408 }
409 })
410 }
411 }
412
413 func TestDaemonSetStatusViewerStatusWithWrongUpdateStrategyType(t *testing.T) {
414 d := &apps.DaemonSet{
415 ObjectMeta: metav1.ObjectMeta{
416 Namespace: "bar",
417 Name: "foo",
418 UID: "8764ae47-9092-11e4-8393-42010af018ff",
419 },
420 Spec: apps.DaemonSetSpec{
421 UpdateStrategy: apps.DaemonSetUpdateStrategy{
422 Type: apps.OnDeleteDaemonSetStrategyType,
423 },
424 },
425 }
426
427 unstructuredD := &unstructured.Unstructured{}
428 var err error
429 unstructuredD.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(d)
430 if err != nil {
431 t.Fatal(err)
432 }
433
434 dsv := &DaemonSetStatusViewer{}
435 msg, done, err := dsv.Status(unstructuredD, 0)
436 errMsg := "rollout status is only available for RollingUpdate strategy type"
437 if err == nil || err.Error() != errMsg {
438 t.Errorf("Status for daemon sets with UpdateStrategy type different than RollingUpdate should return error. Instead got: msg: %s\ndone: %t\n err: %v", msg, done, err)
439 }
440 }
441
442 func newStatefulSet(replicas int32) *apps.StatefulSet {
443 return &apps.StatefulSet{
444 ObjectMeta: metav1.ObjectMeta{
445 Name: "foo",
446 Namespace: metav1.NamespaceDefault,
447 Labels: map[string]string{"a": "b"},
448 },
449 Spec: apps.StatefulSetSpec{
450 PodManagementPolicy: apps.OrderedReadyPodManagement,
451 Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
452 Template: api.PodTemplateSpec{
453 ObjectMeta: metav1.ObjectMeta{
454 Labels: map[string]string{"a": "b"},
455 },
456 Spec: api.PodSpec{
457 Containers: []api.Container{
458 {
459 Name: "test",
460 Image: "test_image",
461 ImagePullPolicy: api.PullIfNotPresent,
462 },
463 },
464 RestartPolicy: api.RestartPolicyAlways,
465 DNSPolicy: api.DNSClusterFirst,
466 },
467 },
468 Replicas: &replicas,
469 UpdateStrategy: apps.StatefulSetUpdateStrategy{Type: apps.RollingUpdateStatefulSetStrategyType},
470 },
471 Status: apps.StatefulSetStatus{},
472 }
473 }
474
View as plain text