1
2
3
4 package status
5
6 import (
7 "fmt"
8 "testing"
9 "time"
10
11 "github.com/stretchr/testify/assert"
12 corev1 "k8s.io/api/core/v1"
13 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
14 "sigs.k8s.io/yaml"
15 )
16
17 func y2u(t *testing.T, spec string) *unstructured.Unstructured {
18 j, err := yaml.YAMLToJSON([]byte(spec))
19 assert.NoError(t, err)
20 u, _, err := unstructured.UnstructuredJSONScheme.Decode(j, nil, nil)
21 assert.NoError(t, err)
22 return u.(*unstructured.Unstructured)
23 }
24
25 type testSpec struct {
26 spec string
27 expectedStatus Status
28 expectedConditions []Condition
29 absentConditionTypes []ConditionType
30 }
31
32 func runStatusTest(t *testing.T, tc testSpec) {
33 res, err := Compute(y2u(t, tc.spec))
34 assert.NoError(t, err)
35 assert.Equal(t, tc.expectedStatus, res.Status)
36
37 for _, expectedCondition := range tc.expectedConditions {
38 found := false
39 for _, condition := range res.Conditions {
40 if condition.Type != expectedCondition.Type {
41 continue
42 }
43 found = true
44 assert.Equal(t, expectedCondition.Status, condition.Status)
45 assert.Equal(t, expectedCondition.Reason, condition.Reason)
46 }
47 if !found {
48 t.Errorf("Expected condition of type %s, but didn't find it", expectedCondition.Type)
49 }
50 }
51
52 for _, absentConditionType := range tc.absentConditionTypes {
53 for _, condition := range res.Conditions {
54 if condition.Type == absentConditionType {
55 t.Errorf("Expected condition %s to be absent, but found it", absentConditionType)
56 }
57 }
58 }
59 }
60
61 var podNoStatus = `
62 apiVersion: v1
63 kind: Pod
64 metadata:
65 generation: 1
66 name: test
67 `
68
69 var podReady = `
70 apiVersion: v1
71 kind: Pod
72 metadata:
73 generation: 1
74 name: test
75 namespace: qual
76 status:
77 conditions:
78 - type: Ready
79 status: "True"
80 phase: Running
81 `
82
83 var podCompletedOK = `
84 apiVersion: v1
85 kind: Pod
86 metadata:
87 generation: 1
88 name: test
89 namespace: qual
90 status:
91 phase: Succeeded
92 conditions:
93 - type: Ready
94 status: "False"
95 reason: PodCompleted
96
97 `
98
99 var podCompletedFail = `
100 apiVersion: v1
101 kind: Pod
102 metadata:
103 generation: 1
104 name: test
105 namespace: qual
106 status:
107 phase: Failed
108 conditions:
109 - type: Ready
110 status: "False"
111 reason: PodCompleted
112 `
113
114 var podBeingScheduled = `
115 apiVersion: v1
116 kind: Pod
117 metadata:
118 creationTimestamp: %s
119 generation: 1
120 name: test
121 namespace: qual
122 status:
123 phase: Pending
124 conditions:
125 - type: PodScheduled
126 status: "False"
127 reason: Unschedulable
128 `
129
130 var podUnschedulable = `
131 apiVersion: v1
132 kind: Pod
133 metadata:
134 generation: 1
135 name: test
136 namespace: qual
137 status:
138 phase: Pending
139 conditions:
140 - type: PodScheduled
141 status: "False"
142 reason: Unschedulable
143 `
144
145 var podCrashLooping = `
146 apiVersion: v1
147 kind: Pod
148 metadata:
149 generation: 1
150 name: test
151 namespace: qual
152 status:
153 phase: Running
154 conditions:
155 - type: PodScheduled
156 status: "False"
157 reason: Unschedulable
158 containerStatuses:
159 - name: nginx
160 state:
161 waiting:
162 reason: CrashLoopBackOff
163 `
164
165
166 func TestPodStatus(t *testing.T) {
167 testCases := map[string]testSpec{
168 "podNoStatus": {
169 spec: podNoStatus,
170 expectedStatus: InProgressStatus,
171 expectedConditions: []Condition{{
172 Type: ConditionReconciling,
173 Status: corev1.ConditionTrue,
174 Reason: "PodNotObserved",
175 }},
176 absentConditionTypes: []ConditionType{
177 ConditionStalled,
178 },
179 },
180 "podReady": {
181 spec: podReady,
182 expectedStatus: CurrentStatus,
183 expectedConditions: []Condition{},
184 absentConditionTypes: []ConditionType{
185 ConditionReconciling,
186 ConditionStalled,
187 },
188 },
189 "podCompletedSuccessfully": {
190 spec: podCompletedOK,
191 expectedStatus: CurrentStatus,
192 expectedConditions: []Condition{},
193 absentConditionTypes: []ConditionType{
194 ConditionReconciling,
195 ConditionStalled,
196 },
197 },
198 "podCompletedFailed": {
199 spec: podCompletedFail,
200 expectedStatus: CurrentStatus,
201 expectedConditions: []Condition{},
202 absentConditionTypes: []ConditionType{
203 ConditionReconciling,
204 ConditionStalled,
205 },
206 },
207 "podBeingScheduled": {
208 spec: fmt.Sprintf(podBeingScheduled, time.Now().Format(time.RFC3339)),
209 expectedStatus: InProgressStatus,
210 expectedConditions: []Condition{
211 {
212 Type: ConditionReconciling,
213 Status: corev1.ConditionTrue,
214 Reason: "PodNotScheduled",
215 },
216 },
217 absentConditionTypes: []ConditionType{
218 ConditionStalled,
219 },
220 },
221 "podUnschedulable": {
222 spec: podUnschedulable,
223 expectedStatus: FailedStatus,
224 expectedConditions: []Condition{
225 {
226 Type: ConditionStalled,
227 Status: corev1.ConditionTrue,
228 Reason: "PodUnschedulable",
229 },
230 },
231 absentConditionTypes: []ConditionType{
232 ConditionReconciling,
233 },
234 },
235 "podCrashLooping": {
236 spec: podCrashLooping,
237 expectedStatus: FailedStatus,
238 expectedConditions: []Condition{
239 {
240 Type: ConditionStalled,
241 Status: corev1.ConditionTrue,
242 Reason: "ContainerCrashLooping",
243 },
244 },
245 absentConditionTypes: []ConditionType{
246 ConditionReconciling,
247 },
248 },
249 }
250
251 for tn, tc := range testCases {
252 tc := tc
253 t.Run(tn, func(t *testing.T) {
254 runStatusTest(t, tc)
255 })
256 }
257 }
258
259 var pvcNoStatus = `
260 apiVersion: v1
261 kind: PersistentVolumeClaim
262 metadata:
263 generation: 1
264 name: test
265 `
266 var pvcBound = `
267 apiVersion: v1
268 kind: PersistentVolumeClaim
269 metadata:
270 generation: 1
271 name: test
272 namespace: qual
273 status:
274 phase: Bound
275 `
276
277 func TestPVCStatus(t *testing.T) {
278 testCases := map[string]testSpec{
279 "pvcNoStatus": {
280 spec: pvcNoStatus,
281 expectedStatus: InProgressStatus,
282 expectedConditions: []Condition{{
283 Type: ConditionReconciling,
284 Status: corev1.ConditionTrue,
285 Reason: "NotBound",
286 }},
287 absentConditionTypes: []ConditionType{
288 ConditionStalled,
289 },
290 },
291 "pvcBound": {
292 spec: pvcBound,
293 expectedStatus: CurrentStatus,
294 expectedConditions: []Condition{},
295 absentConditionTypes: []ConditionType{
296 ConditionStalled,
297 ConditionReconciling,
298 },
299 },
300 }
301
302 for tn, tc := range testCases {
303 tc := tc
304 t.Run(tn, func(t *testing.T) {
305 runStatusTest(t, tc)
306 })
307 }
308 }
309
310 var stsNoStatus = `
311 apiVersion: apps/v1
312 kind: StatefulSet
313 metadata:
314 generation: 1
315 name: test
316 `
317 var stsBadStatus = `
318 apiVersion: apps/v1
319 kind: StatefulSet
320 metadata:
321 generation: 1
322 name: test
323 namespace: qual
324 status:
325 observedGeneration: 1
326 currentReplicas: 1
327 `
328
329 var stsOK = `
330 apiVersion: apps/v1
331 kind: StatefulSet
332 metadata:
333 generation: 1
334 name: test
335 namespace: qual
336 spec:
337 replicas: 4
338 status:
339 observedGeneration: 1
340 currentReplicas: 4
341 readyReplicas: 4
342 replicas: 4
343 `
344
345 var stsLessReady = `
346 apiVersion: apps/v1
347 kind: StatefulSet
348 metadata:
349 generation: 1
350 name: test
351 namespace: qual
352 spec:
353 replicas: 4
354 status:
355 observedGeneration: 1
356 currentReplicas: 4
357 readyReplicas: 2
358 replicas: 4
359 `
360 var stsLessCurrent = `
361 apiVersion: apps/v1
362 kind: StatefulSet
363 metadata:
364 generation: 1
365 name: test
366 namespace: qual
367 spec:
368 replicas: 4
369 status:
370 observedGeneration: 1
371 currentReplicas: 2
372 readyReplicas: 4
373 replicas: 4
374 `
375 var stsExtraPods = `
376 apiVersion: apps/v1
377 kind: StatefulSet
378 metadata:
379 generation: 1
380 name: test
381 namespace: qual
382 spec:
383 replicas: 4
384 status:
385 observedGeneration: 1
386 currentReplicas: 4
387 readyReplicas: 4
388 replicas: 8
389 `
390
391 func TestStsStatus(t *testing.T) {
392 testCases := map[string]testSpec{
393 "stsNoStatus": {
394 spec: stsNoStatus,
395 expectedStatus: InProgressStatus,
396 expectedConditions: []Condition{{
397 Type: ConditionReconciling,
398 Status: corev1.ConditionTrue,
399 Reason: "LessReplicas",
400 }},
401 absentConditionTypes: []ConditionType{
402 ConditionStalled,
403 },
404 },
405 "stsBadStatus": {
406 spec: stsBadStatus,
407 expectedStatus: InProgressStatus,
408 expectedConditions: []Condition{{
409 Type: ConditionReconciling,
410 Status: corev1.ConditionTrue,
411 Reason: "LessReplicas",
412 }},
413 absentConditionTypes: []ConditionType{
414 ConditionStalled,
415 },
416 },
417 "stsOK": {
418 spec: stsOK,
419 expectedStatus: CurrentStatus,
420 expectedConditions: []Condition{},
421 absentConditionTypes: []ConditionType{
422 ConditionStalled,
423 ConditionReconciling,
424 },
425 },
426 "stsLessReady": {
427 spec: stsLessReady,
428 expectedStatus: InProgressStatus,
429 expectedConditions: []Condition{{
430 Type: ConditionReconciling,
431 Status: corev1.ConditionTrue,
432 Reason: "LessReady",
433 }},
434 absentConditionTypes: []ConditionType{
435 ConditionStalled,
436 },
437 },
438 "stsLessCurrent": {
439 spec: stsLessCurrent,
440 expectedStatus: InProgressStatus,
441 expectedConditions: []Condition{{
442 Type: ConditionReconciling,
443 Status: corev1.ConditionTrue,
444 Reason: "LessCurrent",
445 }},
446 absentConditionTypes: []ConditionType{
447 ConditionStalled,
448 },
449 },
450 "stsExtraPods": {
451 spec: stsExtraPods,
452 expectedStatus: InProgressStatus,
453 expectedConditions: []Condition{{
454 Type: ConditionReconciling,
455 Status: corev1.ConditionTrue,
456 Reason: "ExtraPods",
457 }},
458 absentConditionTypes: []ConditionType{
459 ConditionStalled,
460 },
461 },
462 }
463
464 for tn, tc := range testCases {
465 tc := tc
466 t.Run(tn, func(t *testing.T) {
467 runStatusTest(t, tc)
468 })
469 }
470 }
471
472 var dsNoStatus = `
473 apiVersion: apps/v1
474 kind: DaemonSet
475 metadata:
476 name: test
477 generation: 1
478 `
479 var dsBadStatus = `
480 apiVersion: apps/v1
481 kind: DaemonSet
482 metadata:
483 name: test
484 namespace: qual
485 generation: 1
486 status:
487 observedGeneration: 1
488 currentReplicas: 1
489 `
490
491 var dsOK = `
492 apiVersion: apps/v1
493 kind: DaemonSet
494 metadata:
495 name: test
496 namespace: qual
497 generation: 1
498 status:
499 desiredNumberScheduled: 4
500 currentNumberScheduled: 4
501 updatedNumberScheduled: 4
502 numberAvailable: 4
503 numberReady: 4
504 observedGeneration: 1
505 `
506 var dsDifferentGeneration = `
507 apiVersion: apps/v1
508 kind: DaemonSet
509 metadata:
510 name: test
511 namespace: qual
512 generation: 2
513 status:
514 desiredNumberScheduled: 4
515 currentNumberScheduled: 4
516 updatedNumberScheduled: 4
517 numberAvailable: 4
518 numberReady: 4
519 observedGeneration: 1
520 `
521
522 var dsLessReady = `
523 apiVersion: apps/v1
524 kind: DaemonSet
525 metadata:
526 name: test
527 namespace: qual
528 generation: 1
529 status:
530 observedGeneration: 1
531 desiredNumberScheduled: 4
532 currentNumberScheduled: 4
533 updatedNumberScheduled: 4
534 numberAvailable: 4
535 numberReady: 2
536 `
537 var dsLessAvailable = `
538 apiVersion: apps/v1
539 kind: DaemonSet
540 metadata:
541 name: test
542 namespace: qual
543 generation: 1
544 status:
545 observedGeneration: 1
546 desiredNumberScheduled: 4
547 currentNumberScheduled: 4
548 updatedNumberScheduled: 4
549 numberAvailable: 2
550 numberReady: 4
551 `
552
553 func TestDaemonsetStatus(t *testing.T) {
554 testCases := map[string]testSpec{
555 "dsNoStatus": {
556 spec: dsNoStatus,
557 expectedStatus: InProgressStatus,
558 expectedConditions: []Condition{{
559 Type: ConditionReconciling,
560 Status: corev1.ConditionTrue,
561 Reason: "NoObservedGeneration",
562 }},
563 absentConditionTypes: []ConditionType{
564 ConditionStalled,
565 },
566 },
567 "dsBadStatus": {
568 spec: dsBadStatus,
569 expectedStatus: InProgressStatus,
570 expectedConditions: []Condition{{
571 Type: ConditionReconciling,
572 Status: corev1.ConditionTrue,
573 Reason: "NoDesiredNumber",
574 }},
575 absentConditionTypes: []ConditionType{
576 ConditionStalled,
577 },
578 },
579 "dsOK": {
580 spec: dsOK,
581 expectedStatus: CurrentStatus,
582 expectedConditions: []Condition{},
583 absentConditionTypes: []ConditionType{
584 ConditionStalled,
585 ConditionReconciling,
586 },
587 },
588 "dsLessReady": {
589 spec: dsLessReady,
590 expectedStatus: InProgressStatus,
591 expectedConditions: []Condition{{
592 Type: ConditionReconciling,
593 Status: corev1.ConditionTrue,
594 Reason: "LessReady",
595 }},
596 absentConditionTypes: []ConditionType{
597 ConditionStalled,
598 },
599 },
600 "dsDifferentGeneration": {
601 spec: dsDifferentGeneration,
602 expectedStatus: InProgressStatus,
603 expectedConditions: []Condition{{
604 Type: ConditionReconciling,
605 Status: corev1.ConditionTrue,
606 Reason: "LatestGenerationNotObserved",
607 }},
608 absentConditionTypes: []ConditionType{
609 ConditionStalled,
610 },
611 },
612 "dsLessAvailable": {
613 spec: dsLessAvailable,
614 expectedStatus: InProgressStatus,
615 expectedConditions: []Condition{{
616 Type: ConditionReconciling,
617 Status: corev1.ConditionTrue,
618 Reason: "LessAvailable",
619 }},
620 absentConditionTypes: []ConditionType{
621 ConditionStalled,
622 },
623 },
624 }
625
626 for tn, tc := range testCases {
627 tc := tc
628 t.Run(tn, func(t *testing.T) {
629 runStatusTest(t, tc)
630 })
631 }
632 }
633
634 var depNoStatus = `
635 apiVersion: apps/v1
636 kind: Deployment
637 metadata:
638 name: test
639 generation: 1
640 `
641
642 var depOK = `
643 apiVersion: apps/v1
644 kind: Deployment
645 metadata:
646 name: test
647 generation: 1
648 namespace: qual
649 status:
650 observedGeneration: 1
651 updatedReplicas: 1
652 readyReplicas: 1
653 availableReplicas: 1
654 replicas: 1
655 conditions:
656 - type: Progressing
657 status: "True"
658 reason: NewReplicaSetAvailable
659 - type: Available
660 status: "True"
661 `
662
663 var depNotProgressing = `
664 apiVersion: apps/v1
665 kind: Deployment
666 metadata:
667 name: test
668 generation: 1
669 namespace: qual
670 spec:
671 progressDeadlineSeconds: 45
672 status:
673 observedGeneration: 1
674 updatedReplicas: 1
675 readyReplicas: 1
676 availableReplicas: 1
677 replicas: 1
678 observedGeneration: 1
679 conditions:
680 - type: Progressing
681 status: "False"
682 reason: Some reason
683 - type: Available
684 status: "True"
685 `
686
687 var depNoProgressDeadlineSeconds = `
688 apiVersion: apps/v1
689 kind: Deployment
690 metadata:
691 name: test
692 generation: 1
693 namespace: qual
694 status:
695 observedGeneration: 1
696 updatedReplicas: 1
697 readyReplicas: 1
698 availableReplicas: 1
699 replicas: 1
700 observedGeneration: 1
701 conditions:
702 - type: Available
703 status: "True"
704 `
705
706 var depNotAvailable = `
707 apiVersion: apps/v1
708 kind: Deployment
709 metadata:
710 name: test
711 generation: 1
712 namespace: qual
713 status:
714 observedGeneration: 1
715 updatedReplicas: 1
716 readyReplicas: 1
717 availableReplicas: 1
718 replicas: 1
719 observedGeneration: 1
720 conditions:
721 - type: Progressing
722 status: "True"
723 reason: NewReplicaSetAvailable
724 - type: Available
725 status: "False"
726 `
727
728 func TestDeploymentStatus(t *testing.T) {
729 testCases := map[string]testSpec{
730 "depNoStatus": {
731 spec: depNoStatus,
732 expectedStatus: InProgressStatus,
733 expectedConditions: []Condition{{
734 Type: ConditionReconciling,
735 Status: corev1.ConditionTrue,
736 Reason: "LessReplicas",
737 }},
738 absentConditionTypes: []ConditionType{
739 ConditionStalled,
740 },
741 },
742 "depOK": {
743 spec: depOK,
744 expectedStatus: CurrentStatus,
745 expectedConditions: []Condition{},
746 absentConditionTypes: []ConditionType{
747 ConditionStalled,
748 ConditionReconciling,
749 },
750 },
751 "depNotProgressing": {
752 spec: depNotProgressing,
753 expectedStatus: InProgressStatus,
754 expectedConditions: []Condition{{
755 Type: ConditionReconciling,
756 Status: corev1.ConditionTrue,
757 Reason: "ReplicaSetNotAvailable",
758 }},
759 absentConditionTypes: []ConditionType{
760 ConditionStalled,
761 },
762 },
763 "depNoProgressDeadlineSeconds": {
764 spec: depNoProgressDeadlineSeconds,
765 expectedStatus: CurrentStatus,
766 expectedConditions: []Condition{},
767 absentConditionTypes: []ConditionType{
768 ConditionStalled,
769 ConditionReconciling,
770 },
771 },
772 "depNotAvailable": {
773 spec: depNotAvailable,
774 expectedStatus: InProgressStatus,
775 expectedConditions: []Condition{{
776 Type: ConditionReconciling,
777 Status: corev1.ConditionTrue,
778 Reason: "DeploymentNotAvailable",
779 }},
780 absentConditionTypes: []ConditionType{
781 ConditionStalled,
782 },
783 },
784 }
785
786 for tn, tc := range testCases {
787 tc := tc
788 t.Run(tn, func(t *testing.T) {
789 runStatusTest(t, tc)
790 })
791 }
792 }
793
794 var rsNoStatus = `
795 apiVersion: apps/v1
796 kind: ReplicaSet
797 metadata:
798 name: test
799 generation: 1
800 `
801
802 var rsOK1 = `
803 apiVersion: apps/v1
804 kind: ReplicaSet
805 metadata:
806 name: test
807 namespace: qual
808 generation: 1
809 spec:
810 replicas: 2
811 status:
812 observedGeneration: 1
813 replicas: 2
814 readyReplicas: 2
815 availableReplicas: 2
816 fullyLabeledReplicas: 2
817 conditions:
818 - type: ReplicaFailure
819 status: "False"
820 `
821
822 var rsOK2 = `
823 apiVersion: apps/v1
824 kind: ReplicaSet
825 metadata:
826 name: test
827 namespace: qual
828 generation: 1
829 spec:
830 replicas: 2
831 status:
832 observedGeneration: 1
833 fullyLabeledReplicas: 2
834 replicas: 2
835 readyReplicas: 2
836 availableReplicas: 2
837 `
838
839 var rsLessReady = `
840 apiVersion: apps/v1
841 kind: ReplicaSet
842 metadata:
843 name: test
844 namespace: qual
845 generation: 1
846 spec:
847 replicas: 4
848 status:
849 observedGeneration: 1
850 replicas: 4
851 readyReplicas: 2
852 availableReplicas: 4
853 fullyLabeledReplicas: 4
854 `
855
856 var rsLessAvailable = `
857 apiVersion: apps/v1
858 kind: ReplicaSet
859 metadata:
860 name: test
861 namespace: qual
862 generation: 1
863 spec:
864 replicas: 4
865 status:
866 observedGeneration: 1
867 replicas: 4
868 readyReplicas: 4
869 availableReplicas: 2
870 fullyLabeledReplicas: 4
871 `
872
873 var rsReplicaFailure = `
874 apiVersion: apps/v1
875 kind: ReplicaSet
876 metadata:
877 name: test
878 namespace: qual
879 generation: 1
880 spec:
881 replicas: 4
882 status:
883 observedGeneration: 1
884 replicas: 4
885 readyReplicas: 4
886 fullyLabeledReplicas: 4
887 availableReplicas: 4
888 conditions:
889 - type: ReplicaFailure
890 status: "True"
891 `
892
893 func TestReplicasetStatus(t *testing.T) {
894 testCases := map[string]testSpec{
895 "rsNoStatus": {
896 spec: rsNoStatus,
897 expectedStatus: InProgressStatus,
898 expectedConditions: []Condition{{
899 Type: ConditionReconciling,
900 Status: corev1.ConditionTrue,
901 Reason: "LessLabelled",
902 }},
903 absentConditionTypes: []ConditionType{
904 ConditionStalled,
905 },
906 },
907 "rsOK1": {
908 spec: rsOK1,
909 expectedStatus: CurrentStatus,
910 expectedConditions: []Condition{},
911 absentConditionTypes: []ConditionType{
912 ConditionStalled,
913 ConditionReconciling,
914 },
915 },
916 "rsOK2": {
917 spec: rsOK2,
918 expectedStatus: CurrentStatus,
919 expectedConditions: []Condition{},
920 absentConditionTypes: []ConditionType{
921 ConditionStalled,
922 ConditionReconciling,
923 },
924 },
925 "rsLessAvailable": {
926 spec: rsLessAvailable,
927 expectedStatus: InProgressStatus,
928 expectedConditions: []Condition{{
929 Type: ConditionReconciling,
930 Status: corev1.ConditionTrue,
931 Reason: "LessAvailable",
932 }},
933 absentConditionTypes: []ConditionType{
934 ConditionStalled,
935 },
936 },
937 "rsLessReady": {
938 spec: rsLessReady,
939 expectedStatus: InProgressStatus,
940 expectedConditions: []Condition{{
941 Type: ConditionReconciling,
942 Status: corev1.ConditionTrue,
943 Reason: "LessReady",
944 }},
945 absentConditionTypes: []ConditionType{
946 ConditionStalled,
947 },
948 },
949 "rsReplicaFailure": {
950 spec: rsReplicaFailure,
951 expectedStatus: InProgressStatus,
952 expectedConditions: []Condition{{
953 Type: ConditionReconciling,
954 Status: corev1.ConditionTrue,
955 Reason: "ReplicaFailure",
956 }},
957 absentConditionTypes: []ConditionType{
958 ConditionStalled,
959 },
960 },
961 }
962
963 for tn, tc := range testCases {
964 tc := tc
965 t.Run(tn, func(t *testing.T) {
966 runStatusTest(t, tc)
967 })
968 }
969 }
970
971 var pdbNotObserved = `
972 apiVersion: policy/v1beta1
973 kind: PodDisruptionBudget
974 metadata:
975 generation: 2
976 name: test
977 namespace: qual
978 status:
979 observedGeneration: 1
980 `
981
982 var pdbObserved = `
983 apiVersion: policy/v1beta1
984 kind: PodDisruptionBudget
985 metadata:
986 generation: 1
987 name: test
988 namespace: qual
989 status:
990 observedGeneration: 1
991 `
992
993 func TestPDBStatus(t *testing.T) {
994 testCases := map[string]testSpec{
995 "pdbNotObserved": {
996 spec: pdbNotObserved,
997 expectedStatus: InProgressStatus,
998 expectedConditions: []Condition{{
999 Type: ConditionReconciling,
1000 Status: corev1.ConditionTrue,
1001 Reason: "LatestGenerationNotObserved",
1002 }},
1003 absentConditionTypes: []ConditionType{
1004 ConditionStalled,
1005 },
1006 },
1007 "pdbObserved": {
1008 spec: pdbObserved,
1009 expectedStatus: CurrentStatus,
1010 expectedConditions: []Condition{},
1011 absentConditionTypes: []ConditionType{
1012 ConditionStalled,
1013 ConditionReconciling,
1014 },
1015 },
1016 }
1017
1018 for tn, tc := range testCases {
1019 tc := tc
1020 t.Run(tn, func(t *testing.T) {
1021 runStatusTest(t, tc)
1022 })
1023 }
1024 }
1025
1026 var crdNoStatus = `
1027 apiVersion: something/v1
1028 kind: MyCR
1029 metadata:
1030 generation: 1
1031 name: test
1032 namespace: qual
1033 `
1034
1035 var crdMismatchStatusGeneration = `
1036 apiVersion: something/v1
1037 kind: MyCR
1038 metadata:
1039 name: test
1040 namespace: qual
1041 generation: 2
1042 status:
1043 observedGeneration: 1
1044 `
1045
1046 var crdReady = `
1047 apiVersion: something/v1
1048 kind: MyCR
1049 metadata:
1050 name: test
1051 namespace: qual
1052 generation: 1
1053 status:
1054 conditions:
1055 - type: Ready
1056 status: "True"
1057 message: All looks ok
1058 reason: AllOk
1059 `
1060
1061 var crdNotReady = `
1062 apiVersion: something/v1
1063 kind: MyCR
1064 metadata:
1065 generation: 1
1066 name: test
1067 namespace: qual
1068 status:
1069 observedGeneration: 1
1070 conditions:
1071 - type: Ready
1072 status: "False"
1073 reason: NotReadyYet
1074 `
1075
1076 var crdNoCondition = `
1077 apiVersion: something/v1
1078 kind: MyCR
1079 metadata:
1080 name: test
1081 namespace: qual
1082 generation: 1
1083 status:
1084 conditions:
1085 - type: SomeCondition
1086 status: "False"
1087 `
1088
1089 func TestCRDGenericStatus(t *testing.T) {
1090 testCases := map[string]testSpec{
1091 "crdNoStatus": {
1092 spec: crdNoStatus,
1093 expectedStatus: CurrentStatus,
1094 expectedConditions: []Condition{},
1095 absentConditionTypes: []ConditionType{
1096 ConditionStalled,
1097 ConditionReconciling,
1098 },
1099 },
1100 "crdReady": {
1101 spec: crdReady,
1102 expectedStatus: CurrentStatus,
1103 expectedConditions: []Condition{},
1104 absentConditionTypes: []ConditionType{
1105 ConditionStalled,
1106 ConditionReconciling,
1107 },
1108 },
1109 "crdNotReady": {
1110 spec: crdNotReady,
1111 expectedStatus: InProgressStatus,
1112 expectedConditions: []Condition{
1113 {
1114 Type: ConditionReconciling,
1115 Status: corev1.ConditionTrue,
1116 Reason: "NotReadyYet",
1117 },
1118 },
1119 absentConditionTypes: []ConditionType{
1120 ConditionStalled,
1121 },
1122 },
1123 "crdNoCondition": {
1124 spec: crdNoCondition,
1125 expectedStatus: CurrentStatus,
1126 expectedConditions: []Condition{},
1127 absentConditionTypes: []ConditionType{
1128 ConditionStalled,
1129 ConditionReconciling,
1130 },
1131 },
1132 "crdMismatchStatusGeneration": {
1133 spec: crdMismatchStatusGeneration,
1134 expectedStatus: InProgressStatus,
1135 expectedConditions: []Condition{{
1136 Type: ConditionReconciling,
1137 Status: corev1.ConditionTrue,
1138 Reason: "LatestGenerationNotObserved",
1139 }},
1140 absentConditionTypes: []ConditionType{
1141 ConditionStalled,
1142 },
1143 },
1144 }
1145
1146 for tn, tc := range testCases {
1147 tc := tc
1148 t.Run(tn, func(t *testing.T) {
1149 runStatusTest(t, tc)
1150 })
1151 }
1152 }
1153
1154 var jobNoStatus = `
1155 apiVersion: batch/v1
1156 kind: Job
1157 metadata:
1158 name: test
1159 namespace: qual
1160 generation: 1
1161 `
1162
1163 var jobComplete = `
1164 apiVersion: batch/v1
1165 kind: Job
1166 metadata:
1167 name: test
1168 namespace: qual
1169 generation: 1
1170 status:
1171 succeeded: 1
1172 active: 0
1173 conditions:
1174 - type: Complete
1175 status: "True"
1176 `
1177
1178 var jobFailed = `
1179 apiVersion: batch/v1
1180 kind: Job
1181 metadata:
1182 name: test
1183 namespace: qual
1184 generation: 1
1185 spec:
1186 completions: 4
1187 status:
1188 succeeded: 3
1189 failed: 1
1190 conditions:
1191 - type: Failed
1192 status: "True"
1193 reason: JobFailed
1194 `
1195
1196 var jobInProgress = `
1197 apiVersion: batch/v1
1198 kind: Job
1199 metadata:
1200 name: test
1201 namespace: qual
1202 generation: 1
1203 spec:
1204 completions: 10
1205 parallelism: 2
1206 status:
1207 startTime: "2019-06-04T01:17:13Z"
1208 succeeded: 3
1209 failed: 1
1210 active: 2
1211 conditions:
1212 - type: Failed
1213 status: "False"
1214 - type: Complete
1215 status: "False"
1216 `
1217
1218 func TestJobStatus(t *testing.T) {
1219 testCases := map[string]testSpec{
1220 "jobNoStatus": {
1221 spec: jobNoStatus,
1222 expectedStatus: InProgressStatus,
1223 expectedConditions: []Condition{{
1224 Type: ConditionReconciling,
1225 Status: corev1.ConditionTrue,
1226 Reason: "JobNotStarted",
1227 }},
1228 absentConditionTypes: []ConditionType{
1229 ConditionStalled,
1230 },
1231 },
1232 "jobComplete": {
1233 spec: jobComplete,
1234 expectedStatus: CurrentStatus,
1235 expectedConditions: []Condition{},
1236 absentConditionTypes: []ConditionType{
1237 ConditionStalled,
1238 ConditionReconciling,
1239 },
1240 },
1241 "jobFailed": {
1242 spec: jobFailed,
1243 expectedStatus: FailedStatus,
1244 expectedConditions: []Condition{{
1245 Type: ConditionStalled,
1246 Status: corev1.ConditionTrue,
1247 Reason: "JobFailed",
1248 }},
1249 absentConditionTypes: []ConditionType{
1250 ConditionReconciling,
1251 },
1252 },
1253 "jobInProgress": {
1254 spec: jobInProgress,
1255 expectedStatus: CurrentStatus,
1256 expectedConditions: []Condition{},
1257 absentConditionTypes: []ConditionType{
1258 ConditionReconciling,
1259 ConditionStalled,
1260 },
1261 },
1262 }
1263
1264 for tn, tc := range testCases {
1265 tc := tc
1266 t.Run(tn, func(t *testing.T) {
1267 runStatusTest(t, tc)
1268 })
1269 }
1270 }
1271
1272 var cronjobNoStatus = `
1273 apiVersion: batch/v1
1274 kind: CronJob
1275 metadata:
1276 name: test
1277 namespace: qual
1278 generation: 1
1279 `
1280
1281 var cronjobWithStatus = `
1282 apiVersion: batch/v1
1283 kind: CronJob
1284 metadata:
1285 name: test
1286 namespace: qual
1287 generation: 1
1288 status:
1289 `
1290
1291 func TestCronJobStatus(t *testing.T) {
1292 testCases := map[string]testSpec{
1293 "cronjobNoStatus": {
1294 spec: cronjobNoStatus,
1295 expectedStatus: CurrentStatus,
1296 expectedConditions: []Condition{},
1297 absentConditionTypes: []ConditionType{
1298 ConditionStalled,
1299 ConditionReconciling,
1300 },
1301 },
1302 "cronjobWithStatus": {
1303 spec: cronjobWithStatus,
1304 expectedStatus: CurrentStatus,
1305 expectedConditions: []Condition{},
1306 absentConditionTypes: []ConditionType{
1307 ConditionStalled,
1308 ConditionReconciling,
1309 },
1310 },
1311 }
1312
1313 for tn, tc := range testCases {
1314 tc := tc
1315 t.Run(tn, func(t *testing.T) {
1316 runStatusTest(t, tc)
1317 })
1318 }
1319 }
1320
1321 var serviceDefault = `
1322 apiVersion: v1
1323 kind: Service
1324 metadata:
1325 name: test
1326 namespace: qual
1327 generation: 1
1328 `
1329
1330 var serviceNodePort = `
1331 apiVersion: v1
1332 kind: Service
1333 metadata:
1334 name: test
1335 namespace: qual
1336 generation: 1
1337 spec:
1338 type: NodePort
1339 `
1340
1341 var serviceLBok = `
1342 apiVersion: v1
1343 kind: Service
1344 metadata:
1345 name: test
1346 namespace: qual
1347 generation: 1
1348 spec:
1349 type: LoadBalancer
1350 clusterIP: "1.2.3.4"
1351 `
1352 var serviceLBnok = `
1353 apiVersion: v1
1354 kind: Service
1355 metadata:
1356 name: test
1357 namespace: qual
1358 generation: 1
1359 spec:
1360 type: LoadBalancer
1361 `
1362
1363 func TestServiceStatus(t *testing.T) {
1364 testCases := map[string]testSpec{
1365 "serviceDefault": {
1366 spec: serviceDefault,
1367 expectedStatus: CurrentStatus,
1368 expectedConditions: []Condition{},
1369 absentConditionTypes: []ConditionType{
1370 ConditionStalled,
1371 ConditionReconciling,
1372 },
1373 },
1374 "serviceNodePort": {
1375 spec: serviceNodePort,
1376 expectedStatus: CurrentStatus,
1377 expectedConditions: []Condition{},
1378 absentConditionTypes: []ConditionType{
1379 ConditionStalled,
1380 ConditionReconciling,
1381 },
1382 },
1383 "serviceLBnok": {
1384 spec: serviceLBnok,
1385 expectedStatus: InProgressStatus,
1386 expectedConditions: []Condition{{
1387 Type: ConditionReconciling,
1388 Status: corev1.ConditionTrue,
1389 Reason: "NoIPAssigned",
1390 }},
1391 absentConditionTypes: []ConditionType{
1392 ConditionStalled,
1393 },
1394 },
1395 "serviceLBok": {
1396 spec: serviceLBok,
1397 expectedStatus: CurrentStatus,
1398 expectedConditions: []Condition{},
1399 absentConditionTypes: []ConditionType{
1400 ConditionStalled,
1401 ConditionReconciling,
1402 },
1403 },
1404 }
1405
1406 for tn, tc := range testCases {
1407 tc := tc
1408 t.Run(tn, func(t *testing.T) {
1409 runStatusTest(t, tc)
1410 })
1411 }
1412 }
1413
1414 var crdNoConditions = `
1415 apiVersion: apiextensions.k8s.io/v1
1416 kind: CustomResourceDefinition
1417 metadata:
1418 generation: 1
1419 `
1420
1421 var crdInstalling = `
1422 apiVersion: apiextensions.k8s.io/v1
1423 kind: CustomResourceDefinition
1424 metadata:
1425 generation: 1
1426 status:
1427 conditions:
1428 - type: NamesAccepted
1429 status: "True"
1430 reason: NoConflicts
1431 - type: Established
1432 status: "False"
1433 reason: Installing
1434 `
1435
1436 var crdNamesNotAccepted = `
1437 apiVersion: apiextensions.k8s.io/v1
1438 kind: CustomResourceDefinition
1439 metadata:
1440 generation: 1
1441 status:
1442 conditions:
1443 - type: NamesAccepted
1444 status: "False"
1445 reason: SomeReason
1446 `
1447
1448 var crdEstablished = `
1449 apiVersion: apiextensions.k8s.io/v1
1450 kind: CustomResourceDefinition
1451 metadata:
1452 generation: 1
1453 status:
1454 conditions:
1455 - type: NamesAccepted
1456 status: "True"
1457 reason: NoConflicts
1458 - type: Established
1459 status: "True"
1460 reason: InitialNamesAccepted
1461 `
1462
1463 func TestCRDStatus(t *testing.T) {
1464 testCases := map[string]testSpec{
1465 "crdNoConditions": {
1466 spec: crdNoConditions,
1467 expectedStatus: InProgressStatus,
1468 expectedConditions: []Condition{
1469 {
1470 Type: ConditionReconciling,
1471 Status: corev1.ConditionTrue,
1472 Reason: "Installing",
1473 },
1474 },
1475 absentConditionTypes: []ConditionType{
1476 ConditionStalled,
1477 },
1478 },
1479 "crdInstalling": {
1480 spec: crdInstalling,
1481 expectedStatus: InProgressStatus,
1482 expectedConditions: []Condition{
1483 {
1484 Type: ConditionReconciling,
1485 Status: corev1.ConditionTrue,
1486 Reason: "Installing",
1487 },
1488 },
1489 absentConditionTypes: []ConditionType{
1490 ConditionStalled,
1491 },
1492 },
1493 "crdNamesNotAccepted": {
1494 spec: crdNamesNotAccepted,
1495 expectedStatus: FailedStatus,
1496 expectedConditions: []Condition{
1497 {
1498 Type: ConditionStalled,
1499 Status: corev1.ConditionTrue,
1500 Reason: "SomeReason",
1501 },
1502 },
1503 absentConditionTypes: []ConditionType{
1504 ConditionReconciling,
1505 },
1506 },
1507 "crdEstablished": {
1508 spec: crdEstablished,
1509 expectedStatus: CurrentStatus,
1510 expectedConditions: []Condition{},
1511 absentConditionTypes: []ConditionType{
1512 ConditionReconciling,
1513 ConditionStalled,
1514 },
1515 },
1516 }
1517
1518 for tn, tc := range testCases {
1519 tc := tc
1520 t.Run(tn, func(t *testing.T) {
1521 runStatusTest(t, tc)
1522 })
1523 }
1524 }
1525
View as plain text