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 }