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 }