github.com/redhat-appstudio/e2e-tests@v0.0.0-20230619105049-9a422b2094d7/pkg/utils/has/controller.go (about) 1 package has 2 3 import ( 4 "context" 5 "fmt" 6 "strconv" 7 "strings" 8 "time" 9 10 "github.com/redhat-appstudio/e2e-tests/pkg/constants" 11 "github.com/redhat-appstudio/e2e-tests/pkg/utils" 12 "knative.dev/pkg/apis" 13 14 "github.com/devfile/library/pkg/util" 15 . "github.com/onsi/ginkgo/v2" 16 routev1 "github.com/openshift/api/route/v1" 17 appservice "github.com/redhat-appstudio/application-api/api/v1alpha1" 18 "github.com/redhat-appstudio/e2e-tests/pkg/apis/github" 19 kubeCl "github.com/redhat-appstudio/e2e-tests/pkg/apis/kubernetes" 20 "github.com/redhat-appstudio/e2e-tests/pkg/utils/tekton" 21 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" 22 appsv1 "k8s.io/api/apps/v1" 23 corev1 "k8s.io/api/core/v1" 24 k8sErrors "k8s.io/apimachinery/pkg/api/errors" 25 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 26 "k8s.io/apimachinery/pkg/labels" 27 "k8s.io/apimachinery/pkg/types" 28 "k8s.io/apimachinery/pkg/util/wait" 29 "k8s.io/client-go/util/retry" 30 "k8s.io/utils/pointer" 31 rclient "sigs.k8s.io/controller-runtime/pkg/client" 32 ) 33 34 type SuiteController struct { 35 Github *github.Github 36 *kubeCl.CustomClient 37 } 38 39 func NewSuiteController(kube *kubeCl.CustomClient) (*SuiteController, error) { 40 // Check if a github organization env var is set, if not use by default the redhat-appstudio-qe org. See: https://github.com/redhat-appstudio-qe 41 org := utils.GetEnv(constants.GITHUB_E2E_ORGANIZATION_ENV, "redhat-appstudio-qe") 42 token := utils.GetEnv(constants.GITHUB_TOKEN_ENV, "") 43 gh, err := github.NewGithubClient(token, org) 44 if err != nil { 45 return nil, err 46 } 47 return &SuiteController{ 48 gh, 49 kube, 50 }, nil 51 } 52 53 func (h *SuiteController) refreshComponentForErrorDebug(component *appservice.Component) *appservice.Component { 54 retComp := &appservice.Component{} 55 key := rclient.ObjectKeyFromObject(component) 56 err := h.KubeRest().Get(context.Background(), key, retComp) 57 if err != nil { 58 //TODO let's log this somehow, but return the original component obj, as that is better than nothing 59 return component 60 } 61 return retComp 62 } 63 64 func (h *SuiteController) refreshApplicationForErrorDebug(application *appservice.Application) *appservice.Application { 65 retApp := &appservice.Application{} 66 key := rclient.ObjectKeyFromObject(application) 67 err := h.KubeRest().Get(context.Background(), key, retApp) 68 if err != nil { 69 return application 70 } 71 return retApp 72 } 73 74 // GetHasApplication return the Application Custom Resource object 75 func (h *SuiteController) GetHasApplication(name, namespace string) (*appservice.Application, error) { 76 namespacedName := types.NamespacedName{ 77 Name: name, 78 Namespace: namespace, 79 } 80 81 application := appservice.Application{ 82 Spec: appservice.ApplicationSpec{}, 83 } 84 err := h.KubeRest().Get(context.TODO(), namespacedName, &application) 85 if err != nil { 86 return nil, err 87 } 88 return &application, nil 89 } 90 91 // CreateHasApplication create an application Custom Resource object 92 func (h *SuiteController) CreateHasApplication(name, namespace string) (*appservice.Application, error) { 93 return h.CreateHasApplicationWithTimeout(name, namespace, time.Minute*10) 94 } 95 96 // CreateHasApplicationWithTimeout create an application Custom Resource object 97 func (h *SuiteController) CreateHasApplicationWithTimeout(name string, namespace string, timeout time.Duration) (*appservice.Application, error) { 98 application := &appservice.Application{ 99 ObjectMeta: metav1.ObjectMeta{ 100 Name: name, 101 Namespace: namespace, 102 }, 103 Spec: appservice.ApplicationSpec{ 104 DisplayName: name, 105 }, 106 } 107 err := h.KubeRest().Create(context.TODO(), application) 108 if err != nil { 109 return nil, err 110 } 111 112 if err := utils.WaitUntil(h.ApplicationDevfilePresent(application), timeout); err != nil { 113 application = h.refreshApplicationForErrorDebug(application) 114 return nil, fmt.Errorf("timed out when waiting for devfile content creation for application %s in %s namespace: %+v. applicattion: %s", name, namespace, err, utils.ToPrettyJSONString(application)) 115 } 116 117 return application, nil 118 } 119 120 func (h *SuiteController) ApplicationDevfilePresent(application *appservice.Application) wait.ConditionFunc { 121 return func() (bool, error) { 122 app, err := h.GetHasApplication(application.Name, application.Namespace) 123 if err != nil { 124 return false, nil 125 } 126 application.Status = app.Status 127 return application.Status.Devfile != "", nil 128 } 129 } 130 131 // DeleteHasApplication delete a HAS Application resource from the namespace. 132 // Optionally, it can avoid returning an error if the resource did not exist: 133 // - specify 'false', if it's likely the Application has already been deleted (for example, because the Namespace was deleted) 134 func (h *SuiteController) DeleteHasApplication(name, namespace string, reportErrorOnNotFound bool) error { 135 application := appservice.Application{ 136 ObjectMeta: metav1.ObjectMeta{ 137 Name: name, 138 Namespace: namespace, 139 }, 140 } 141 if err := h.KubeRest().Delete(context.TODO(), &application); err != nil { 142 if !k8sErrors.IsNotFound(err) || (k8sErrors.IsNotFound(err) && reportErrorOnNotFound) { 143 return fmt.Errorf("error deleting an application: %+v", err) 144 } 145 } 146 return utils.WaitUntil(h.ApplicationDeleted(&application), 1*time.Minute) 147 } 148 149 func (h *SuiteController) ApplicationDeleted(application *appservice.Application) wait.ConditionFunc { 150 return func() (bool, error) { 151 _, err := h.GetHasApplication(application.Name, application.Namespace) 152 return err != nil && k8sErrors.IsNotFound(err), nil 153 } 154 } 155 156 // GetHasComponent returns the Appstudio Component Custom Resource object 157 func (h *SuiteController) GetHasComponent(name, namespace string) (*appservice.Component, error) { 158 namespacedName := types.NamespacedName{ 159 Name: name, 160 Namespace: namespace, 161 } 162 163 component := appservice.Component{} 164 err := h.KubeRest().Get(context.TODO(), namespacedName, &component) 165 if err != nil { 166 return nil, err 167 } 168 return &component, nil 169 } 170 171 // ScaleDeploymentReplicas scales the replicas of a given deployment 172 func (h *SuiteController) ScaleComponentReplicas(component *appservice.Component, replicas *int) (*appservice.Component, error) { 173 component.Spec.Replicas = replicas 174 175 err := h.KubeRest().Update(context.TODO(), component, &rclient.UpdateOptions{}) 176 if err != nil { 177 return &appservice.Component{}, err 178 } 179 return component, nil 180 } 181 182 // DeleteHasComponent delete an has component from a given name and namespace 183 func (h *SuiteController) DeleteHasComponent(name string, namespace string, reportErrorOnNotFound bool) error { 184 component := appservice.Component{ 185 ObjectMeta: metav1.ObjectMeta{ 186 Name: name, 187 Namespace: namespace, 188 }, 189 } 190 if err := h.KubeRest().Delete(context.TODO(), &component); err != nil { 191 if !k8sErrors.IsNotFound(err) || (k8sErrors.IsNotFound(err) && reportErrorOnNotFound) { 192 return fmt.Errorf("error deleting a component: %+v", err) 193 } 194 } 195 196 return utils.WaitUntil(h.ComponentDeleted(&component), 1*time.Minute) 197 } 198 199 // CreateComponent create an has component from a given name, namespace, application, devfile and a container image 200 func (h *SuiteController) CreateComponent(applicationName, componentName, namespace, gitSourceURL, gitSourceRevision, containerImageSource, outputContainerImage, secret string, skipInitialChecks bool) (*appservice.Component, error) { 201 var containerImage string 202 annotations := map[string]string{ 203 // PLNSRVCE-957 - if true, run only basic build pipeline tasks 204 "skip-initial-checks": strconv.FormatBool(skipInitialChecks), 205 } 206 if outputContainerImage != "" { 207 containerImage = outputContainerImage 208 } else if containerImageSource != "" { 209 containerImage = containerImageSource 210 } else { 211 // When no image image is selected then add annotatation to generate new image repository 212 annotations = utils.MergeMaps(annotations, constants.ImageControllerAnnotationDeleteRepoTrue) 213 } 214 component := &appservice.Component{ 215 ObjectMeta: metav1.ObjectMeta{ 216 Annotations: annotations, 217 Labels: constants.ComponentDefaultLabel, 218 Name: componentName, 219 Namespace: namespace, 220 }, 221 Spec: appservice.ComponentSpec{ 222 ComponentName: componentName, 223 Application: applicationName, 224 Source: appservice.ComponentSource{ 225 ComponentSourceUnion: appservice.ComponentSourceUnion{ 226 GitSource: &appservice.GitSource{ 227 URL: gitSourceURL, 228 Revision: gitSourceRevision, 229 }, 230 }, 231 }, 232 Secret: secret, 233 ContainerImage: containerImage, 234 Replicas: pointer.Int(1), 235 TargetPort: 8081, 236 Route: "", 237 }, 238 } 239 err := h.KubeRest().Create(context.TODO(), component) 240 if err != nil { 241 return nil, err 242 } 243 if err = utils.WaitUntil(h.ComponentReady(component), time.Minute*10); err != nil { 244 component = h.refreshComponentForErrorDebug(component) 245 return nil, fmt.Errorf("timed out when waiting for component %s to be ready in %s namespace. component: %s", componentName, namespace, utils.ToPrettyJSONString(component)) 246 } 247 return component, nil 248 } 249 250 func (h *SuiteController) ComponentReady(component *appservice.Component) wait.ConditionFunc { 251 return func() (bool, error) { 252 messages, err := h.GetHasComponentConditionStatusMessages(component.Name, component.Namespace) 253 if err != nil { 254 return false, nil 255 } 256 for _, m := range messages { 257 if strings.Contains(m, "success") { 258 return true, nil 259 } 260 } 261 return false, nil 262 } 263 } 264 265 func (h *SuiteController) ComponentDeleted(component *appservice.Component) wait.ConditionFunc { 266 return func() (bool, error) { 267 _, err := h.GetHasComponent(component.Name, component.Namespace) 268 return err != nil && k8sErrors.IsNotFound(err), nil 269 } 270 } 271 272 // CreateComponentWithPaCEnabled creates a component with "pipelinesascode: '1'" annotation that is used for triggering PaC builds 273 func (h *SuiteController) CreateComponentWithPaCEnabled(applicationName, componentName, namespace, gitSourceURL, baseBranch string, deleteRepo bool) (*appservice.Component, error) { 274 275 var annotations map[string]string 276 if deleteRepo { 277 annotations = utils.MergeMaps(constants.ComponentPaCRequestAnnotation, constants.ImageControllerAnnotationDeleteRepoTrue) 278 } else { 279 annotations = utils.MergeMaps(constants.ComponentPaCRequestAnnotation, constants.ImageControllerAnnotationDeleteRepoFalse) 280 } 281 282 component := &appservice.Component{ 283 ObjectMeta: metav1.ObjectMeta{ 284 Annotations: annotations, 285 Name: componentName, 286 Namespace: namespace, 287 }, 288 Spec: appservice.ComponentSpec{ 289 ComponentName: componentName, 290 Application: applicationName, 291 Source: appservice.ComponentSource{ 292 ComponentSourceUnion: appservice.ComponentSourceUnion{ 293 GitSource: &appservice.GitSource{ 294 URL: gitSourceURL, 295 Revision: baseBranch, 296 }, 297 }, 298 }, 299 }, 300 } 301 err := h.KubeRest().Create(context.TODO(), component) 302 if err != nil { 303 return nil, err 304 } 305 if err = utils.WaitUntil(h.ComponentReady(component), time.Minute*10); err != nil { 306 component = h.refreshComponentForErrorDebug(component) 307 return nil, fmt.Errorf("timed out when waiting for component %s to be ready in %s namespace. component: %s", componentName, namespace, utils.ToPrettyJSONString(component)) 308 } 309 return component, nil 310 } 311 312 func (h *SuiteController) CreateComponentFromStubSkipInitialChecks(compDetected appservice.ComponentDetectionDescription, namespace string, outputContainerImage string, secret string, applicationName string, skipInitialChecks bool) (*appservice.Component, error) { 313 component := &appservice.Component{ 314 ObjectMeta: metav1.ObjectMeta{ 315 // adding default label because of the BuildPipelineSelector in build test 316 Labels: constants.ComponentDefaultLabel, 317 Annotations: map[string]string{ 318 "skip-initial-checks": strconv.FormatBool(skipInitialChecks), 319 }, 320 Name: compDetected.ComponentStub.ComponentName, 321 Namespace: namespace, 322 }, 323 Spec: compDetected.ComponentStub, 324 } 325 326 if outputContainerImage != "" { 327 component.Spec.ContainerImage = outputContainerImage 328 } else { 329 component.Annotations = utils.MergeMaps(component.Annotations, constants.ImageControllerAnnotationDeleteRepoTrue) 330 } 331 332 if component.Spec.TargetPort == 0 { 333 component.Spec.TargetPort = 8081 334 } 335 336 component.Spec.Secret = secret 337 component.Spec.Application = applicationName 338 339 err := h.KubeRest().Create(context.TODO(), component) 340 if err != nil { 341 return nil, err 342 } 343 if err = utils.WaitUntil(h.ComponentReady(component), time.Minute*10); err != nil { 344 component = h.refreshComponentForErrorDebug(component) 345 return nil, fmt.Errorf("timed out when waiting for component %s to be ready in %s namespace. component: %s", compDetected.ComponentStub.ComponentName, namespace, utils.ToPrettyJSONString(component)) 346 } 347 return component, nil 348 } 349 350 // CreateComponentFromStub create a HAS Component resource from a Completed CDQ resource, which includes a stub Component CR 351 // The Component from the CDQ is only a template, and needs things like name filled in 352 func (h *SuiteController) CreateComponentFromStub(compDetected appservice.ComponentDetectionDescription, namespace string, outputContainerImage string, secret string, applicationName string) (*appservice.Component, error) { 353 return h.CreateComponentFromStubSkipInitialChecks(compDetected, namespace, outputContainerImage, secret, applicationName, true) 354 } 355 356 // DeleteHasComponent delete an has component from a given name and namespace 357 func (h *SuiteController) DeleteHasComponentDetectionQuery(name string, namespace string) error { 358 component := appservice.ComponentDetectionQuery{ 359 ObjectMeta: metav1.ObjectMeta{ 360 Name: name, 361 Namespace: namespace, 362 }, 363 } 364 return h.KubeRest().Delete(context.TODO(), &component) 365 } 366 367 // CreateComponentDetectionQuery create a has componentdetectionquery from a given name, namespace, and git source 368 func (h *SuiteController) CreateComponentDetectionQuery(cdqName, namespace, gitSourceURL, gitSourceRevision, gitSourceContext, secret string, isMultiComponent bool) (*appservice.ComponentDetectionQuery, error) { 369 return h.CreateComponentDetectionQueryWithTimeout(cdqName, namespace, gitSourceURL, gitSourceRevision, gitSourceContext, secret, isMultiComponent, 5*time.Minute) 370 } 371 372 // CreateComponentDetectionQueryWithTimeout create a has componentdetectionquery from a given name, namespace, and git source and waits for it to be read 373 func (h *SuiteController) CreateComponentDetectionQueryWithTimeout(cdqName, namespace, gitSourceURL, gitSourceRevision, gitSourceContext, secret string, isMultiComponent bool, timeout time.Duration) (*appservice.ComponentDetectionQuery, error) { 374 componentDetectionQuery := &appservice.ComponentDetectionQuery{ 375 ObjectMeta: metav1.ObjectMeta{ 376 Name: cdqName, 377 Namespace: namespace, 378 }, 379 Spec: appservice.ComponentDetectionQuerySpec{ 380 GitSource: appservice.GitSource{ 381 URL: gitSourceURL, 382 Revision: gitSourceRevision, 383 Context: gitSourceContext, 384 }, 385 Secret: secret, 386 }, 387 } 388 err := h.KubeRest().Create(context.TODO(), componentDetectionQuery) 389 if err != nil { 390 return nil, err 391 } 392 393 err = utils.WaitUntil(func() (done bool, err error) { 394 componentDetectionQuery, err = h.GetComponentDetectionQuery(componentDetectionQuery.Name, componentDetectionQuery.Namespace) 395 if err != nil { 396 return false, err 397 } 398 for _, condition := range componentDetectionQuery.Status.Conditions { 399 if condition.Type == "Completed" && len(componentDetectionQuery.Status.ComponentDetected) > 0 { 400 return true, nil 401 } 402 } 403 return false, nil 404 }, timeout) 405 406 if err != nil { 407 return nil, fmt.Errorf("error waiting for cdq to be ready: %v", err) 408 } 409 410 return componentDetectionQuery, nil 411 } 412 413 // GetComponentDetectionQuery return the status from the ComponentDetectionQuery Custom Resource object 414 func (h *SuiteController) GetComponentDetectionQuery(name, namespace string) (*appservice.ComponentDetectionQuery, error) { 415 namespacedName := types.NamespacedName{ 416 Name: name, 417 Namespace: namespace, 418 } 419 420 componentDetectionQuery := appservice.ComponentDetectionQuery{ 421 Spec: appservice.ComponentDetectionQuerySpec{}, 422 } 423 err := h.KubeRest().Get(context.TODO(), namespacedName, &componentDetectionQuery) 424 if err != nil { 425 return nil, err 426 } 427 return &componentDetectionQuery, nil 428 } 429 430 // GetComponentPipeline returns the pipeline for a given component labels 431 func (h *SuiteController) GetComponentPipelineRun(componentName, applicationName, namespace, sha string) (*v1beta1.PipelineRun, error) { 432 pipelineRunLabels := map[string]string{"appstudio.openshift.io/component": componentName, "appstudio.openshift.io/application": applicationName} 433 434 if sha != "" { 435 pipelineRunLabels["pipelinesascode.tekton.dev/sha"] = sha 436 } 437 438 list := &v1beta1.PipelineRunList{} 439 err := h.KubeRest().List(context.TODO(), list, &rclient.ListOptions{LabelSelector: labels.SelectorFromSet(pipelineRunLabels), Namespace: namespace}) 440 441 if err != nil && !k8sErrors.IsNotFound(err) { 442 return nil, fmt.Errorf("error listing pipelineruns in %s namespace: %v", namespace, err) 443 } 444 445 if len(list.Items) > 0 { 446 return &list.Items[0], nil 447 } 448 449 return nil, fmt.Errorf("no pipelinerun found for component %s", componentName) 450 } 451 452 // GetEventListenerRoute returns the route for a given component name's event listener 453 func (h *SuiteController) GetEventListenerRoute(componentName string, componentNamespace string) (*routev1.Route, error) { 454 namespacedName := types.NamespacedName{ 455 Name: fmt.Sprintf("el%s", componentName), 456 Namespace: componentNamespace, 457 } 458 route := &routev1.Route{} 459 err := h.KubeRest().Get(context.TODO(), namespacedName, route) 460 if err != nil { 461 return &routev1.Route{}, err 462 } 463 return route, nil 464 } 465 466 // GetComponentDeployment returns the deployment for a given component name 467 func (h *SuiteController) GetComponentDeployment(componentName string, componentNamespace string) (*appsv1.Deployment, error) { 468 namespacedName := types.NamespacedName{ 469 Name: fmt.Sprintf("el-%s", componentName), 470 Namespace: componentNamespace, 471 } 472 473 deployment := &appsv1.Deployment{} 474 err := h.KubeRest().Get(context.TODO(), namespacedName, deployment) 475 if err != nil { 476 return &appsv1.Deployment{}, err 477 } 478 return deployment, nil 479 } 480 481 // GetComponentService returns the service for a given component name 482 func (h *SuiteController) GetComponentService(componentName string, componentNamespace string) (*corev1.Service, error) { 483 namespacedName := types.NamespacedName{ 484 Name: fmt.Sprintf("el-%s", componentName), 485 Namespace: componentNamespace, 486 } 487 488 service := &corev1.Service{} 489 err := h.KubeRest().Get(context.TODO(), namespacedName, service) 490 if err != nil { 491 return &corev1.Service{}, err 492 } 493 return service, nil 494 } 495 496 func (h *SuiteController) WaitForComponentPipelineToBeFinished(component *appservice.Component, sha string, maxRetries int) error { 497 attempts := 1 498 app := component.Spec.Application 499 var pr *v1beta1.PipelineRun 500 501 for { 502 err := wait.PollImmediate(20*time.Second, 30*time.Minute, func() (done bool, err error) { 503 pr, err = h.GetComponentPipelineRun(component.GetName(), app, component.GetNamespace(), sha) 504 505 if err != nil { 506 GinkgoWriter.Println("PipelineRun has not been created yet") 507 return false, nil 508 } 509 510 GinkgoWriter.Printf("PipelineRun %s reason: %s\n", pr.Name, pr.GetStatusCondition().GetCondition(apis.ConditionSucceeded).GetReason()) 511 512 if !pr.IsDone() { 513 return false, nil 514 } 515 516 if pr.GetStatusCondition().GetCondition(apis.ConditionSucceeded).IsTrue() { 517 return true, nil 518 } else { 519 var prLogs string 520 if err = tekton.StorePipelineRun(pr, h.KubeRest(), h.KubeInterface()); err != nil { 521 GinkgoWriter.Printf("failed to store PipelineRun %s:%s: %s\n", pr.GetNamespace(), pr.GetName(), err.Error()) 522 } 523 if prLogs, err = tekton.GetFailedPipelineRunLogs(h.KubeRest(), h.KubeInterface(), pr); err != nil { 524 GinkgoWriter.Printf("failed to get logs for PipelineRun %s:%s: %s\n", pr.GetNamespace(), pr.GetName(), err.Error()) 525 } 526 return false, fmt.Errorf(prLogs) 527 } 528 }) 529 530 if err != nil { 531 GinkgoWriter.Printf("attempt %d/%d: PipelineRun %q failed: %+v", attempts, maxRetries+1, pr.GetName(), err) 532 // Retry the PipelineRun only in case we hit the known issue https://issues.redhat.com/browse/SRVKP-2749 533 if attempts == maxRetries+1 || pr.GetStatusCondition().GetCondition(apis.ConditionSucceeded).GetReason() != "CouldntGetTask" { 534 return err 535 } 536 if sha, err = h.RetriggerComponentPipelineRun(component, pr); err != nil { 537 return fmt.Errorf("unable to retrigger component %s:%s: %+v", component.GetNamespace(), component.GetName(), err) 538 } 539 attempts++ 540 } else { 541 break 542 } 543 } 544 545 return nil 546 547 } 548 549 // CreateComponentFromDevfile creates a has component from a given name, namespace, application, devfile and a container image 550 func (h *SuiteController) CreateComponentFromDevfile(applicationName, componentName, namespace, gitSourceURL, devfile, containerImageSource, outputContainerImage, secret string) (*appservice.Component, error) { 551 component := &appservice.Component{ 552 ObjectMeta: metav1.ObjectMeta{ 553 Name: componentName, 554 Namespace: namespace, 555 }, 556 Spec: appservice.ComponentSpec{ 557 ComponentName: componentName, 558 Application: applicationName, 559 Source: appservice.ComponentSource{ 560 ComponentSourceUnion: appservice.ComponentSourceUnion{ 561 GitSource: &appservice.GitSource{ 562 URL: gitSourceURL, 563 DevfileURL: devfile, 564 }, 565 }, 566 }, 567 Secret: secret, 568 Replicas: pointer.Int(1), 569 TargetPort: 8080, 570 Route: "", 571 }, 572 } 573 if outputContainerImage != "" { 574 component.Spec.ContainerImage = outputContainerImage 575 } else if containerImageSource != "" { 576 component.Spec.ContainerImage = containerImageSource 577 } else { 578 component.Annotations = constants.ImageControllerAnnotationDeleteRepoTrue 579 } 580 err := h.KubeRest().Create(context.TODO(), component) 581 if err != nil { 582 return nil, err 583 } 584 if err = utils.WaitUntil(h.ComponentReady(component), time.Minute*2); err != nil { 585 component = h.refreshComponentForErrorDebug(component) 586 return nil, fmt.Errorf("timed out when waiting for component %s to be ready in %s namespace. component: %s", componentName, namespace, utils.ToPrettyJSONString(component)) 587 } 588 return component, nil 589 } 590 591 // DeleteAllComponentsInASpecificNamespace removes all component CRs from a specific namespace. Useful when creating a lot of resources and want to remove all of them 592 func (h *SuiteController) DeleteAllComponentsInASpecificNamespace(namespace string, timeout time.Duration) error { 593 if err := h.KubeRest().DeleteAllOf(context.TODO(), &appservice.Component{}, rclient.InNamespace(namespace)); err != nil { 594 return fmt.Errorf("error deleting components from the namespace %s: %+v", namespace, err) 595 } 596 597 componentList := &appservice.ComponentList{} 598 return utils.WaitUntil(func() (done bool, err error) { 599 if err := h.KubeRest().List(context.Background(), componentList, &rclient.ListOptions{Namespace: namespace}); err != nil { 600 return false, nil 601 } 602 return len(componentList.Items) == 0, nil 603 }, timeout) 604 } 605 606 // DeleteAllApplicationsInASpecificNamespace removes all application CRs from a specific namespace. Useful when creating a lot of resources and want to remove all of them 607 func (h *SuiteController) DeleteAllApplicationsInASpecificNamespace(namespace string, timeout time.Duration) error { 608 if err := h.KubeRest().DeleteAllOf(context.TODO(), &appservice.Application{}, rclient.InNamespace(namespace)); err != nil { 609 return fmt.Errorf("error deleting applications from the namespace %s: %+v", namespace, err) 610 } 611 612 applicationList := &appservice.ApplicationList{} 613 return utils.WaitUntil(func() (done bool, err error) { 614 if err := h.KubeRest().List(context.Background(), applicationList, &rclient.ListOptions{Namespace: namespace}); err != nil { 615 return false, nil 616 } 617 return len(applicationList.Items) == 0, nil 618 }, timeout) 619 } 620 621 func (h *SuiteController) GetHasComponentConditionStatusMessages(name, namespace string) (messages []string, err error) { 622 c, err := h.GetHasComponent(name, namespace) 623 if err != nil { 624 return messages, fmt.Errorf("error getting HAS component: %v", err) 625 } 626 for _, condition := range c.Status.Conditions { 627 messages = append(messages, condition.Message) 628 } 629 return 630 } 631 632 // DeleteAllSnapshotEnvBindingsInASpecificNamespace removes all snapshotEnvironmentBindings from a specific namespace. Useful when creating a lot of resources and want to remove all of them 633 func (h *SuiteController) DeleteAllSnapshotEnvBindingsInASpecificNamespace(namespace string, timeout time.Duration) error { 634 if err := h.KubeRest().DeleteAllOf(context.TODO(), &appservice.SnapshotEnvironmentBinding{}, rclient.InNamespace(namespace)); err != nil { 635 return fmt.Errorf("error deleting snapshotEnvironmentBindings from the namespace %s: %+v", namespace, err) 636 } 637 638 snapshotEnvironmentBindingList := &appservice.SnapshotEnvironmentBindingList{} 639 return utils.WaitUntil(func() (done bool, err error) { 640 if err := h.KubeRest().List(context.Background(), snapshotEnvironmentBindingList, &rclient.ListOptions{Namespace: namespace}); err != nil { 641 return false, nil 642 } 643 return len(snapshotEnvironmentBindingList.Items) == 0, nil 644 }, timeout) 645 } 646 647 func (s *SuiteController) ApplicationGitopsRepoExists(devfileContent string) wait.ConditionFunc { 648 return func() (bool, error) { 649 gitOpsRepoURL := utils.ObtainGitOpsRepositoryName(devfileContent) 650 return s.Github.CheckIfRepositoryExist(gitOpsRepoURL), nil 651 } 652 } 653 654 func (h *SuiteController) PipelineRunDeleted(pr *v1beta1.PipelineRun) wait.ConditionFunc { 655 return func() (bool, error) { 656 o := &v1beta1.PipelineRun{} 657 err := h.KubeRest().Get(context.TODO(), types.NamespacedName{Name: pr.GetName(), Namespace: pr.GetNamespace()}, o) 658 return err != nil && k8sErrors.IsNotFound(err), nil 659 } 660 } 661 662 func (h *SuiteController) RetriggerComponentPipelineRun(component *appservice.Component, pr *v1beta1.PipelineRun) (sha string, err error) { 663 if err = h.KubeRest().Delete(context.TODO(), pr); err != nil { 664 return "", fmt.Errorf("failed to delete PipelineRun %q from %q namespace", pr.GetName(), pr.GetNamespace()) 665 } 666 667 prLabels := pr.GetLabels() 668 // In case of PipelineRun managed by PaC we are able to retrigger the pipeline only 669 // by updating the related branch 670 if prLabels["app.kubernetes.io/managed-by"] == "pipelinesascode.tekton.dev" { 671 var ok bool 672 var repoName, eventType, branchName string 673 pacRepoNameLabelName := "pipelinesascode.tekton.dev/url-repository" 674 pacEventTypeLabelName := "pipelinesascode.tekton.dev/event-type" 675 componentLabelName := "appstudio.openshift.io/component" 676 targetBranchAnnotationName := "build.appstudio.redhat.com/target_branch" 677 678 if repoName, ok = prLabels[pacRepoNameLabelName]; !ok { 679 return "", fmt.Errorf("cannot retrigger PipelineRun - required label %q not found", pacRepoNameLabelName) 680 } 681 if eventType, ok = prLabels[pacEventTypeLabelName]; !ok { 682 return "", fmt.Errorf("cannot retrigger PipelineRun - required label %q not found", pacEventTypeLabelName) 683 } 684 // PipelineRun is triggered from a pull request, need to update the PaC PR source branch 685 if eventType == "pull_request" { 686 if len(prLabels[componentLabelName]) < 1 { 687 return "", fmt.Errorf("cannot retrigger PipelineRun - required label %q not found", componentLabelName) 688 } 689 branchName = constants.PaCPullRequestBranchPrefix + prLabels[componentLabelName] 690 } else { 691 // No straightforward way to get a target branch from PR labels -> using annotation 692 if branchName, ok = pr.GetAnnotations()[targetBranchAnnotationName]; !ok { 693 return "", fmt.Errorf("cannot retrigger PipelineRun - required annotation %q not found", targetBranchAnnotationName) 694 } 695 } 696 file, err := h.Github.CreateFile(repoName, util.GenerateRandomString(5), "test", branchName) 697 if err != nil { 698 return "", fmt.Errorf("failed to retrigger PipelineRun %s in %s namespace: %+v", pr.GetName(), pr.GetNamespace(), err) 699 } 700 sha = file.GetSHA() 701 702 // To retrigger simple build PipelineRun we just need to update the initial build annotation 703 // in Component CR 704 } else { 705 err := retry.RetryOnConflict(retry.DefaultRetry, func() error { 706 component, err := h.GetHasComponent(component.GetName(), component.GetNamespace()) 707 if err != nil { 708 return fmt.Errorf("failed to get component for PipelineRun %q in %q namespace: %+v", pr.GetName(), pr.GetNamespace(), err) 709 } 710 delete(component.Annotations, constants.ComponentInitialBuildAnnotationKey) 711 if err = h.KubeRest().Update(context.Background(), component); err != nil { 712 return fmt.Errorf("failed to update Component %q in %q namespace", component.GetName(), component.GetNamespace()) 713 } 714 return err 715 }) 716 717 if err != nil { 718 return "", err 719 } 720 } 721 watch, err := h.PipelineClient().TektonV1beta1().PipelineRuns(component.GetNamespace()).Watch(context.Background(), metav1.ListOptions{}) 722 if err != nil { 723 return "", fmt.Errorf("error when initiating watch for new PipelineRun after retriggering it for component %s:%s", component.GetNamespace(), component.GetName()) 724 } 725 newPRFound := false 726 for { 727 select { 728 case <-time.After(5 * time.Minute): 729 return "", fmt.Errorf("timed out waiting for new PipelineRun to appear after retriggering it for component %s:%s", component.GetNamespace(), component.GetName()) 730 case event := <-watch.ResultChan(): 731 if event.Object == nil { 732 continue 733 } 734 newPR, ok := event.Object.(*v1beta1.PipelineRun) 735 if !ok { 736 continue 737 } 738 if pr.GetName() != newPR.GetName() { 739 newPRFound = true 740 } 741 } 742 if newPRFound { 743 break 744 } 745 } 746 747 return sha, nil 748 }