github.com/redhat-appstudio/e2e-tests@v0.0.0-20240520140907-9709f6f59323/pkg/clients/integration/pipelineruns.go (about) 1 package integration 2 3 import ( 4 "context" 5 "fmt" 6 "sort" 7 "time" 8 9 . "github.com/onsi/ginkgo/v2" 10 appstudioApi "github.com/redhat-appstudio/application-api/api/v1alpha1" 11 "github.com/redhat-appstudio/e2e-tests/pkg/constants" 12 "github.com/redhat-appstudio/e2e-tests/pkg/utils" 13 "github.com/redhat-appstudio/e2e-tests/pkg/utils/tekton" 14 integrationv1beta1 "github.com/konflux-ci/integration-service/api/v1beta1" 15 tektonv1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" 16 k8sErrors "k8s.io/apimachinery/pkg/api/errors" 17 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 18 "k8s.io/apimachinery/pkg/labels" 19 "k8s.io/apimachinery/pkg/util/wait" 20 "knative.dev/pkg/apis" 21 "sigs.k8s.io/controller-runtime/pkg/client" 22 "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" 23 ) 24 25 // CreateIntegrationPipelineRun creates new integrationPipelineRun. 26 func (i *IntegrationController) CreateIntegrationPipelineRun(snapshotName, namespace, componentName, integrationTestScenarioName string) (*tektonv1.PipelineRun, error) { 27 testpipelineRun := &tektonv1.PipelineRun{ 28 ObjectMeta: metav1.ObjectMeta{ 29 GenerateName: "component-pipelinerun" + "-", 30 Namespace: namespace, 31 Labels: map[string]string{ 32 "pipelinesascode.tekton.dev/event-type": "push", 33 "appstudio.openshift.io/component": componentName, 34 "pipelines.appstudio.openshift.io/type": "test", 35 "appstudio.openshift.io/snapshot": snapshotName, 36 "test.appstudio.openshift.io/scenario": integrationTestScenarioName, 37 }, 38 }, 39 Spec: tektonv1.PipelineRunSpec{ 40 PipelineRef: tekton.NewBundleResolverPipelineRef( 41 "integration-pipeline-pass", 42 "quay.io/redhat-appstudio/example-tekton-bundle:integration-pipeline-pass", 43 ), 44 Params: []tektonv1.Param{ 45 { 46 Name: "output-image", 47 Value: tektonv1.ParamValue{ 48 Type: "string", 49 StringVal: "quay.io/redhat-appstudio/sample-image", 50 }, 51 }, 52 }, 53 }, 54 } 55 err := i.KubeRest().Create(context.Background(), testpipelineRun) 56 if err != nil { 57 return nil, err 58 } 59 return testpipelineRun, err 60 } 61 62 // GetComponentPipeline returns the pipeline for a given component labels. 63 // In case of failure, this function retries till it gets timed out. 64 func (i *IntegrationController) GetBuildPipelineRun(componentName, applicationName, namespace string, pacBuild bool, sha string) (*tektonv1.PipelineRun, error) { 65 var pipelineRun *tektonv1.PipelineRun 66 67 err := wait.PollUntilContextTimeout(context.Background(), constants.PipelineRunPollingInterval, 20*time.Minute, true, func(ctx context.Context) (done bool, err error) { 68 pipelineRunLabels := map[string]string{"appstudio.openshift.io/component": componentName, "appstudio.openshift.io/application": applicationName, "pipelines.appstudio.openshift.io/type": "build"} 69 70 if sha != "" { 71 pipelineRunLabels["pipelinesascode.tekton.dev/sha"] = sha 72 } 73 74 list := &tektonv1.PipelineRunList{} 75 err = i.KubeRest().List(context.Background(), list, &client.ListOptions{LabelSelector: labels.SelectorFromSet(pipelineRunLabels), Namespace: namespace}) 76 77 if err != nil && !k8sErrors.IsNotFound(err) { 78 GinkgoWriter.Printf("error listing pipelineruns in %s namespace: %v", namespace, err) 79 return false, nil 80 } 81 82 if len(list.Items) > 0 { 83 // sort PipelineRuns by StartTime in ascending order 84 sort.Slice(list.Items, func(i, j int) bool { 85 return list.Items[i].Status.StartTime.Before(list.Items[j].Status.StartTime) 86 }) 87 // get latest pipelineRun 88 pipelineRun = &list.Items[len(list.Items)-1] 89 return true, nil 90 } 91 92 pipelineRun = &tektonv1.PipelineRun{} 93 GinkgoWriter.Printf("no pipelinerun found for component %s %s", componentName, utils.GetAdditionalInfo(applicationName, namespace)) 94 return false, nil 95 }) 96 97 return pipelineRun, err 98 } 99 100 // GetIntegrationPipelineRun returns the integration pipelineRun 101 // for a given scenario, snapshot labels. 102 func (i *IntegrationController) GetIntegrationPipelineRun(integrationTestScenarioName string, snapshotName string, namespace string) (*tektonv1.PipelineRun, error) { 103 opts := []client.ListOption{ 104 client.InNamespace(namespace), 105 client.MatchingLabels{ 106 "pipelines.appstudio.openshift.io/type": "test", 107 "test.appstudio.openshift.io/scenario": integrationTestScenarioName, 108 "appstudio.openshift.io/snapshot": snapshotName, 109 }, 110 } 111 112 list := &tektonv1.PipelineRunList{} 113 err := i.KubeRest().List(context.Background(), list, opts...) 114 115 if err != nil && !k8sErrors.IsNotFound(err) { 116 return nil, fmt.Errorf("error listing pipelineruns in %s namespace", namespace) 117 } 118 119 if len(list.Items) > 0 { 120 return &list.Items[0], nil 121 } 122 123 return &tektonv1.PipelineRun{}, fmt.Errorf("no pipelinerun found for integrationTestScenario %s (snapshot: %s, namespace: %s)", integrationTestScenarioName, snapshotName, namespace) 124 } 125 126 // WaitForIntegrationPipelineToGetStarted wait for given integration pipeline to get started. 127 // In case of failure, this function retries till it gets timed out. 128 func (i *IntegrationController) WaitForIntegrationPipelineToGetStarted(testScenarioName, snapshotName, appNamespace string) (*tektonv1.PipelineRun, error) { 129 var testPipelinerun *tektonv1.PipelineRun 130 131 err := wait.PollUntilContextTimeout(context.Background(), time.Second*2, time.Minute*5, true, func(ctx context.Context) (done bool, err error) { 132 testPipelinerun, err = i.GetIntegrationPipelineRun(testScenarioName, snapshotName, appNamespace) 133 if err != nil { 134 GinkgoWriter.Println("PipelineRun has not been created yet for test scenario %s and snapshot %s/%s", testScenarioName, appNamespace, snapshotName) 135 return false, nil 136 } 137 if !testPipelinerun.HasStarted() { 138 GinkgoWriter.Println("pipelinerun %s/%s hasn't started yet", testPipelinerun.GetNamespace(), testPipelinerun.GetName()) 139 return false, nil 140 } 141 return true, nil 142 }) 143 144 return testPipelinerun, err 145 } 146 147 // WaitForIntegrationPipelineToBeFinished wait for given integration pipeline to finish. 148 // In case of failure, this function retries till it gets timed out. 149 func (i *IntegrationController) WaitForIntegrationPipelineToBeFinished(testScenario *integrationv1beta1.IntegrationTestScenario, snapshot *appstudioApi.Snapshot, appNamespace string) error { 150 return wait.PollUntilContextTimeout(context.Background(), constants.PipelineRunPollingInterval, 20*time.Minute, true, func(ctx context.Context) (done bool, err error) { 151 pipelineRun, err := i.GetIntegrationPipelineRun(testScenario.Name, snapshot.Name, appNamespace) 152 if err != nil { 153 GinkgoWriter.Println("PipelineRun has not been created yet for test scenario %s and snapshot %s/%s", testScenario.GetName(), snapshot.GetNamespace(), snapshot.GetName()) 154 return false, nil 155 } 156 for _, condition := range pipelineRun.Status.Conditions { 157 GinkgoWriter.Printf("PipelineRun %s reason: %s\n", pipelineRun.Name, condition.Reason) 158 159 if !pipelineRun.IsDone() { 160 return false, nil 161 } 162 163 if pipelineRun.GetStatusCondition().GetCondition(apis.ConditionSucceeded).IsTrue() { 164 return true, nil 165 } else { 166 return false, fmt.Errorf(tekton.GetFailedPipelineRunLogs(i.KubeRest(), i.KubeInterface(), pipelineRun)) 167 } 168 } 169 return false, nil 170 }) 171 } 172 173 // WaitForAllIntegrationPipelinesToBeFinished wait for all integration pipelines to finish. 174 func (i *IntegrationController) WaitForAllIntegrationPipelinesToBeFinished(testNamespace, applicationName string, snapshot *appstudioApi.Snapshot) error { 175 integrationTestScenarios, err := i.GetIntegrationTestScenarios(applicationName, testNamespace) 176 if err != nil { 177 return fmt.Errorf("unable to get IntegrationTestScenarios for Application %s/%s. Error: %v", testNamespace, applicationName, err) 178 } 179 180 for _, testScenario := range *integrationTestScenarios { 181 GinkgoWriter.Printf("Integration test scenario %s is found\n", testScenario.Name) 182 err = i.WaitForIntegrationPipelineToBeFinished(&testScenario, snapshot, testNamespace) 183 if err != nil { 184 return fmt.Errorf("error occurred while waiting for Integration PLR (associated with IntegrationTestScenario: %s) to get finished in %s namespace. Error: %v", testScenario.Name, testNamespace, err) 185 } 186 } 187 188 return nil 189 } 190 191 // WaitForFinalizerToGetRemovedFromAllIntegrationPipelineRuns waits for 192 // the given finalizer to get removed from all integration pipelinesruns 193 // that are related to the given application and namespace. 194 func (i *IntegrationController) WaitForFinalizerToGetRemovedFromAllIntegrationPipelineRuns(testNamespace, applicationName string, snapshot *appstudioApi.Snapshot) error { 195 integrationTestScenarios, err := i.GetIntegrationTestScenarios(applicationName, testNamespace) 196 if err != nil { 197 return fmt.Errorf("unable to get IntegrationTestScenarios for Application %s/%s. Error: %v", testNamespace, applicationName, err) 198 } 199 200 for _, testScenario := range *integrationTestScenarios { 201 testScenario := testScenario 202 GinkgoWriter.Printf("Integration test scenario %s is found\n", testScenario.Name) 203 err = i.WaitForFinalizerToGetRemovedFromIntegrationPipeline(&testScenario, snapshot, testNamespace) 204 if err != nil { 205 return fmt.Errorf("error occurred while waiting for Integration PLR (associated with IntegrationTestScenario: %s) to NOT have the finalizer. Error: %v", testScenario.Name, err) 206 } 207 } 208 209 return nil 210 } 211 212 // WaitForFinalizerToGetRemovedFromIntegrationPipeline waits for the 213 // given finalizer to get removed from the given integration pipelinerun 214 func (i *IntegrationController) WaitForFinalizerToGetRemovedFromIntegrationPipeline(testScenario *integrationv1beta1.IntegrationTestScenario, snapshot *appstudioApi.Snapshot, appNamespace string) error { 215 return wait.PollUntilContextTimeout(context.Background(), constants.PipelineRunPollingInterval, 10*time.Minute, true, func(ctx context.Context) (done bool, err error) { 216 pipelineRun, err := i.GetIntegrationPipelineRun(testScenario.Name, snapshot.Name, appNamespace) 217 if err != nil { 218 GinkgoWriter.Println("PipelineRun has not been created yet for test scenario %s and snapshot %s/%s", testScenario.GetName(), snapshot.GetNamespace(), snapshot.GetName()) 219 return false, nil 220 } 221 if controllerutil.ContainsFinalizer(pipelineRun, "test.appstudio.openshift.io/pipelinerun") { 222 GinkgoWriter.Printf("build pipelineRun %s/%s still contains the finalizer: %s", pipelineRun.GetNamespace(), pipelineRun.GetName(), "test.appstudio.openshift.io/pipelinerun") 223 return false, nil 224 } 225 226 return true, nil 227 }) 228 } 229 230 // GetAnnotationIfExists returns the value of a given annotation within a pipelinerun, if it exists. 231 func (i *IntegrationController) GetAnnotationIfExists(testNamespace, applicationName, componentName, annotationKey string) (string, error) { 232 pipelineRun, err := i.GetBuildPipelineRun(componentName, applicationName, testNamespace, false, "") 233 if err != nil { 234 return "", fmt.Errorf("pipelinerun for Component %s/%s can't be gotten successfully. Error: %v", testNamespace, componentName, err) 235 } 236 return pipelineRun.Annotations[annotationKey], nil 237 } 238 239 // WaitForBuildPipelineRunToGetAnnotated waits for given build pipeline to get annotated with a specific annotation. 240 // In case of failure, this function retries till it gets timed out. 241 func (i *IntegrationController) WaitForBuildPipelineRunToGetAnnotated(testNamespace, applicationName, componentName, annotationKey string) error { 242 return wait.PollUntilContextTimeout(context.Background(), constants.PipelineRunPollingInterval, 5*time.Minute, true, func(ctx context.Context) (done bool, err error) { 243 pipelineRun, err := i.GetBuildPipelineRun(componentName, applicationName, testNamespace, false, "") 244 if err != nil { 245 GinkgoWriter.Printf("pipelinerun for Component %s/%s can't be gotten successfully. Error: %v", testNamespace, componentName, err) 246 return false, nil 247 } 248 249 annotationValue, _ := i.GetAnnotationIfExists(testNamespace, applicationName, componentName, annotationKey) 250 if annotationValue == "" { 251 GinkgoWriter.Printf("build pipelinerun %s/%s doesn't contain annotation %s yet", testNamespace, pipelineRun.Name, annotationKey) 252 return false, nil 253 } 254 return true, nil 255 }) 256 }