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  }