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

     1  package tekton
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"strings"
     7  	"time"
     8  
     9  	g "github.com/onsi/ginkgo/v2"
    10  	"github.com/redhat-appstudio/e2e-tests/pkg/utils"
    11  	"github.com/tektoncd/pipeline/pkg/apis/pipeline/pod"
    12  	"k8s.io/apimachinery/pkg/types"
    13  	"k8s.io/apimachinery/pkg/util/wait"
    14  	pointer "k8s.io/utils/ptr"
    15  
    16  	pipeline "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
    17  	corev1 "k8s.io/api/core/v1"
    18  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    19  	crclient "sigs.k8s.io/controller-runtime/pkg/client"
    20  )
    21  
    22  // CreateTaskRunCopy creates a TaskRun that copies one image to a second image repository.
    23  func (t *TektonController) CreateTaskRunCopy(name, namespace, serviceAccountName, srcImageURL, destImageURL string) (*pipeline.TaskRun, error) {
    24  	taskRun := pipeline.TaskRun{
    25  		ObjectMeta: metav1.ObjectMeta{
    26  			Name:      name,
    27  			Namespace: namespace,
    28  		},
    29  		Spec: pipeline.TaskRunSpec{
    30  			ServiceAccountName: serviceAccountName,
    31  			TaskRef: &pipeline.TaskRef{
    32  				Name: "skopeo-copy",
    33  				Kind: pipeline.TaskKind("ClusterTask"),
    34  			},
    35  			Params: []pipeline.Param{
    36  				{
    37  					Name: "srcImageURL",
    38  					Value: pipeline.ParamValue{
    39  						StringVal: srcImageURL,
    40  						Type:      pipeline.ParamTypeString,
    41  					},
    42  				},
    43  				{
    44  					Name: "destImageURL",
    45  					Value: pipeline.ParamValue{
    46  						StringVal: destImageURL,
    47  						Type:      pipeline.ParamTypeString,
    48  					},
    49  				},
    50  			},
    51  			// workaround to avoid the error "container has runAsNonRoot and image will run as root"
    52  			PodTemplate: &pod.Template{
    53  				SecurityContext: &corev1.PodSecurityContext{
    54  					RunAsNonRoot: pointer.To[bool](true),
    55  					RunAsUser:    pointer.To[int64](65532),
    56  				},
    57  			},
    58  			Workspaces: []pipeline.WorkspaceBinding{
    59  				{
    60  					Name:     "images-url",
    61  					EmptyDir: &corev1.EmptyDirVolumeSource{},
    62  				},
    63  			},
    64  		},
    65  	}
    66  
    67  	err := t.KubeRest().Create(context.Background(), &taskRun)
    68  	if err != nil {
    69  		return nil, err
    70  	}
    71  	return &taskRun, nil
    72  }
    73  
    74  // GetTaskRun returns the requested TaskRun object.
    75  func (t *TektonController) GetTaskRun(name, namespace string) (*pipeline.TaskRun, error) {
    76  	namespacedName := types.NamespacedName{
    77  		Name:      name,
    78  		Namespace: namespace,
    79  	}
    80  
    81  	taskRun := pipeline.TaskRun{
    82  		ObjectMeta: metav1.ObjectMeta{
    83  			Name:      name,
    84  			Namespace: namespace,
    85  		},
    86  	}
    87  	err := t.KubeRest().Get(context.Background(), namespacedName, &taskRun)
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  	return &taskRun, nil
    92  }
    93  
    94  // GetTaskRunLogs returns logs of a specified taskRun.
    95  func (t *TektonController) GetTaskRunLogs(pipelineRunName, pipelineTaskName, namespace string) (map[string]string, error) {
    96  	tektonClient := t.PipelineClient().TektonV1beta1().PipelineRuns(namespace)
    97  	pipelineRun, err := tektonClient.Get(context.Background(), pipelineRunName, metav1.GetOptions{})
    98  	if err != nil {
    99  		return nil, err
   100  	}
   101  
   102  	podName := ""
   103  	for _, childStatusReference := range pipelineRun.Status.ChildReferences {
   104  		if childStatusReference.PipelineTaskName == pipelineTaskName {
   105  			taskRun := &pipeline.TaskRun{}
   106  			taskRunKey := types.NamespacedName{Namespace: pipelineRun.Namespace, Name: childStatusReference.Name}
   107  			if err := t.KubeRest().Get(context.Background(), taskRunKey, taskRun); err != nil {
   108  				return nil, err
   109  			}
   110  			podName = taskRun.Status.PodName
   111  			break
   112  		}
   113  	}
   114  	if podName == "" {
   115  		return nil, fmt.Errorf("task with %s name doesn't exist in %s pipelinerun", pipelineTaskName, pipelineRunName)
   116  	}
   117  
   118  	podClient := t.KubeInterface().CoreV1().Pods(namespace)
   119  	pod, err := podClient.Get(context.Background(), podName, metav1.GetOptions{})
   120  	if err != nil {
   121  		return nil, err
   122  	}
   123  
   124  	logs := make(map[string]string)
   125  	for _, container := range pod.Spec.Containers {
   126  		containerName := container.Name
   127  		if containerLogs, err := t.fetchContainerLog(podName, containerName, namespace); err == nil {
   128  			logs[containerName] = containerLogs
   129  		} else {
   130  			logs[containerName] = "failed to get logs"
   131  		}
   132  	}
   133  	return logs, nil
   134  }
   135  
   136  func (t *TektonController) GetTaskRunFromPipelineRun(c crclient.Client, pr *pipeline.PipelineRun, pipelineTaskName string) (*pipeline.TaskRun, error) {
   137  	for _, chr := range pr.Status.ChildReferences {
   138  		if chr.PipelineTaskName != pipelineTaskName {
   139  			continue
   140  		}
   141  
   142  		taskRun := &pipeline.TaskRun{}
   143  		taskRunKey := types.NamespacedName{Namespace: pr.Namespace, Name: chr.Name}
   144  		if err := c.Get(context.Background(), taskRunKey, taskRun); err != nil {
   145  			return nil, err
   146  		}
   147  		return taskRun, nil
   148  	}
   149  
   150  	return nil, fmt.Errorf("task %q not found in PipelineRun %q/%q", pipelineTaskName, pr.Namespace, pr.Name)
   151  }
   152  
   153  func (t *TektonController) GetTaskRunResult(c crclient.Client, pr *pipeline.PipelineRun, pipelineTaskName string, result string) (string, error) {
   154  	taskRun, err := t.GetTaskRunFromPipelineRun(c, pr, pipelineTaskName)
   155  	if err != nil {
   156  		return "", err
   157  	}
   158  
   159  	for _, trResult := range taskRun.Status.TaskRunStatusFields.Results {
   160  		if trResult.Name == result {
   161  			// for some reason the result might contain \n suffix
   162  			return strings.TrimSuffix(trResult.Value.StringVal, "\n"), nil
   163  		}
   164  	}
   165  	return "", fmt.Errorf(
   166  		"result %q not found in TaskRuns of PipelineRun %s/%s", result, pr.ObjectMeta.Namespace, pr.ObjectMeta.Name)
   167  }
   168  
   169  // GetTaskRunStatus returns the status of a specified taskRun.
   170  func (t *TektonController) GetTaskRunStatus(c crclient.Client, pr *pipeline.PipelineRun, pipelineTaskName string) (*pipeline.PipelineRunTaskRunStatus, error) {
   171  	for _, chr := range pr.Status.ChildReferences {
   172  		if chr.PipelineTaskName == pipelineTaskName {
   173  			taskRun := &pipeline.TaskRun{}
   174  			taskRunKey := types.NamespacedName{Namespace: pr.Namespace, Name: chr.Name}
   175  			if err := c.Get(context.Background(), taskRunKey, taskRun); err != nil {
   176  				return nil, err
   177  			}
   178  			return &pipeline.PipelineRunTaskRunStatus{PipelineTaskName: chr.PipelineTaskName, Status: &taskRun.Status}, nil
   179  		}
   180  	}
   181  	return nil, fmt.Errorf(
   182  		"TaskRun status for pipeline task name %q not found in the status of PipelineRun %s/%s", pipelineTaskName, pr.ObjectMeta.Namespace, pr.ObjectMeta.Name)
   183  }
   184  
   185  // DeleteAllTaskRunsInASpecificNamespace removes all TaskRuns from a given repository. Useful when creating a lot of resources and wanting to remove all of them.
   186  func (t *TektonController) DeleteAllTaskRunsInASpecificNamespace(namespace string) error {
   187  	return t.KubeRest().DeleteAllOf(context.Background(), &pipeline.TaskRun{}, crclient.InNamespace(namespace))
   188  }
   189  
   190  // GetTaskRunParam gets value of a TaskRun param.
   191  func (t *TektonController) GetTaskRunParam(c crclient.Client, pr *pipeline.PipelineRun, pipelineTaskName, paramName string) (string, error) {
   192  	taskRun, err := t.GetTaskRunFromPipelineRun(c, pr, pipelineTaskName)
   193  	if err != nil {
   194  		return "", err
   195  	}
   196  	for _, param := range taskRun.Spec.Params {
   197  		if param.Name == paramName {
   198  			return strings.TrimSpace(param.Value.StringVal), nil
   199  		}
   200  	}
   201  	return "", fmt.Errorf("cannot find param %s from TaskRun %s", paramName, pipelineTaskName)
   202  }
   203  
   204  func (t *TektonController) GetResultFromTaskRun(tr *pipeline.TaskRun, result string) (string, error) {
   205  	for _, trResult := range tr.Status.TaskRunStatusFields.Results {
   206  		if trResult.Name == result {
   207  			// for some reason the result might contain \n suffix
   208  			return strings.TrimSuffix(trResult.Value.StringVal, "\n"), nil
   209  		}
   210  	}
   211  	return "", fmt.Errorf(
   212  		"result %q not found in TaskRun %s/%s", result, tr.ObjectMeta.Namespace, tr.ObjectMeta.Name)
   213  }
   214  
   215  func (t *TektonController) GetEnvVariable(tr *pipeline.TaskRun, envVar string) (string, error) {
   216  	if tr.Status.TaskSpec != nil {
   217  		for _, trEnv := range tr.Status.TaskSpec.StepTemplate.Env {
   218  			if trEnv.Name == envVar {
   219  				return strings.TrimSuffix(trEnv.Value, "\n"), nil
   220  			}
   221  		}
   222  	}
   223  	return "", fmt.Errorf(
   224  		"env var %q not found in TaskRun %s/%s", envVar, tr.ObjectMeta.Namespace, tr.ObjectMeta.Name,
   225  	)
   226  }
   227  
   228  func (t *TektonController) WatchTaskRun(taskRunName, namespace string, taskTimeout int) error {
   229  	g.GinkgoWriter.Printf("Waiting for pipeline %q to finish\n", taskRunName)
   230  	return utils.WaitUntil(t.CheckTaskRunFinished(taskRunName, namespace), time.Duration(taskTimeout)*time.Second)
   231  }
   232  
   233  // CheckTaskRunFinished checks if taskRun finished.
   234  func (t *TektonController) CheckTaskRunFinished(taskRunName, namespace string) wait.ConditionFunc {
   235  	return func() (bool, error) {
   236  		tr, err := t.GetTaskRun(taskRunName, namespace)
   237  		if err != nil {
   238  			return false, nil
   239  		}
   240  		if tr.Status.CompletionTime != nil {
   241  			return true, nil
   242  		}
   243  		return false, nil
   244  	}
   245  }
   246  
   247  // CheckTaskRunSucceeded checks if taskRun succeeded. Returns error if getting taskRun fails.
   248  func (t *TektonController) CheckTaskRunSucceeded(taskRunName, namespace string) wait.ConditionFunc {
   249  	return func() (bool, error) {
   250  		tr, err := t.GetTaskRun(taskRunName, namespace)
   251  		if err != nil {
   252  			return false, err
   253  		}
   254  		if len(tr.Status.Conditions) > 0 {
   255  			for _, c := range tr.Status.Conditions {
   256  				if c.Type == "Succeeded" && c.Status == "True" {
   257  					return true, nil
   258  				}
   259  			}
   260  		}
   261  		return false, nil
   262  	}
   263  }
   264  
   265  func (t *TektonController) RunTaskAndWait(trSpec *pipeline.TaskRun, namespace string) (*pipeline.TaskRun, error) {
   266  	tr, err := t.CreateTaskRun(trSpec, namespace)
   267  	if err != nil {
   268  		return nil, err
   269  	}
   270  	err = t.WatchTaskRun(tr.Name, namespace, 100)
   271  	if err != nil {
   272  		return nil, err
   273  	}
   274  	return t.GetTaskRun(tr.Name, namespace)
   275  }
   276  
   277  func (t *TektonController) CreateTaskRun(taskRun *pipeline.TaskRun, ns string) (*pipeline.TaskRun, error) {
   278  	return t.PipelineClient().TektonV1().TaskRuns(ns).Create(context.Background(), taskRun, metav1.CreateOptions{})
   279  }