github.com/redhat-appstudio/e2e-tests@v0.0.0-20240520140907-9709f6f59323/pkg/clients/tekton/pipelineruns.go (about)

     1  package tekton
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  	"time"
     8  
     9  	"github.com/redhat-appstudio/e2e-tests/pkg/logs"
    10  	"github.com/redhat-appstudio/e2e-tests/pkg/utils"
    11  
    12  	"github.com/redhat-appstudio/e2e-tests/pkg/utils/tekton"
    13  	pipeline "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
    14  	"k8s.io/apimachinery/pkg/api/errors"
    15  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    16  	"k8s.io/apimachinery/pkg/util/wait"
    17  	"k8s.io/apimachinery/pkg/watch"
    18  	"sigs.k8s.io/controller-runtime/pkg/client"
    19  	crclient "sigs.k8s.io/controller-runtime/pkg/client"
    20  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    21  	"sigs.k8s.io/yaml"
    22  
    23  	g "github.com/onsi/ginkgo/v2"
    24  )
    25  
    26  // CreatePipelineRun creates a tekton pipelineRun and returns the pipelineRun or error
    27  func (t *TektonController) CreatePipelineRun(pipelineRun *pipeline.PipelineRun, ns string) (*pipeline.PipelineRun, error) {
    28  	return t.PipelineClient().TektonV1().PipelineRuns(ns).Create(context.Background(), pipelineRun, metav1.CreateOptions{})
    29  }
    30  
    31  // createAndWait creates a pipelineRun and waits until it starts.
    32  func (t *TektonController) createAndWait(pr *pipeline.PipelineRun, namespace string, taskTimeout int) (*pipeline.PipelineRun, error) {
    33  	pipelineRun, err := t.CreatePipelineRun(pr, namespace)
    34  	if err != nil {
    35  		return nil, err
    36  	}
    37  	g.GinkgoWriter.Printf("Creating Pipeline %q\n", pipelineRun.Name)
    38  	return pipelineRun, utils.WaitUntil(t.CheckPipelineRunStarted(pipelineRun.Name, namespace), time.Duration(taskTimeout)*time.Second)
    39  }
    40  
    41  // RunPipeline creates a pipelineRun and waits for it to start.
    42  func (t *TektonController) RunPipeline(g tekton.PipelineRunGenerator, namespace string, taskTimeout int) (*pipeline.PipelineRun, error) {
    43  	pr, err := g.Generate()
    44  	if err != nil {
    45  		return nil, err
    46  	}
    47  	pvcs := t.KubeInterface().CoreV1().PersistentVolumeClaims(pr.Namespace)
    48  	for _, w := range pr.Spec.Workspaces {
    49  		if w.PersistentVolumeClaim != nil {
    50  			pvcName := w.PersistentVolumeClaim.ClaimName
    51  			if _, err := pvcs.Get(context.Background(), pvcName, metav1.GetOptions{}); err != nil {
    52  				if errors.IsNotFound(err) {
    53  					err := tekton.CreatePVC(pvcs, pvcName)
    54  					if err != nil {
    55  						return nil, err
    56  					}
    57  				} else {
    58  					return nil, err
    59  				}
    60  			}
    61  		}
    62  	}
    63  
    64  	return t.createAndWait(pr, namespace, taskTimeout)
    65  }
    66  
    67  // GetPipelineRun returns a pipelineRun with a given name.
    68  func (t *TektonController) GetPipelineRun(pipelineRunName, namespace string) (*pipeline.PipelineRun, error) {
    69  	return t.PipelineClient().TektonV1().PipelineRuns(namespace).Get(context.Background(), pipelineRunName, metav1.GetOptions{})
    70  }
    71  
    72  // GetPipelineRunLogs returns logs of a given pipelineRun.
    73  func (t *TektonController) GetPipelineRunLogs(pipelineRunName, namespace string) (string, error) {
    74  	podClient := t.KubeInterface().CoreV1().Pods(namespace)
    75  	podList, err := podClient.List(context.Background(), metav1.ListOptions{})
    76  	if err != nil {
    77  		return "", err
    78  	}
    79  	podLog := ""
    80  	for _, pod := range podList.Items {
    81  		if !strings.HasPrefix(pod.Name, pipelineRunName) {
    82  			continue
    83  		}
    84  		for _, c := range pod.Spec.InitContainers {
    85  			var err error
    86  			var cLog string
    87  			cLog, err = t.fetchContainerLog(pod.Name, c.Name, namespace)
    88  			podLog = podLog + fmt.Sprintf("\ninit container %s: \n", c.Name) + cLog
    89  			if err != nil {
    90  				return podLog, err
    91  			}
    92  		}
    93  		for _, c := range pod.Spec.Containers {
    94  			var err error
    95  			var cLog string
    96  			cLog, err = t.fetchContainerLog(pod.Name, c.Name, namespace)
    97  			podLog = podLog + fmt.Sprintf("\ncontainer %s: \n", c.Name) + cLog
    98  			if err != nil {
    99  				return podLog, err
   100  			}
   101  		}
   102  	}
   103  	return podLog, nil
   104  }
   105  
   106  // GetPipelineRunWatch returns pipelineRun watch interface.
   107  func (t *TektonController) GetPipelineRunWatch(ctx context.Context, namespace string) (watch.Interface, error) {
   108  	return t.PipelineClient().TektonV1().PipelineRuns(namespace).Watch(ctx, metav1.ListOptions{})
   109  }
   110  
   111  // WatchPipelineRun waits until pipelineRun finishes.
   112  func (t *TektonController) WatchPipelineRun(pipelineRunName, namespace string, taskTimeout int) error {
   113  	g.GinkgoWriter.Printf("Waiting for pipeline %q to finish\n", pipelineRunName)
   114  	return utils.WaitUntil(t.CheckPipelineRunFinished(pipelineRunName, namespace), time.Duration(taskTimeout)*time.Second)
   115  }
   116  
   117  // WatchPipelineRunSucceeded waits until the pipelineRun succeeds.
   118  func (t *TektonController) WatchPipelineRunSucceeded(pipelineRunName, namespace string, taskTimeout int) error {
   119  	g.GinkgoWriter.Printf("Waiting for pipeline %q to finish\n", pipelineRunName)
   120  	return utils.WaitUntil(t.CheckPipelineRunSucceeded(pipelineRunName, namespace), time.Duration(taskTimeout)*time.Second)
   121  }
   122  
   123  // CheckPipelineRunStarted checks if pipelineRUn started.
   124  func (t *TektonController) CheckPipelineRunStarted(pipelineRunName, namespace string) wait.ConditionFunc {
   125  	return func() (bool, error) {
   126  		pr, err := t.GetPipelineRun(pipelineRunName, namespace)
   127  		if err != nil {
   128  			return false, nil
   129  		}
   130  		if pr.Status.StartTime != nil {
   131  			return true, nil
   132  		}
   133  		return false, nil
   134  	}
   135  }
   136  
   137  // CheckPipelineRunFinished checks if pipelineRun finished.
   138  func (t *TektonController) CheckPipelineRunFinished(pipelineRunName, namespace string) wait.ConditionFunc {
   139  	return func() (bool, error) {
   140  		pr, err := t.GetPipelineRun(pipelineRunName, namespace)
   141  		if err != nil {
   142  			return false, nil
   143  		}
   144  		if pr.Status.CompletionTime != nil {
   145  			return true, nil
   146  		}
   147  		return false, nil
   148  	}
   149  }
   150  
   151  // CheckPipelineRunSucceeded checks if pipelineRun succeeded. Returns error if getting pipelineRun fails.
   152  func (t *TektonController) CheckPipelineRunSucceeded(pipelineRunName, namespace string) wait.ConditionFunc {
   153  	return func() (bool, error) {
   154  		pr, err := t.GetPipelineRun(pipelineRunName, namespace)
   155  		if err != nil {
   156  			return false, err
   157  		}
   158  		if len(pr.Status.Conditions) > 0 {
   159  			for _, c := range pr.Status.Conditions {
   160  				if c.Type == "Succeeded" && c.Status == "True" {
   161  					return true, nil
   162  				}
   163  			}
   164  		}
   165  		return false, nil
   166  	}
   167  }
   168  
   169  // ListAllPipelineRuns returns a list of all pipelineRuns in a namespace.
   170  func (t *TektonController) ListAllPipelineRuns(ns string) (*pipeline.PipelineRunList, error) {
   171  	return t.PipelineClient().TektonV1().PipelineRuns(ns).List(context.Background(), metav1.ListOptions{})
   172  }
   173  
   174  // DeletePipelineRun deletes a pipelineRun form a given namespace.
   175  func (t *TektonController) DeletePipelineRun(name, ns string) error {
   176  	return t.PipelineClient().TektonV1().PipelineRuns(ns).Delete(context.Background(), name, metav1.DeleteOptions{})
   177  }
   178  
   179  // DeleteAllPipelineRunsInASpecificNamespace deletes all PipelineRuns in a given namespace (removing the finalizers field, first)
   180  func (t *TektonController) DeleteAllPipelineRunsInASpecificNamespace(ns string) error {
   181  
   182  	pipelineRunList, err := t.ListAllPipelineRuns(ns)
   183  	if err != nil || pipelineRunList == nil {
   184  		return fmt.Errorf("unable to delete all PipelineRuns in '%s': %v", ns, err)
   185  	}
   186  
   187  	for _, pipelineRun := range pipelineRunList.Items {
   188  		err := wait.PollUntilContextTimeout(context.Background(), time.Second, 30*time.Second, true, func(ctx context.Context) (done bool, err error) {
   189  			pipelineRunCR := pipeline.PipelineRun{
   190  				ObjectMeta: metav1.ObjectMeta{
   191  					Name:      pipelineRun.Name,
   192  					Namespace: ns,
   193  				},
   194  			}
   195  			if err := t.KubeRest().Get(context.Background(), crclient.ObjectKeyFromObject(&pipelineRunCR), &pipelineRunCR); err != nil {
   196  				if errors.IsNotFound(err) {
   197  					// PipelinerRun CR is already removed
   198  					return true, nil
   199  				}
   200  				g.GinkgoWriter.Printf("unable to retrieve PipelineRun '%s' in '%s': %v\n", pipelineRunCR.Name, pipelineRunCR.Namespace, err)
   201  				return false, nil
   202  
   203  			}
   204  
   205  			// Remove the finalizer, so that it can be deleted.
   206  			pipelineRunCR.Finalizers = []string{}
   207  			if err := t.KubeRest().Update(context.Background(), &pipelineRunCR); err != nil {
   208  				g.GinkgoWriter.Printf("unable to remove finalizers from PipelineRun '%s' in '%s': %v\n", pipelineRunCR.Name, pipelineRunCR.Namespace, err)
   209  				return false, nil
   210  			}
   211  
   212  			if err := t.KubeRest().Delete(context.Background(), &pipelineRunCR); err != nil {
   213  				g.GinkgoWriter.Printf("unable to delete PipelineRun '%s' in '%s': %v\n", pipelineRunCR.Name, pipelineRunCR.Namespace, err)
   214  				return false, nil
   215  			}
   216  			return true, nil
   217  		})
   218  		if err != nil {
   219  			return fmt.Errorf("deletion of PipelineRun '%s' in '%s' timed out", pipelineRun.Name, ns)
   220  		}
   221  
   222  	}
   223  
   224  	return nil
   225  }
   226  
   227  // StorePipelineRun stores a given PipelineRun as an artifact.
   228  func (t *TektonController) StorePipelineRun(pipelineRun *pipeline.PipelineRun) error {
   229  	artifacts := make(map[string][]byte)
   230  	pipelineRunLog, err := t.GetPipelineRunLogs(pipelineRun.Name, pipelineRun.Namespace)
   231  	if err != nil {
   232  		return err
   233  	}
   234  	artifacts["pipelineRun-"+pipelineRun.Name+".log"] = []byte(pipelineRunLog)
   235  
   236  	pipelineRunYaml, err := yaml.Marshal(pipelineRun)
   237  	if err != nil {
   238  		return err
   239  	}
   240  	artifacts["pipelineRun-"+pipelineRun.Name+".yaml"] = pipelineRunYaml
   241  
   242  	if err := logs.StoreArtifacts(artifacts); err != nil {
   243  		return err
   244  	}
   245  
   246  	return nil
   247  }
   248  
   249  // StoreAllPipelineRuns stores all PipelineRuns in a given namespace.
   250  func (t *TektonController) StoreAllPipelineRuns(namespace string) error {
   251  	pipelineRuns, err := t.ListAllPipelineRuns(namespace)
   252  	if err != nil {
   253  		return fmt.Errorf("got error fetching PR list: %v\n", err.Error())
   254  	}
   255  
   256  	for _, pipelineRun := range pipelineRuns.Items {
   257  		pipelineRun := pipelineRun
   258  		if err := t.StorePipelineRun(&pipelineRun); err != nil {
   259  			return fmt.Errorf("got error storing PR: %v\n", err.Error())
   260  		}
   261  	}
   262  
   263  	return nil
   264  }
   265  
   266  func (t *TektonController) AddFinalizerToPipelineRun(pipelineRun *pipeline.PipelineRun, finalizerName string) error {
   267  	ctx := context.Background()
   268  	kubeClient := t.KubeRest()
   269  	patch := crclient.MergeFrom(pipelineRun.DeepCopy())
   270  	if ok := controllerutil.AddFinalizer(pipelineRun, finalizerName); ok {
   271  		err := kubeClient.Patch(ctx, pipelineRun, patch)
   272  		if err != nil {
   273  			return fmt.Errorf("error occurred while patching the updated PipelineRun after finalizer addition: %v", err)
   274  		}
   275  	}
   276  	return nil
   277  }
   278  
   279  func (t *TektonController) RemoveFinalizerFromPipelineRun(pipelineRun *pipeline.PipelineRun, finalizerName string) error {
   280  	ctx := context.Background()
   281  	kubeClient := t.KubeRest()
   282  	patch := client.MergeFrom(pipelineRun.DeepCopy())
   283  	if ok := controllerutil.RemoveFinalizer(pipelineRun, finalizerName); ok {
   284  		err := kubeClient.Patch(ctx, pipelineRun, patch)
   285  		if err != nil {
   286  			return fmt.Errorf("error occurred while patching the updated PipelineRun after finalizer removal: %v", err)
   287  		}
   288  	}
   289  	return nil
   290  }