github.com/iter8-tools/iter8@v1.1.2/base/readiness_test.go (about) 1 package base 2 3 import ( 4 "context" 5 "os" 6 "strings" 7 "testing" 8 9 "github.com/stretchr/testify/assert" 10 11 "helm.sh/helm/v3/pkg/cli" 12 13 corev1 "k8s.io/api/core/v1" 14 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 15 "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" 16 "k8s.io/apimachinery/pkg/runtime" 17 "k8s.io/apimachinery/pkg/runtime/schema" 18 ) 19 20 // TestNoObject tests that task fails if the object is not present 21 func TestNoObject(t *testing.T) { 22 _ = os.Chdir(t.TempDir()) 23 ns, nm := "default", "test-pod" 24 pod := newPod(ns, nm).withCondition("Ready", "True").build() 25 rTask := newReadinessTask("non-existent-pod").withVersion("v1").withResource("pods").withNamespace(ns).withCondition("Ready").build() 26 runTaskTest(t, rTask, false, ns, pod) 27 } 28 29 // TestWithoutCondition tests the task succeeds when there are no conditions on the object 30 // It should be successful 31 // Also validates parsing of timeout 32 // Also validates setting of default namespace 33 func TestWithoutConditions(t *testing.T) { 34 _ = os.Chdir(t.TempDir()) 35 ns, nm := "default", "test-pod" 36 pod := newPod(ns, nm).build() 37 rTask := newReadinessTask(nm).withVersion("v1").withResource("pods").withTimeout("20s").build() 38 runTaskTest(t, rTask, true, ns, pod) 39 } 40 41 // TestWithCondition tests that the task succeeds when the condition is present and True 42 func TestWithCondition(t *testing.T) { 43 _ = os.Chdir(t.TempDir()) 44 ns, nm := "default", "test-pod" 45 pod := newPod(ns, nm).withCondition("Ready", "True").build() 46 rTask := newReadinessTask(nm).withVersion("v1").withResource("pods").withNamespace(ns).withCondition("Ready").build() 47 runTaskTest(t, rTask, true, ns, pod) 48 } 49 50 // TestWithFalseCondition tests that the task fails when the condition is present and not True 51 func TestWithFalseCondition(t *testing.T) { 52 _ = os.Chdir(t.TempDir()) 53 ns, nm := "default", "test-pod" 54 pod := newPod(ns, nm).withCondition("Ready", "False").build() 55 rTask := newReadinessTask(nm).withVersion("v1").withResource("pods").withNamespace(ns).withCondition("Ready").build() 56 runTaskTest(t, rTask, false, ns, pod) 57 } 58 59 // TestConditionNotPresent tests that the task fails when the condition is not present (but others are) 60 func TestConditionNotPresent(t *testing.T) { 61 _ = os.Chdir(t.TempDir()) 62 ns, nm := "default", "test-pod" 63 pod := newPod(ns, nm).build() 64 rTask := newReadinessTask(nm).withVersion("v1").withResource("pods").withNamespace(ns).withCondition("NotPresent").build() 65 runTaskTest(t, rTask, false, ns, pod) 66 } 67 68 // TestInvalidTimeout tests that the task fails when the specified timeout is invalid (not parseable) 69 func TestInvalidTimeout(t *testing.T) { 70 _ = os.Chdir(t.TempDir()) 71 ns, nm := "default", "test-pod" 72 pod := newPod(ns, nm).withCondition("Ready", "True").build() 73 rTask := newReadinessTask(nm).withVersion("v1").withResource("pods").withNamespace(ns).withTimeout("timeout").build() 74 runTaskTest(t, rTask, false, ns, pod) 75 } 76 77 // UTILITY METHODS for all tests 78 79 // runTaskTest creates fake cluster with pod and runs rTask 80 func runTaskTest(t *testing.T, rTask *readinessTask, success bool, ns string, pod *unstructured.Unstructured) { 81 *kd = *NewFakeKubeDriver(cli.New()) 82 rs := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pods"} 83 _, err := kd.dynamicClient.Resource(rs).Namespace(ns).Create(context.Background(), pod, metav1.CreateOptions{}) 84 assert.NoError(t, err, "get failed") 85 86 err = rTask.run(&Experiment{ 87 Spec: []Task{rTask}, 88 Result: &ExperimentResult{}, 89 }) 90 if success { 91 assert.NoError(t, err) 92 } else { 93 assert.Error(t, err) 94 } 95 } 96 97 type podBuilder corev1.Pod 98 99 func newPod(ns string, nm string) *podBuilder { 100 pod := &corev1.Pod{ 101 TypeMeta: metav1.TypeMeta{ 102 APIVersion: "v1", 103 Kind: "Pod", 104 }, 105 ObjectMeta: metav1.ObjectMeta{ 106 Name: nm, 107 Namespace: ns, 108 }, 109 } 110 return (*podBuilder)(pod) 111 } 112 113 func (p *podBuilder) build() *unstructured.Unstructured { 114 o, err := runtime.DefaultUnstructuredConverter.ToUnstructured((*corev1.Pod)(p)) 115 if err != nil { 116 return nil 117 } 118 return &unstructured.Unstructured{Object: o} 119 } 120 121 func (p *podBuilder) withCondition(typ string, value string) *podBuilder { 122 c := corev1.PodCondition{Type: (corev1.PodConditionType(typ))} 123 switch strings.ToLower(value) { 124 case "true": 125 c.Status = corev1.ConditionTrue 126 case "false": 127 c.Status = corev1.ConditionFalse 128 default: 129 c.Status = corev1.ConditionUnknown 130 } 131 p.Status.Conditions = append(p.Status.Conditions, c) 132 133 return p 134 } 135 136 type readinessTaskBuilder readinessTask 137 138 func newReadinessTask(name string) *readinessTaskBuilder { 139 rTask := &readinessTask{ 140 TaskMeta: TaskMeta{ 141 Task: StringPointer(ReadinessTaskName), 142 }, 143 With: readinessInputs{ 144 Name: name, 145 }, 146 } 147 148 return (*readinessTaskBuilder)(rTask) 149 } 150 151 // func (t *readinessTaskBuilder) withGroup(group string) *readinessTaskBuilder { 152 // t.With.Group = group 153 // return t 154 // } 155 func (t *readinessTaskBuilder) withVersion(version string) *readinessTaskBuilder { 156 t.With.Version = version 157 return t 158 } 159 160 func (t *readinessTaskBuilder) withResource(resource string) *readinessTaskBuilder { 161 t.With.Resource = resource 162 return t 163 } 164 165 func (t *readinessTaskBuilder) withNamespace(ns string) *readinessTaskBuilder { 166 t.With.Namespace = StringPointer(ns) 167 return t 168 } 169 170 func (t *readinessTaskBuilder) withTimeout(timeout string) *readinessTaskBuilder { 171 t.With.Timeout = StringPointer(timeout) 172 return t 173 } 174 175 func (t *readinessTaskBuilder) withCondition(condition string) *readinessTaskBuilder { 176 t.With.Conditions = append(t.With.Conditions, condition) 177 return t 178 } 179 180 func (t *readinessTaskBuilder) build() *readinessTask { 181 return (*readinessTask)(t) 182 }