github.com/redhat-appstudio/e2e-tests@v0.0.0-20230619105049-9a422b2094d7/pkg/utils/integration/controller.go (about) 1 package integration 2 3 import ( 4 "context" 5 "fmt" 6 "time" 7 8 codereadytoolchainv1alpha1 "github.com/codeready-toolchain/api/api/v1alpha1" 9 "github.com/devfile/library/pkg/util" 10 . "github.com/onsi/ginkgo/v2" 11 appstudioApi "github.com/redhat-appstudio/application-api/api/v1alpha1" 12 kubeCl "github.com/redhat-appstudio/e2e-tests/pkg/apis/kubernetes" 13 "github.com/redhat-appstudio/e2e-tests/pkg/utils" 14 "github.com/redhat-appstudio/e2e-tests/pkg/utils/tekton" 15 integrationv1alpha1 "github.com/redhat-appstudio/integration-service/api/v1alpha1" 16 integrationv1beta1 "github.com/redhat-appstudio/integration-service/api/v1beta1" 17 releasev1alpha1 "github.com/redhat-appstudio/release-service/api/v1alpha1" 18 releasemetadata "github.com/redhat-appstudio/release-service/metadata" 19 tektonv1beta1 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" 20 k8sErrors "k8s.io/apimachinery/pkg/api/errors" 21 "k8s.io/apimachinery/pkg/api/meta" 22 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 "k8s.io/apimachinery/pkg/types" 24 "k8s.io/apimachinery/pkg/util/wait" 25 "knative.dev/pkg/apis" 26 "sigs.k8s.io/controller-runtime/pkg/client" 27 ) 28 29 type SuiteController struct { 30 *kubeCl.CustomClient 31 } 32 33 func NewSuiteController(kube *kubeCl.CustomClient) (*SuiteController, error) { 34 return &SuiteController{ 35 kube, 36 }, nil 37 } 38 39 func (h *SuiteController) HaveTestsSucceeded(snapshot *appstudioApi.Snapshot) bool { 40 return meta.IsStatusConditionTrue(snapshot.Status.Conditions, "HACBSTestSucceeded") || 41 meta.IsStatusConditionTrue(snapshot.Status.Conditions, "AppStudioTestSucceeded") 42 } 43 44 func (h *SuiteController) HaveTestsFinished(snapshot *appstudioApi.Snapshot) bool { 45 return meta.FindStatusCondition(snapshot.Status.Conditions, "HACBSTestSucceeded") != nil || 46 meta.FindStatusCondition(snapshot.Status.Conditions, "AppStudioTestSucceeded") != nil 47 } 48 49 func (h *SuiteController) MarkTestsSucceeded(snapshot *appstudioApi.Snapshot) (*appstudioApi.Snapshot, error) { 50 patch := client.MergeFrom(snapshot.DeepCopy()) 51 meta.SetStatusCondition(&snapshot.Status.Conditions, metav1.Condition{ 52 Type: "AppStudioTestSucceeded", 53 Status: metav1.ConditionTrue, 54 Reason: "Passed", 55 Message: "Snapshot Passed", 56 }) 57 err := h.KubeRest().Status().Patch(context.TODO(), snapshot, patch) 58 if err != nil { 59 return nil, err 60 } 61 return snapshot, nil 62 } 63 64 // GetSnapshot returns the Snapshot in the namespace and nil if it's not found 65 // It will search for the Snapshot based on the Snapshot name, associated PipelineRun name or Component name 66 // In the case the List operation fails, an error will be returned. 67 func (h *SuiteController) GetSnapshot(snapshotName, pipelineRunName, componentName, namespace string) (*appstudioApi.Snapshot, error) { 68 ctx := context.Background() 69 // If Snapshot name is provided, try to get the resource directly 70 if len(snapshotName) > 0 { 71 snapshot := &appstudioApi.Snapshot{} 72 if err := h.KubeRest().Get(ctx, types.NamespacedName{Name: snapshotName, Namespace: namespace}, snapshot); err != nil { 73 return nil, fmt.Errorf("couldn't find Snapshot with name '%s' in '%s' namespace", snapshotName, namespace) 74 } 75 return snapshot, nil 76 } 77 // Search for the Snapshot in the namespace based on the associated Component or PipelineRun 78 snapshots := &appstudioApi.SnapshotList{} 79 opts := []client.ListOption{ 80 client.InNamespace(namespace), 81 } 82 err := h.KubeRest().List(ctx, snapshots, opts...) 83 if err != nil { 84 return nil, fmt.Errorf("error when listing Snapshots in '%s' namespace", namespace) 85 } 86 for _, snapshot := range snapshots.Items { 87 if snapshot.Name == snapshotName { 88 return &snapshot, nil 89 } 90 // find snapshot by pipelinerun name 91 if len(pipelineRunName) > 0 && snapshot.Labels["appstudio.openshift.io/build-pipelinerun"] == pipelineRunName { 92 return &snapshot, nil 93 94 } 95 // find snapshot by component name 96 if len(componentName) > 0 && snapshot.Labels["appstudio.openshift.io/component"] == componentName { 97 return &snapshot, nil 98 99 } 100 } 101 return nil, fmt.Errorf("no snapshot found for component '%s', pipelineRun '%s' in '%s' namespace", componentName, pipelineRunName, namespace) 102 } 103 104 func (h *SuiteController) GetComponent(applicationName, namespace string) (*appstudioApi.Component, error) { 105 components := &appstudioApi.ComponentList{} 106 opts := []client.ListOption{ 107 client.InNamespace(namespace), 108 } 109 err := h.KubeRest().List(context.TODO(), components, opts...) 110 if err != nil { 111 return nil, err 112 } 113 for _, component := range components.Items { 114 if component.Spec.Application == applicationName { 115 return &component, nil 116 } 117 } 118 119 return &appstudioApi.Component{}, fmt.Errorf("no component found %s", utils.GetAdditionalInfo(applicationName, namespace)) 120 } 121 122 func (h *SuiteController) GetReleasesWithSnapshot(snapshot *appstudioApi.Snapshot, namespace string) (*[]releasev1alpha1.Release, error) { 123 releases := &releasev1alpha1.ReleaseList{} 124 opts := []client.ListOption{ 125 client.InNamespace(namespace), 126 } 127 128 err := h.KubeRest().List(context.TODO(), releases, opts...) 129 if err != nil { 130 return nil, err 131 } 132 133 for _, release := range releases.Items { 134 GinkgoWriter.Printf("Release %s is found\n", release.Name) 135 } 136 137 return &releases.Items, nil 138 } 139 140 // Get return the status from the Application Custom Resource object 141 func (h *SuiteController) GetIntegrationTestScenarios(applicationName, namespace string) (*[]integrationv1beta1.IntegrationTestScenario, error) { 142 opts := []client.ListOption{ 143 client.InNamespace(namespace), 144 } 145 146 integrationTestScenarioList := &integrationv1beta1.IntegrationTestScenarioList{} 147 err := h.KubeRest().List(context.TODO(), integrationTestScenarioList, opts...) 148 if err != nil { 149 return nil, err 150 } 151 152 items := make([]integrationv1beta1.IntegrationTestScenario, 0) 153 for _, t := range integrationTestScenarioList.Items { 154 if t.Spec.Application == applicationName { 155 items = append(items, t) 156 } 157 } 158 return &items, nil 159 } 160 161 func (h *SuiteController) CreateEnvironment(namespace string, environmenName string) (*appstudioApi.Environment, error) { 162 env := &appstudioApi.Environment{ 163 ObjectMeta: metav1.ObjectMeta{ 164 Name: environmenName, 165 Namespace: namespace, 166 }, 167 Spec: appstudioApi.EnvironmentSpec{ 168 Type: "POC", 169 DisplayName: "my-environment", 170 DeploymentStrategy: appstudioApi.DeploymentStrategy_Manual, 171 ParentEnvironment: "", 172 Tags: []string{}, 173 Configuration: appstudioApi.EnvironmentConfiguration{ 174 Env: []appstudioApi.EnvVarPair{ 175 { 176 Name: "var_name", 177 Value: "test", 178 }, 179 }, 180 }, 181 }, 182 } 183 184 if err := h.KubeRest().Create(context.TODO(), env); err != nil { 185 if err != nil { 186 if k8sErrors.IsAlreadyExists(err) { 187 environment := &appstudioApi.Environment{} 188 189 err := h.KubeRest().Get(context.TODO(), types.NamespacedName{ 190 Name: environmenName, 191 Namespace: namespace, 192 }, environment) 193 194 return environment, err 195 } else { 196 return nil, err 197 } 198 } 199 } 200 201 return env, nil 202 } 203 204 // DeleteEnvironment deletes default Environment from the namespace 205 func (h *SuiteController) DeleteEnvironment(namespace string) (*appstudioApi.Environment, error) { 206 env := &appstudioApi.Environment{ 207 ObjectMeta: metav1.ObjectMeta{ 208 Name: "envname", 209 Namespace: namespace, 210 }, 211 } 212 err := h.KubeRest().Delete(context.TODO(), env) 213 if err != nil { 214 return nil, err 215 } 216 217 return env, err 218 } 219 220 func (h *SuiteController) CreateSnapshot(applicationName, namespace, componentName, containerImage string) (*appstudioApi.Snapshot, error) { 221 hasSnapshot := &appstudioApi.Snapshot{ 222 ObjectMeta: metav1.ObjectMeta{ 223 Name: "snapshot-sample-" + util.GenerateRandomString(4), 224 Namespace: namespace, 225 Labels: map[string]string{ 226 "test.appstudio.openshift.io/type": "component", 227 "appstudio.openshift.io/component": componentName, 228 "pac.test.appstudio.openshift.io/event-type": "push", 229 }, 230 }, 231 Spec: appstudioApi.SnapshotSpec{ 232 Application: applicationName, 233 Components: []appstudioApi.SnapshotComponent{ 234 { 235 Name: componentName, 236 ContainerImage: containerImage, 237 }, 238 }, 239 }, 240 } 241 err := h.KubeRest().Create(context.TODO(), hasSnapshot) 242 if err != nil { 243 return nil, err 244 } 245 return hasSnapshot, err 246 } 247 248 func (h *SuiteController) DeleteSnapshot(hasSnapshot *appstudioApi.Snapshot, namespace string) error { 249 err := h.KubeRest().Delete(context.TODO(), hasSnapshot) 250 return err 251 } 252 253 func (h *SuiteController) DeleteIntegrationTestScenario(testScenario *integrationv1beta1.IntegrationTestScenario, namespace string) error { 254 err := h.KubeRest().Delete(context.TODO(), testScenario) 255 return err 256 } 257 258 //func (h *SuiteController) DeleteEnvironment(env *integrationv1alpha1.TestEnvironment, namespace string) error { 259 // err := h.KubeRest().Delete(context.TODO(), env) 260 // return err 261 //} 262 263 func (h *SuiteController) CreateReleasePlan(applicationName, namespace string) (*releasev1alpha1.ReleasePlan, error) { 264 testReleasePlan := &releasev1alpha1.ReleasePlan{ 265 ObjectMeta: metav1.ObjectMeta{ 266 GenerateName: "test-releaseplan-", 267 Namespace: namespace, 268 Labels: map[string]string{ 269 releasemetadata.AutoReleaseLabel: "true", 270 releasemetadata.AttributionLabel: "true", 271 }, 272 }, 273 Spec: releasev1alpha1.ReleasePlanSpec{ 274 Application: applicationName, 275 Target: "default", 276 }, 277 } 278 err := h.KubeRest().Create(context.TODO(), testReleasePlan) 279 if err != nil { 280 return nil, err 281 } 282 283 return testReleasePlan, err 284 } 285 286 func (h *SuiteController) CreateIntegrationPipelineRun(snapshotName, namespace, componentName, integrationTestScenarioName string) (*tektonv1beta1.PipelineRun, error) { 287 testpipelineRun := &tektonv1beta1.PipelineRun{ 288 ObjectMeta: metav1.ObjectMeta{ 289 GenerateName: "component-pipelinerun" + "-", 290 Namespace: namespace, 291 Labels: map[string]string{ 292 "pipelinesascode.tekton.dev/event-type": "push", 293 "appstudio.openshift.io/component": componentName, 294 "pipelines.appstudio.openshift.io/type": "test", 295 "appstudio.openshift.io/snapshot": snapshotName, 296 "test.appstudio.openshift.io/scenario": integrationTestScenarioName, 297 }, 298 }, 299 Spec: tektonv1beta1.PipelineRunSpec{ 300 PipelineRef: &tektonv1beta1.PipelineRef{ 301 Name: "integration-pipeline-pass", 302 Bundle: "quay.io/redhat-appstudio/example-tekton-bundle:integration-pipeline-pass", 303 }, 304 Params: []tektonv1beta1.Param{ 305 { 306 Name: "output-image", 307 Value: tektonv1beta1.ArrayOrString{ 308 Type: "string", 309 StringVal: "quay.io/redhat-appstudio/sample-image", 310 }, 311 }, 312 }, 313 }, 314 } 315 err := h.KubeRest().Create(context.TODO(), testpipelineRun) 316 if err != nil { 317 return nil, err 318 } 319 return testpipelineRun, err 320 } 321 322 func (h *SuiteController) CreateIntegrationTestScenario(applicationName, namespace, bundleURL, pipelineName string) (*integrationv1alpha1.IntegrationTestScenario, error) { 323 integrationTestScenario := &integrationv1alpha1.IntegrationTestScenario{ 324 ObjectMeta: metav1.ObjectMeta{ 325 Name: "example-pass-" + util.GenerateRandomString(4), 326 Namespace: namespace, 327 Labels: map[string]string{ 328 "test.appstudio.openshift.io/optional": "false", 329 }, 330 }, 331 Spec: integrationv1alpha1.IntegrationTestScenarioSpec{ 332 Application: applicationName, 333 Bundle: bundleURL, 334 Pipeline: pipelineName, 335 }, 336 } 337 338 err := h.KubeRest().Create(context.TODO(), integrationTestScenario) 339 if err != nil { 340 return nil, err 341 } 342 return integrationTestScenario, nil 343 } 344 345 func (h *SuiteController) CreateIntegrationTestScenarioWithEnvironment(applicationName, namespace, bundleURL, pipelineName, environmentName string) (*integrationv1alpha1.IntegrationTestScenario, error) { 346 integrationTestScenario := &integrationv1alpha1.IntegrationTestScenario{ 347 ObjectMeta: metav1.ObjectMeta{ 348 Name: "example-pass-" + util.GenerateRandomString(4), 349 Namespace: namespace, 350 Labels: map[string]string{ 351 "test.appstudio.openshift.io/optional": "false", 352 }, 353 }, 354 Spec: integrationv1alpha1.IntegrationTestScenarioSpec{ 355 Application: applicationName, 356 Bundle: bundleURL, 357 Pipeline: pipelineName, 358 Environment: integrationv1alpha1.TestEnvironment{ 359 Name: environmentName, 360 Type: "POC", 361 }, 362 }, 363 } 364 365 err := h.KubeRest().Create(context.TODO(), integrationTestScenario) 366 if err != nil { 367 return nil, err 368 } 369 return integrationTestScenario, nil 370 } 371 372 func (h *SuiteController) WaitForIntegrationPipelineToBeFinished(testScenario *integrationv1beta1.IntegrationTestScenario, snapshot *appstudioApi.Snapshot, applicationName string, appNamespace string) error { 373 return wait.PollImmediate(20*time.Second, 100*time.Minute, func() (done bool, err error) { 374 pipelineRun, _ := h.GetIntegrationPipelineRun(testScenario.Name, snapshot.Name, appNamespace) 375 376 for _, condition := range pipelineRun.Status.Conditions { 377 GinkgoWriter.Printf("PipelineRun %s reason: %s\n", pipelineRun.Name, condition.Reason) 378 379 if !pipelineRun.IsDone() { 380 return false, nil 381 } 382 383 if pipelineRun.GetStatusCondition().GetCondition(apis.ConditionSucceeded).IsTrue() { 384 return true, nil 385 } else { 386 return false, fmt.Errorf(tekton.GetFailedPipelineRunLogs(h.KubeRest(), h.KubeInterface(), pipelineRun)) 387 } 388 } 389 return false, nil 390 }) 391 } 392 393 // GetComponentPipeline returns the pipeline for a given component labels 394 func (h *SuiteController) GetBuildPipelineRun(componentName, applicationName, namespace string, pacBuild bool, sha string) (*tektonv1beta1.PipelineRun, error) { 395 pipelineRunLabels := map[string]string{"appstudio.openshift.io/component": componentName, "appstudio.openshift.io/application": applicationName, "pipelines.appstudio.openshift.io/type": "build"} 396 opts := []client.ListOption{ 397 client.InNamespace(namespace), 398 client.MatchingLabels{ 399 "pipelines.appstudio.openshift.io/type": "build", 400 "appstudio.openshift.io/application": applicationName, 401 "appstudio.openshift.io/component": componentName, 402 }, 403 } 404 405 if sha != "" { 406 pipelineRunLabels["pipelinesascode.tekton.dev/sha"] = sha 407 } 408 409 list := &tektonv1beta1.PipelineRunList{} 410 err := h.KubeRest().List(context.TODO(), list, opts...) 411 412 if err != nil && !k8sErrors.IsNotFound(err) { 413 return nil, fmt.Errorf("error listing pipelineruns in %s namespace: %v", namespace, err) 414 } 415 416 if len(list.Items) > 0 { 417 return &list.Items[0], nil 418 } 419 420 return &tektonv1beta1.PipelineRun{}, fmt.Errorf("no pipelinerun found for component %s %s", componentName, utils.GetAdditionalInfo(applicationName, namespace)) 421 } 422 423 // GetComponentPipeline returns the pipeline for a given component labels 424 func (h *SuiteController) GetIntegrationPipelineRun(integrationTestScenarioName string, snapshotName string, namespace string) (*tektonv1beta1.PipelineRun, error) { 425 426 opts := []client.ListOption{ 427 client.InNamespace(namespace), 428 client.MatchingLabels{ 429 "pipelines.appstudio.openshift.io/type": "test", 430 "test.appstudio.openshift.io/scenario": integrationTestScenarioName, 431 "appstudio.openshift.io/snapshot": snapshotName, 432 }, 433 } 434 435 list := &tektonv1beta1.PipelineRunList{} 436 err := h.KubeRest().List(context.TODO(), list, opts...) 437 438 if err != nil && !k8sErrors.IsNotFound(err) { 439 return nil, fmt.Errorf("error listing pipelineruns in %s namespace", namespace) 440 } 441 442 if len(list.Items) > 0 { 443 return &list.Items[0], nil 444 } 445 446 return &tektonv1beta1.PipelineRun{}, fmt.Errorf("no pipelinerun found for integrationTestScenario %s (snapshot: %s, namespace: %s)", integrationTestScenarioName, snapshotName, namespace) 447 } 448 449 // GetComponentPipeline returns the pipeline for a given component labels 450 func (h *SuiteController) GetSnapshotEnvironmentBinding(applicationName string, namespace string, environment *appstudioApi.Environment) (*appstudioApi.SnapshotEnvironmentBinding, error) { 451 snapshotEnvironmentBindingList := &appstudioApi.SnapshotEnvironmentBindingList{} 452 opts := []client.ListOption{ 453 client.InNamespace(namespace), 454 } 455 456 err := h.KubeRest().List(context.TODO(), snapshotEnvironmentBindingList, opts...) 457 if err != nil { 458 return nil, err 459 } 460 461 for _, binding := range snapshotEnvironmentBindingList.Items { 462 if binding.Spec.Application == applicationName && binding.Spec.Environment == environment.Name { 463 return &binding, nil 464 } 465 } 466 467 return &appstudioApi.SnapshotEnvironmentBinding{}, fmt.Errorf("no SnapshotEnvironmentBinding found in environment %s %s", environment.Name, utils.GetAdditionalInfo(applicationName, namespace)) 468 } 469 470 // HaveAvailableDeploymentTargetClassExist attempts to find a DeploymentTargetClass with appstudioApi.Provisioner_Devsandbox as provisioner. 471 // reurn nil if not found 472 func (h *SuiteController) HaveAvailableDeploymentTargetClassExist() (*appstudioApi.DeploymentTargetClass, error) { 473 deploymentTargetClassList := &appstudioApi.DeploymentTargetClassList{} 474 err := h.KubeRest().List(context.TODO(), deploymentTargetClassList) 475 if err != nil && !k8sErrors.IsNotFound(err) { 476 return nil, fmt.Errorf("error occurred while trying to list all the available DeploymentTargetClass: %v", err) 477 } 478 479 for _, dtcls := range deploymentTargetClassList.Items { 480 if dtcls.Spec.Provisioner == appstudioApi.Provisioner_Devsandbox { 481 return &dtcls, nil 482 } 483 } 484 485 return nil, nil 486 } 487 488 func (h *SuiteController) GetSpaceRequests(namespace string) (*codereadytoolchainv1alpha1.SpaceRequestList, error) { 489 spaceRequestList := &codereadytoolchainv1alpha1.SpaceRequestList{} 490 491 opts := []client.ListOption{ 492 client.InNamespace(namespace), 493 } 494 495 err := h.KubeRest().List(context.Background(), spaceRequestList, opts...) 496 if err != nil && !k8sErrors.IsNotFound(err) { 497 return nil, fmt.Errorf("error occurred while trying to list spaceRequests in %s namespace: %v", namespace, err) 498 } 499 500 return spaceRequestList, nil 501 } 502 503 func (h *SuiteController) GetDeploymentTargets(namespace string) (*appstudioApi.DeploymentTargetList, error) { 504 deploymentTargetList := &appstudioApi.DeploymentTargetList{} 505 506 opts := []client.ListOption{ 507 client.InNamespace(namespace), 508 } 509 510 err := h.KubeRest().List(context.Background(), deploymentTargetList, opts...) 511 if err != nil && !k8sErrors.IsNotFound(err) { 512 return nil, fmt.Errorf("error occurred while trying to list deploymentTargets in %s namespace: %v", namespace, err) 513 } 514 515 return deploymentTargetList, nil 516 } 517 518 func (h *SuiteController) GetDeploymentTargetClaims(namespace string) (*appstudioApi.DeploymentTargetClaimList, error) { 519 deploymentTargetClaimList := &appstudioApi.DeploymentTargetClaimList{} 520 521 opts := []client.ListOption{ 522 client.InNamespace(namespace), 523 } 524 525 err := h.KubeRest().List(context.Background(), deploymentTargetClaimList, opts...) 526 if err != nil && !k8sErrors.IsNotFound(err) { 527 return nil, fmt.Errorf("error occurred while trying to list DeploymentTargetClaim in %s namespace: %v", namespace, err) 528 } 529 530 return deploymentTargetClaimList, nil 531 } 532 533 func (h *SuiteController) GetEnvironments(namespace string) (*appstudioApi.EnvironmentList, error) { 534 environmentList := &appstudioApi.EnvironmentList{} 535 opts := []client.ListOption{ 536 client.InNamespace(namespace), 537 } 538 539 err := h.KubeRest().List(context.TODO(), environmentList, opts...) 540 541 if err != nil && !k8sErrors.IsNotFound(err) { 542 return nil, fmt.Errorf("error occurred while trying to list environments in %s namespace: %v", namespace, err) 543 } 544 545 return environmentList, nil 546 } 547 548 func (h *SuiteController) CreateIntegrationTestScenario_beta1(applicationName, namespace, gitURL, revision, pathInRepo string) (*integrationv1beta1.IntegrationTestScenario, error) { 549 integrationTestScenario := &integrationv1beta1.IntegrationTestScenario{ 550 ObjectMeta: metav1.ObjectMeta{ 551 Name: "example-resolver-pass-" + util.GenerateRandomString(4), 552 Namespace: namespace, 553 Labels: map[string]string{ 554 "test.appstudio.openshift.io/optional": "false", 555 }, 556 }, 557 Spec: integrationv1beta1.IntegrationTestScenarioSpec{ 558 Application: applicationName, 559 ResolverRef: integrationv1beta1.ResolverRef{ 560 Resolver: "git", 561 Params: []integrationv1beta1.ResolverParameter{ 562 { 563 Name: "url", 564 Value: gitURL, 565 }, 566 { 567 Name: "revision", 568 Value: revision, 569 }, 570 { 571 Name: "pathInRepo", 572 Value: pathInRepo, 573 }, 574 }, 575 }, 576 }, 577 } 578 579 err := h.KubeRest().Create(context.TODO(), integrationTestScenario) 580 if err != nil { 581 return nil, err 582 } 583 return integrationTestScenario, nil 584 }