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  })