github.com/IBM-Blockchain/fabric-operator@v1.0.4/pkg/manager/resources/job/job_test.go (about) 1 /* 2 * Copyright contributors to the Hyperledger Fabric Operator project 3 * 4 * SPDX-License-Identifier: Apache-2.0 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 package job_test 20 21 import ( 22 "context" 23 "errors" 24 "time" 25 26 . "github.com/onsi/ginkgo/v2" 27 . "github.com/onsi/gomega" 28 29 "github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources/container" 30 "github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources/job" 31 "github.com/IBM-Blockchain/fabric-operator/pkg/manager/resources/job/mocks" 32 33 v1 "k8s.io/api/batch/v1" 34 corev1 "k8s.io/api/core/v1" 35 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 36 "k8s.io/apimachinery/pkg/types" 37 38 k8sclient "sigs.k8s.io/controller-runtime/pkg/client" 39 ) 40 41 var _ = Describe("Job", func() { 42 var ( 43 k8sJob *v1.Job 44 testJob *job.Job 45 ) 46 47 BeforeEach(func() { 48 k8sJob = &v1.Job{ 49 ObjectMeta: metav1.ObjectMeta{ 50 Name: "k8sJob", 51 Namespace: "default", 52 }, 53 } 54 testJob = &job.Job{ 55 Job: k8sJob, 56 } 57 }) 58 59 It("creates job with defaults", func() { 60 testJob = job.NewWithDefaults(k8sJob) 61 Expect(testJob.Timeouts).To(Equal(&job.Timeouts{ 62 WaitUntilActive: 60 * time.Second, 63 WaitUntilFinished: 60 * time.Second, 64 })) 65 66 By("adding unique id to job name", func() { 67 Expect(testJob.Name).To(ContainSubstring("k8sJob-")) 68 }) 69 }) 70 71 It("adds container", func() { 72 cont := container.Container{ 73 Container: &corev1.Container{ 74 Name: "test-cont", 75 }, 76 } 77 78 testJob.AddContainer(cont) 79 Expect(len(testJob.Spec.Template.Spec.Containers)).To(Equal(1)) 80 Expect(testJob.Spec.Template.Spec.Containers[0]).To(Equal(*cont.Container)) 81 }) 82 83 Context("volumes", func() { 84 BeforeEach(func() { 85 testJob.Spec.Template.Spec.Volumes = []corev1.Volume{ 86 { 87 Name: "test-volume", 88 }, 89 } 90 }) 91 92 It("appends volume if missing", func() { 93 testJob.AppendVolumeIfMissing(corev1.Volume{Name: "test-volume"}) 94 testJob.AppendVolumeIfMissing(corev1.Volume{Name: "test-volume2"}) 95 96 Expect(len(testJob.Spec.Template.Spec.Volumes)).To(Equal(2)) 97 Expect(testJob.Spec.Template.Spec.Volumes[1]).To(Equal(corev1.Volume{Name: "test-volume2"})) 98 }) 99 }) 100 101 Context("image pull secrets", func() { 102 BeforeEach(func() { 103 testJob.Spec.Template.Spec.ImagePullSecrets = []corev1.LocalObjectReference{ 104 { 105 Name: "pullsecret", 106 }, 107 } 108 }) 109 110 It("appends volume if missing", func() { 111 testJob.AppendPullSecret(corev1.LocalObjectReference{Name: "pullsecret"}) 112 testJob.AppendPullSecret(corev1.LocalObjectReference{Name: "pullsecret2"}) 113 114 Expect(len(testJob.Spec.Template.Spec.ImagePullSecrets)).To(Equal(2)) 115 Expect(testJob.Spec.Template.Spec.ImagePullSecrets[1]).To( 116 Equal(corev1.LocalObjectReference{Name: "pullsecret2"}), 117 ) 118 }) 119 }) 120 121 Context("events", func() { 122 var ( 123 client *mocks.Client 124 ) 125 126 BeforeEach(func() { 127 client = &mocks.Client{} 128 129 }) 130 131 Context("status", func() { 132 Context("failures", func() { 133 Context("job", func() { 134 When("getting job from API server fails", func() { 135 BeforeEach(func() { 136 client.GetStub = func(ctx context.Context, nn types.NamespacedName, obj k8sclient.Object) error { 137 return errors.New("failed to get job") 138 } 139 }) 140 141 It("returns error and UNKNOWN status", func() { 142 status, err := testJob.Status(client) 143 Expect(err).To(HaveOccurred()) 144 Expect(status).To(Equal(job.UNKNOWN)) 145 }) 146 }) 147 148 When("job has failed", func() { 149 BeforeEach(func() { 150 client.GetStub = func(ctx context.Context, nn types.NamespacedName, obj k8sclient.Object) error { 151 j := obj.(*v1.Job) 152 j.Status = v1.JobStatus{ 153 Failed: int32(1), 154 } 155 return nil 156 } 157 }) 158 159 It("returns FAILED status", func() { 160 status, err := testJob.Status(client) 161 Expect(err).NotTo(HaveOccurred()) 162 Expect(status).To(Equal(job.FAILED)) 163 }) 164 }) 165 }) 166 167 Context("pods", func() { 168 When("getting pods from API server fails", func() { 169 BeforeEach(func() { 170 client.ListStub = func(ctx context.Context, list k8sclient.ObjectList, opts ...k8sclient.ListOption) error { 171 return errors.New("failed to list pods") 172 } 173 }) 174 175 It("returns error and UNKNOWN status", func() { 176 status, err := testJob.Status(client) 177 Expect(err).To(HaveOccurred()) 178 Expect(status).To(Equal(job.UNKNOWN)) 179 }) 180 }) 181 182 When("job has failed", func() { 183 BeforeEach(func() { 184 client.ListStub = func(ctx context.Context, list k8sclient.ObjectList, opts ...k8sclient.ListOption) error { 185 pods := list.(*corev1.PodList) 186 pods.Items = []corev1.Pod{ 187 { 188 Status: corev1.PodStatus{ 189 Phase: corev1.PodFailed, 190 }, 191 }, 192 } 193 return nil 194 } 195 }) 196 197 It("returns FAILED status", func() { 198 status, err := testJob.Status(client) 199 Expect(err).NotTo(HaveOccurred()) 200 Expect(status).To(Equal(job.FAILED)) 201 }) 202 }) 203 }) 204 }) 205 206 It("returns COMPLETED state", func() { 207 status, err := testJob.Status(client) 208 Expect(err).NotTo(HaveOccurred()) 209 Expect(status).To(Equal(job.COMPLETED)) 210 }) 211 }) 212 213 Context("wait until active", func() { 214 BeforeEach(func() { 215 testJob.Timeouts = &job.Timeouts{ 216 WaitUntilActive: time.Second, 217 } 218 219 client.GetStub = func(ctx context.Context, nn types.NamespacedName, obj k8sclient.Object) error { 220 j := obj.(*v1.Job) 221 j.Status = v1.JobStatus{ 222 Active: int32(1), 223 } 224 return nil 225 } 226 }) 227 228 It("returns before timeout with no errors", func() { 229 err := testJob.WaitUntilActive(client) 230 Expect(err).NotTo(HaveOccurred()) 231 }) 232 }) 233 234 Context("wait until finished", func() { 235 BeforeEach(func() { 236 testJob.Timeouts = &job.Timeouts{ 237 WaitUntilFinished: time.Second, 238 } 239 240 client.ListStub = func(ctx context.Context, list k8sclient.ObjectList, opts ...k8sclient.ListOption) error { 241 pods := list.(*corev1.PodList) 242 pods.Items = []corev1.Pod{ 243 { 244 Status: corev1.PodStatus{ 245 ContainerStatuses: []corev1.ContainerStatus{ 246 { 247 State: corev1.ContainerState{ 248 Terminated: &corev1.ContainerStateTerminated{}, 249 }, 250 }, 251 }, 252 }, 253 }, 254 } 255 return nil 256 } 257 }) 258 259 It("returns before timeout with no errors", func() { 260 err := testJob.WaitUntilFinished(client) 261 Expect(err).NotTo(HaveOccurred()) 262 }) 263 }) 264 }) 265 })