github.com/redhat-appstudio/release-service@v0.0.0-20240507045911-a8558ef3422a/tekton/utils/pipeline_run_builder_test.go (about) 1 /* 2 Copyright 2023. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package utils 18 19 import ( 20 "fmt" 21 "github.com/hashicorp/go-multierror" 22 . "github.com/onsi/ginkgo/v2" 23 . "github.com/onsi/gomega" 24 tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" 25 corev1 "k8s.io/api/core/v1" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "time" 28 ) 29 30 var _ = Describe("PipelineRun builder", func() { 31 32 When("NewPipelineRunBuilder method is called", func() { 33 var ( 34 namePrefix = "testPrefix" 35 namespace = "testNamespace" 36 builder *PipelineRunBuilder 37 ) 38 39 BeforeEach(func() { 40 builder = NewPipelineRunBuilder(namePrefix, namespace) 41 }) 42 43 It("should return a new PipelineRunBuilder instance", func() { 44 Expect(builder).To(Not(BeNil())) 45 }) 46 47 It("should set the correct GenerateName in the returned PipelineRunBuilder instance", func() { 48 Expect(builder.pipelineRun.ObjectMeta.GenerateName).To(Equal(namePrefix + "-")) 49 }) 50 51 It("should set the correct Namespace in the returned PipelineRunBuilder instance", func() { 52 Expect(builder.pipelineRun.ObjectMeta.Namespace).To(Equal(namespace)) 53 }) 54 55 It("should initialize an empty PipelineRunSpec", func() { 56 Expect(builder.pipelineRun.Spec).To(Equal(tektonv1.PipelineRunSpec{})) 57 }) 58 }) 59 60 When("Build method is called", func() { 61 It("should return the constructed PipelineRun if there are no errors", func() { 62 builder := NewPipelineRunBuilder("testPrefix", "testNamespace") 63 pr, err := builder.Build() 64 Expect(pr).To(Not(BeNil())) 65 Expect(err).To(BeNil()) 66 }) 67 68 It("should return the accumulated errors", func() { 69 builder := &PipelineRunBuilder{ 70 err: multierror.Append(nil, fmt.Errorf("dummy error 1"), fmt.Errorf("dummy error 2")), 71 } 72 _, err := builder.Build() 73 Expect(err).To(Not(BeNil())) 74 Expect(err.Error()).To(ContainSubstring("dummy error 1")) 75 Expect(err.Error()).To(ContainSubstring("dummy error 2")) 76 }) 77 }) 78 79 When("WithAnnotations method is called", func() { 80 var ( 81 builder *PipelineRunBuilder 82 ) 83 84 BeforeEach(func() { 85 builder = NewPipelineRunBuilder("testPrefix", "testNamespace") 86 }) 87 88 It("should add annotations when none previously existed", func() { 89 builder.WithAnnotations(map[string]string{ 90 "annotation1": "value1", 91 "annotation2": "value2", 92 }) 93 Expect(builder.pipelineRun.ObjectMeta.Annotations).To(HaveKeyWithValue("annotation1", "value1")) 94 Expect(builder.pipelineRun.ObjectMeta.Annotations).To(HaveKeyWithValue("annotation2", "value2")) 95 }) 96 97 It("should update existing annotations and add new ones", func() { 98 builder.pipelineRun.ObjectMeta.Annotations = map[string]string{ 99 "annotation1": "oldValue1", 100 "annotation3": "value3", 101 } 102 builder.WithAnnotations(map[string]string{ 103 "annotation1": "newValue1", 104 "annotation2": "value2", 105 }) 106 Expect(builder.pipelineRun.ObjectMeta.Annotations).To(HaveKeyWithValue("annotation1", "newValue1")) 107 Expect(builder.pipelineRun.ObjectMeta.Annotations).To(HaveKeyWithValue("annotation2", "value2")) 108 Expect(builder.pipelineRun.ObjectMeta.Annotations).To(HaveKeyWithValue("annotation3", "value3")) 109 }) 110 }) 111 112 When("WithFinalizer method is called", func() { 113 var ( 114 builder *PipelineRunBuilder 115 ) 116 117 BeforeEach(func() { 118 builder = NewPipelineRunBuilder("testPrefix", "testNamespace") 119 }) 120 121 It("should add a finalizer when none previously existed", func() { 122 builder.WithFinalizer("finalizer1") 123 Expect(builder.pipelineRun.ObjectMeta.Finalizers).To(ContainElement("finalizer1")) 124 }) 125 126 It("should append a new finalizer to the existing finalizers", func() { 127 builder.pipelineRun.ObjectMeta.Finalizers = []string{"existingFinalizer"} 128 builder.WithFinalizer("finalizer2") 129 Expect(builder.pipelineRun.ObjectMeta.Finalizers).To(ContainElements("existingFinalizer", "finalizer2")) 130 }) 131 }) 132 133 When("WithLabels method is called", func() { 134 var ( 135 builder *PipelineRunBuilder 136 ) 137 138 BeforeEach(func() { 139 builder = NewPipelineRunBuilder("testPrefix", "testNamespace") 140 }) 141 142 It("should add labels when none previously existed", func() { 143 builder.WithLabels(map[string]string{ 144 "label1": "value1", 145 "label2": "value2", 146 }) 147 Expect(builder.pipelineRun.ObjectMeta.Labels).To(HaveKeyWithValue("label1", "value1")) 148 Expect(builder.pipelineRun.ObjectMeta.Labels).To(HaveKeyWithValue("label2", "value2")) 149 }) 150 151 It("should update existing labels and add new ones", func() { 152 builder.pipelineRun.ObjectMeta.Labels = map[string]string{ 153 "label1": "oldValue1", 154 "label3": "value3", 155 } 156 builder.WithLabels(map[string]string{ 157 "label1": "newValue1", 158 "label2": "value2", 159 }) 160 Expect(builder.pipelineRun.ObjectMeta.Labels).To(HaveKeyWithValue("label1", "newValue1")) 161 Expect(builder.pipelineRun.ObjectMeta.Labels).To(HaveKeyWithValue("label2", "value2")) 162 Expect(builder.pipelineRun.ObjectMeta.Labels).To(HaveKeyWithValue("label3", "value3")) 163 }) 164 }) 165 166 When("WithObjectReferences method is called", func() { 167 It("should add parameters based on the provided client.Objects", func() { 168 builder := NewPipelineRunBuilder("testPrefix", "testNamespace") 169 configMap1 := &corev1.ConfigMap{ 170 ObjectMeta: metav1.ObjectMeta{ 171 Name: "configName1", 172 Namespace: "configNamespace1", 173 }, 174 } 175 configMap1.Kind = "ConfigMap" 176 configMap2 := &corev1.ConfigMap{ 177 ObjectMeta: metav1.ObjectMeta{ 178 Name: "configName2", 179 Namespace: "configNamespace2", 180 }, 181 } 182 configMap2.Kind = "ConfigMap" 183 184 builder.WithObjectReferences(configMap1, configMap2) 185 186 Expect(builder.pipelineRun.Spec.Params).To(ContainElement(tektonv1.Param{ 187 Name: "configMap", 188 Value: tektonv1.ParamValue{Type: tektonv1.ParamTypeString, StringVal: "configNamespace1/configName1"}, 189 })) 190 Expect(builder.pipelineRun.Spec.Params).To(ContainElement(tektonv1.Param{ 191 Name: "configMap", 192 Value: tektonv1.ParamValue{Type: tektonv1.ParamTypeString, StringVal: "configNamespace2/configName2"}, 193 })) 194 }) 195 }) 196 197 When("WithObjectSpecsAsJson method is called", func() { 198 It("should add parameters with JSON representation of the object's Spec", func() { 199 builder := NewPipelineRunBuilder("testPrefix", "testNamespace") 200 pod1 := &corev1.Pod{ 201 Spec: corev1.PodSpec{ 202 Containers: []corev1.Container{ 203 { 204 Name: "container1", 205 Image: "image1", 206 }, 207 }, 208 }, 209 } 210 pod1.Kind = "Pod" 211 pod2 := &corev1.Pod{ 212 Spec: corev1.PodSpec{ 213 Containers: []corev1.Container{ 214 { 215 Name: "container2", 216 Image: "image2", 217 }, 218 }, 219 }, 220 } 221 pod2.Kind = "Pod" 222 223 builder.WithObjectSpecsAsJson(pod1, pod2) 224 225 Expect(builder.pipelineRun.Spec.Params).To(ContainElement(tektonv1.Param{ 226 Name: "pod", 227 Value: tektonv1.ParamValue{ 228 Type: tektonv1.ParamTypeString, 229 StringVal: `{"containers":[{"name":"container1","image":"image1","resources":{}}]}`, 230 }, 231 })) 232 Expect(builder.pipelineRun.Spec.Params).To(ContainElement(tektonv1.Param{ 233 Name: "pod", 234 Value: tektonv1.ParamValue{ 235 Type: tektonv1.ParamTypeString, 236 StringVal: `{"containers":[{"name":"container2","image":"image2","resources":{}}]}`, 237 }, 238 })) 239 }) 240 }) 241 242 When("WithParams method is called", func() { 243 It("should append the provided parameters to the PipelineRun's spec", func() { 244 builder := NewPipelineRunBuilder("testPrefix", "testNamespace") 245 246 param1 := tektonv1.Param{ 247 Name: "param1", 248 Value: tektonv1.ParamValue{Type: tektonv1.ParamTypeString, StringVal: "value1"}, 249 } 250 param2 := tektonv1.Param{ 251 Name: "param2", 252 Value: tektonv1.ParamValue{Type: tektonv1.ParamTypeString, StringVal: "value2"}, 253 } 254 255 builder.WithParams(param1, param2) 256 257 Expect(builder.pipelineRun.Spec.Params).To(ContainElements(param1, param2)) 258 }) 259 }) 260 261 When("WithOwner method is called", func() { 262 var ( 263 builder *PipelineRunBuilder 264 configMap *corev1.ConfigMap 265 ) 266 267 BeforeEach(func() { 268 builder = NewPipelineRunBuilder("testPrefix", "testNamespace") 269 configMap = &corev1.ConfigMap{ 270 ObjectMeta: metav1.ObjectMeta{ 271 Name: "configName", 272 Namespace: "configNamespace", 273 }, 274 } 275 configMap.Kind = "Config" 276 }) 277 278 It("should handle owner without errors", func() { 279 builder.WithOwner(configMap) 280 _, err := builder.Build() 281 Expect(err).ToNot(HaveOccurred()) 282 }) 283 284 It("should have added owner annotations to the PipelineRun", func() { 285 builder.WithOwner(configMap) 286 Expect(builder.pipelineRun.Annotations).ToNot(BeEmpty()) 287 }) 288 }) 289 290 When("WithParamsFromConfigMap method is called", func() { 291 It("should add parameters corresponding to the provided keys", func() { 292 builder := NewPipelineRunBuilder("testPrefix", "testNamespace") 293 configMap := &corev1.ConfigMap{ 294 Data: map[string]string{ 295 "key1": "value1", 296 "key2": "value2", 297 }, 298 } 299 300 builder.WithParamsFromConfigMap(configMap, []string{"key1", "key2", "key3"}) // "key3" doesn't exist in the ConfigMap. 301 302 paramKey1 := tektonv1.Param{ 303 Name: "key1", 304 Value: tektonv1.ParamValue{Type: tektonv1.ParamTypeString, StringVal: "value1"}, 305 } 306 paramKey2 := tektonv1.Param{ 307 Name: "key2", 308 Value: tektonv1.ParamValue{Type: tektonv1.ParamTypeString, StringVal: "value2"}, 309 } 310 311 Expect(builder.pipelineRun.Spec.Params).To(ContainElement(paramKey1)) 312 Expect(builder.pipelineRun.Spec.Params).To(ContainElement(paramKey2)) 313 314 // Check that "key3" is not added as a Param since it doesn't exist in the ConfigMap. 315 for _, param := range builder.pipelineRun.Spec.Params { 316 Expect(param.Name).ToNot(Equal("key3")) 317 } 318 }) 319 }) 320 321 When("WithPipelineRef method is called", func() { 322 It("should set the PipelineRef for the PipelineRun's spec", func() { 323 builder := NewPipelineRunBuilder("testPrefix", "testNamespace") 324 pipelineRef := &tektonv1.PipelineRef{ 325 Name: "samplePipeline", 326 APIVersion: "tekton.dev/v1", 327 } 328 329 builder.WithPipelineRef(pipelineRef) 330 Expect(builder.pipelineRun.Spec.PipelineRef).To(Equal(pipelineRef)) 331 }) 332 333 It("adds the taskGit pipeline parameters to the PipelineRun object when using a git resolver", func() { 334 builder := NewPipelineRunBuilder("testPrefix", "testNamespace") 335 336 pipelineRef := &PipelineRef{ 337 Resolver: "git", 338 Params: []Param{ 339 { 340 Name: "url", 341 Value: "pipelineUrl", 342 }, 343 { 344 Name: "revision", 345 Value: "pipelineRevision", 346 }, 347 }, 348 } 349 350 builder.WithPipelineRef(pipelineRef.ToTektonPipelineRef()) 351 Expect(builder.pipelineRun.Spec.Params[0].Name).To(Equal("taskGitUrl")) 352 Expect(builder.pipelineRun.Spec.Params[0].Value.StringVal).To(Equal("pipelineUrl")) 353 Expect(builder.pipelineRun.Spec.Params[1].Name).To(Equal("taskGitRevision")) 354 Expect(builder.pipelineRun.Spec.Params[1].Value.StringVal).To(Equal("pipelineRevision")) 355 }) 356 }) 357 358 When("WithServiceAccount method is called", func() { 359 It("should set the ServiceAccountName for the PipelineRun's TaskRunTemplate", func() { 360 builder := NewPipelineRunBuilder("testPrefix", "testNamespace") 361 serviceAccount := "sampleServiceAccount" 362 builder.WithServiceAccount(serviceAccount) 363 Expect(builder.pipelineRun.Spec.TaskRunTemplate.ServiceAccountName).To(Equal(serviceAccount)) 364 }) 365 }) 366 367 When("WithTimeouts method is called", func() { 368 It("should set the timeouts for the PipelineRun", func() { 369 builder := NewPipelineRunBuilder("testPrefix", "testNamespace") 370 timeouts := &tektonv1.TimeoutFields{ 371 Pipeline: &metav1.Duration{Duration: 1 * time.Hour}, 372 Tasks: &metav1.Duration{Duration: 1 * time.Hour}, 373 Finally: &metav1.Duration{Duration: 1 * time.Hour}, 374 } 375 builder.WithTimeouts(timeouts, nil) 376 Expect(builder.pipelineRun.Spec.Timeouts).To(Equal(timeouts)) 377 }) 378 379 It("should use the default timeouts if the given timeouts are empty", func() { 380 builder := NewPipelineRunBuilder("testPrefix", "testNamespace") 381 defaultTimeouts := &tektonv1.TimeoutFields{ 382 Pipeline: &metav1.Duration{Duration: 1 * time.Hour}, 383 Tasks: &metav1.Duration{Duration: 1 * time.Hour}, 384 Finally: &metav1.Duration{Duration: 1 * time.Hour}, 385 } 386 builder.WithTimeouts(nil, defaultTimeouts) 387 Expect(builder.pipelineRun.Spec.Timeouts).To(Equal(defaultTimeouts)) 388 }) 389 }) 390 391 When("WithWorkspaceFromVolumeTemplate method is called", func() { 392 var ( 393 builder *PipelineRunBuilder 394 name string 395 ) 396 397 BeforeEach(func() { 398 builder = NewPipelineRunBuilder("testPrefix", "testNamespace") 399 name = "sampleWorkspace" 400 }) 401 402 It("should add a new workspace binding to the PipelineRun's spec with the correct name and size", func() { 403 size := "5Gi" 404 builder.WithWorkspaceFromVolumeTemplate(name, size) 405 Expect(len(builder.pipelineRun.Spec.Workspaces)).To(Equal(1)) 406 407 workspaceBinding := builder.pipelineRun.Spec.Workspaces[0] 408 Expect(workspaceBinding.Name).To(Equal(name)) 409 workspaceQuantity := workspaceBinding.VolumeClaimTemplate.Spec.Resources.Requests[corev1.ResourceStorage] 410 Expect(workspaceQuantity.String()).To(Equal(size)) 411 }) 412 413 It("should fail if the size is not in the right format", func() { 414 builder.WithWorkspaceFromVolumeTemplate(name, "invalid") 415 _, err := builder.Build() 416 Expect(err).To(HaveOccurred()) 417 Expect(err.Error()).To(ContainSubstring("invalid size format")) 418 }) 419 }) 420 })