github.com/redhat-appstudio/e2e-tests@v0.0.0-20240520140907-9709f6f59323/tests/rhtap-demo/rhtap-demo.go (about) 1 package rhtap_demo 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 "os" 8 "strings" 9 "time" 10 11 buildcontrollers "github.com/redhat-appstudio/build-service/controllers" 12 tektonutils "github.com/redhat-appstudio/release-service/tekton/utils" 13 14 "github.com/redhat-appstudio/jvm-build-service/openshift-with-appstudio-test/e2e" 15 jvmclientSet "github.com/redhat-appstudio/jvm-build-service/pkg/client/clientset/versioned" 16 pipelineclientset "github.com/tektoncd/pipeline/pkg/client/clientset/versioned" 17 "k8s.io/client-go/kubernetes" 18 19 "github.com/devfile/library/v2/pkg/util" 20 ecp "github.com/enterprise-contract/enterprise-contract-controller/api/v1alpha1" 21 "github.com/google/go-github/v44/github" 22 . "github.com/onsi/ginkgo/v2" 23 . "github.com/onsi/gomega" 24 appservice "github.com/redhat-appstudio/application-api/api/v1alpha1" 25 "github.com/redhat-appstudio/e2e-tests/pkg/clients/has" 26 "github.com/redhat-appstudio/e2e-tests/pkg/constants" 27 "github.com/redhat-appstudio/e2e-tests/pkg/framework" 28 "github.com/redhat-appstudio/e2e-tests/pkg/utils" 29 "github.com/redhat-appstudio/e2e-tests/pkg/utils/build" 30 "github.com/redhat-appstudio/e2e-tests/pkg/utils/tekton" 31 "github.com/redhat-appstudio/jvm-build-service/pkg/apis/jvmbuildservice/v1alpha1" 32 corev1 "k8s.io/api/core/v1" 33 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 34 35 releasecommon "github.com/redhat-appstudio/e2e-tests/tests/release" 36 integrationv1beta1 "github.com/konflux-ci/integration-service/api/v1beta1" 37 releaseApi "github.com/redhat-appstudio/release-service/api/v1alpha1" 38 tektonapi "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" 39 40 e2eConfig "github.com/redhat-appstudio/e2e-tests/tests/rhtap-demo/config" 41 ) 42 43 const ( 44 45 // Secret Name created by spi to interact with github 46 SPIGithubSecretName string = "e2e-github-secret" 47 48 // Environment name used for e2e-tests demos 49 SPIQuaySecretName string = "e2e-quay-secret" 50 51 // Timeouts 52 appDeployTimeout = time.Minute * 20 53 appRouteAvailableTimeout = time.Minute * 5 54 customResourceUpdateTimeout = time.Minute * 10 55 jvmRebuildTimeout = time.Minute * 40 56 mergePRTimeout = time.Minute * 1 57 pipelineRunStartedTimeout = time.Minute * 5 58 pullRequestCreationTimeout = time.Minute * 5 59 releasePipelineTimeout = time.Minute * 15 60 snapshotTimeout = time.Minute * 4 61 releaseTimeout = time.Minute * 4 62 testPipelineTimeout = time.Minute * 15 63 branchCreateTimeout = time.Minute * 1 64 65 // Intervals 66 defaultPollingInterval = time.Second * 2 67 jvmRebuildPollingInterval = time.Second * 10 68 snapshotPollingInterval = time.Second * 1 69 releasePollingInterval = time.Second * 1 70 71 // test metadata 72 devEnvTestLabel = "rhtap-demo" 73 74 // stage env test related env vars 75 stageTimeout = time.Minute * 5 76 stageEnvTestLabel = "verify-stage" 77 ) 78 79 var supportedRuntimes = []string{"Dockerfile", "Node.js", "Go", "Quarkus", "Python", "JavaScript", "springboot", "dotnet", "maven"} 80 81 var _ = framework.RhtapDemoSuiteDescribe(func() { 82 defer GinkgoRecover() 83 84 var timeout, interval time.Duration 85 var namespace string 86 var err error 87 88 // Initialize the application struct 89 application := &appservice.Application{} 90 snapshot := &appservice.Snapshot{} 91 92 fw := &framework.Framework{} 93 AfterEach(framework.ReportFailure(&fw)) 94 var token, ssourl, apiurl string 95 var TestScenarios []e2eConfig.TestSpec 96 97 if strings.Contains(GinkgoLabelFilter(), stageEnvTestLabel) { 98 TestScenarios = append(TestScenarios, e2eConfig.GetScenarios(true)...) 99 } else { 100 TestScenarios = append(TestScenarios, e2eConfig.GetScenarios(false)...) 101 } 102 103 for _, appTest := range TestScenarios { 104 appTest := appTest 105 if !appTest.Skip { 106 107 Describe(appTest.Name, Ordered, func() { 108 BeforeAll(func() { 109 if strings.Contains(GinkgoLabelFilter(), stageEnvTestLabel) { 110 token = utils.GetEnv("STAGEUSER_TOKEN", "") 111 ssourl = utils.GetEnv("STAGE_SSOURL", "") 112 apiurl = utils.GetEnv("STAGE_APIURL", "") 113 username := utils.GetEnv("STAGE_USERNAME", "") 114 fw, err = framework.NewFrameworkWithTimeout(username, stageTimeout, utils.Options{ 115 ToolchainApiUrl: apiurl, 116 KeycloakUrl: ssourl, 117 OfflineToken: token, 118 }) 119 } else { 120 fw, err = framework.NewFramework(utils.GetGeneratedNamespace(devEnvTestLabel)) 121 } 122 Expect(err).NotTo(HaveOccurred()) 123 namespace = fw.UserNamespace 124 Expect(err).NotTo(HaveOccurred()) 125 126 suiteConfig, _ := GinkgoConfiguration() 127 GinkgoWriter.Printf("Parallel processes: %d\n", suiteConfig.ParallelTotal) 128 GinkgoWriter.Printf("Running on namespace: %s\n", namespace) 129 GinkgoWriter.Printf("User: %s\n", fw.UserName) 130 }) 131 132 // Remove all resources created by the tests 133 AfterAll(func() { 134 if !appTest.Stage { 135 // collect SPI ResourceQuota metrics (temporary) 136 err := fw.AsKubeAdmin.CommonController.GetResourceQuotaInfo(devEnvTestLabel, namespace, "appstudio-crds-spi") 137 Expect(err).NotTo(HaveOccurred()) 138 139 if !(strings.EqualFold(os.Getenv("E2E_SKIP_CLEANUP"), "true")) && !CurrentSpecReport().Failed() { // RHTAPBUGS-978: temporary timeout to 15min 140 if err := fw.AsKubeAdmin.HasController.DeleteAllComponentsInASpecificNamespace(namespace, 15*time.Minute); err != nil { 141 if err := fw.AsKubeAdmin.StoreAllArtifactsForNamespace(namespace); err != nil { 142 Fail(fmt.Sprintf("error archiving artifacts:\n%s", err)) 143 } 144 Fail(fmt.Sprintf("error deleting all componentns in namespace:\n%s", err)) 145 } 146 Expect(fw.AsKubeAdmin.HasController.DeleteAllApplicationsInASpecificNamespace(namespace, 30*time.Second)).To(Succeed()) 147 Expect(fw.AsKubeAdmin.IntegrationController.DeleteAllSnapshotsInASpecificNamespace(namespace, 30*time.Second)).To(Succeed()) 148 Expect(fw.AsKubeAdmin.TektonController.DeleteAllPipelineRunsInASpecificNamespace(namespace)).To(Succeed()) 149 Expect(fw.SandboxController.DeleteUserSignup(fw.UserName)).To(BeTrue()) 150 } 151 } else { 152 err := fw.AsKubeDeveloper.HasController.DeleteAllApplicationsInASpecificNamespace(fw.UserNamespace, stageTimeout) 153 if err != nil { 154 GinkgoWriter.Println("Error while deleting resources for user, got error: %v\n", err) 155 } 156 Expect(err).NotTo(HaveOccurred()) 157 err = fw.AsKubeDeveloper.HasController.DeleteAllComponentDetectionQueriesInASpecificNamespace(fw.UserNamespace, stageTimeout) 158 if err != nil { 159 GinkgoWriter.Println("while deleting component detection queries for user, got error: %v\n", err) 160 } 161 Expect(err).NotTo(HaveOccurred()) 162 } 163 }) 164 165 // Create an application in a specific namespace 166 It("creates an application", Label(devEnvTestLabel, stageEnvTestLabel), func() { 167 GinkgoWriter.Printf("Parallel process %d\n", GinkgoParallelProcess()) 168 createdApplication, err := fw.AsKubeDeveloper.HasController.CreateApplication(appTest.ApplicationName, namespace) 169 Expect(err).NotTo(HaveOccurred()) 170 Expect(createdApplication.Spec.DisplayName).To(Equal(appTest.ApplicationName)) 171 Expect(createdApplication.Namespace).To(Equal(namespace)) 172 }) 173 174 It("checks if application is healthy", Label(devEnvTestLabel, stageEnvTestLabel), func() { 175 Eventually(func() string { 176 application, err = fw.AsKubeDeveloper.HasController.GetApplication(appTest.ApplicationName, namespace) 177 Expect(err).NotTo(HaveOccurred()) 178 179 return application.Status.Devfile 180 }, 3*time.Minute, 100*time.Millisecond).Should(Not(BeEmpty()), fmt.Sprintf("timed out waiting for the %s application in %s namespace to be ready", appTest.ApplicationName, fw.UserNamespace)) 181 }) 182 183 for _, componentSpec := range appTest.Components { 184 componentSpec := componentSpec 185 var componentNewBaseBranch string 186 componentRepositoryName := utils.ExtractGitRepositoryNameFromURL(componentSpec.GitSourceUrl) 187 cdq := &appservice.ComponentDetectionQuery{} 188 componentList := []*appservice.Component{} 189 var secret string 190 191 if componentSpec.Private { 192 secret = SPIGithubSecretName 193 It(fmt.Sprintf("injects manually SPI token for component %s", componentSpec.Name), Label(devEnvTestLabel, stageEnvTestLabel), func() { 194 // Inject spi tokens to work with private components 195 if componentSpec.ContainerSource != "" { 196 // More info about manual token upload for quay.io here: https://github.com/redhat-appstudio/service-provider-integration-operator/pull/115 197 oauthCredentials := `{"access_token":"` + utils.GetEnv(constants.QUAY_OAUTH_TOKEN_ENV, "") + `", "username":"` + utils.GetEnv(constants.QUAY_OAUTH_USER_ENV, "") + `"}` 198 199 _ = fw.AsKubeAdmin.SPIController.InjectManualSPIToken(namespace, componentSpec.ContainerSource, oauthCredentials, corev1.SecretTypeDockerConfigJson, SPIQuaySecretName) 200 } 201 githubCredentials := `{"access_token":"` + utils.GetEnv(constants.GITHUB_TOKEN_ENV, "") + `"}` 202 _ = fw.AsKubeDeveloper.SPIController.InjectManualSPIToken(namespace, componentSpec.GitSourceUrl, githubCredentials, corev1.SecretTypeBasicAuth, SPIGithubSecretName) 203 }) 204 } 205 206 It(fmt.Sprintf("creates componentdetectionquery for component %s", componentSpec.Name), Label(devEnvTestLabel, stageEnvTestLabel), func() { 207 gitRevision := componentSpec.GitSourceRevision 208 // In case the advanced build (PaC) is enabled for this component, 209 // we need to create a new branch that we will target 210 // and that will contain the PaC configuration, so we can avoid polluting the default (main) branch 211 if componentSpec.AdvancedBuildSpec != nil { 212 componentNewBaseBranch = fmt.Sprintf("base-%s", util.GenerateRandomString(6)) 213 gitRevision = componentNewBaseBranch 214 Expect(fw.AsKubeAdmin.CommonController.Github.CreateRef(componentRepositoryName, componentSpec.GitSourceDefaultBranchName, componentSpec.GitSourceRevision, componentNewBaseBranch)).To(Succeed()) 215 } 216 cdq, err = fw.AsKubeDeveloper.HasController.CreateComponentDetectionQuery(componentSpec.Name, namespace, componentSpec.GitSourceUrl, gitRevision, componentSpec.GitSourceContext, secret, false) 217 Expect(err).NotTo(HaveOccurred()) 218 }) 219 220 It("checks if components have supported languages by AppStudio", Label(devEnvTestLabel, stageEnvTestLabel), func() { 221 if appTest.Name == e2eConfig.MultiComponentWithUnsupportedRuntime { 222 // Validate that the completed CDQ only has detected 1 component and not also the unsupported component 223 Expect(cdq.Status.ComponentDetected).To(HaveLen(1), "cdq also detect unsupported component") 224 } 225 for _, component := range cdq.Status.ComponentDetected { 226 Expect(supportedRuntimes).To(ContainElement(component.ProjectType), "unsupported runtime used for multi component tests") 227 } 228 }) 229 230 // Components for now can be imported from gitUrl, container image or a devfile 231 if componentSpec.GitSourceUrl != "" { 232 It(fmt.Sprintf("creates component %s (private: %t) from git source %s", componentSpec.Name, componentSpec.Private, componentSpec.GitSourceUrl), Label(devEnvTestLabel, stageEnvTestLabel), func() { 233 for _, compDetected := range cdq.Status.ComponentDetected { 234 c, err := fw.AsKubeDeveloper.HasController.CreateComponent(compDetected.ComponentStub, namespace, "", secret, appTest.ApplicationName, true, map[string]string{}) 235 Expect(err).NotTo(HaveOccurred()) 236 Expect(c.Name).To(Equal(compDetected.ComponentStub.ComponentName)) 237 Expect(supportedRuntimes).To(ContainElement(compDetected.ProjectType), "unsupported runtime used for multi component tests") 238 239 componentList = append(componentList, c) 240 } 241 }) 242 } else { 243 defer GinkgoRecover() 244 Fail("Please Provide a valid test sample") 245 } 246 247 // Start to watch the pipeline until is finished 248 It(fmt.Sprintf("waits for %s component (private: %t) pipeline to be finished", componentSpec.Name, componentSpec.Private), Label(devEnvTestLabel, stageEnvTestLabel), func() { 249 if componentSpec.ContainerSource != "" { 250 Skip(fmt.Sprintf("component %s was imported from quay.io/docker.io source. Skipping pipelinerun check.", componentSpec.Name)) 251 } 252 for _, component := range componentList { 253 component, err = fw.AsKubeDeveloper.HasController.GetComponent(component.GetName(), namespace) 254 Expect(err).ShouldNot(HaveOccurred(), "failed to get component: %v", err) 255 256 Expect(fw.AsKubeDeveloper.HasController.WaitForComponentPipelineToBeFinished(component, "", 257 fw.AsKubeDeveloper.TektonController, &has.RetryOptions{Retries: 3, Always: true}, nil)).To(Succeed()) 258 } 259 }) 260 261 It("finds the snapshot and checks if it is marked as successful", Label(devEnvTestLabel), func() { 262 timeout = time.Second * 600 263 interval = time.Second * 10 264 for _, component := range componentList { 265 Eventually(func() error { 266 snapshot, err = fw.AsKubeAdmin.IntegrationController.GetSnapshot("", "", component.Name, namespace) 267 if err != nil { 268 GinkgoWriter.Println("snapshot has not been found yet") 269 return err 270 } 271 if !fw.AsKubeAdmin.CommonController.HaveTestsSucceeded(snapshot) { 272 return fmt.Errorf("tests haven't succeeded for snapshot %s/%s. snapshot status: %+v", snapshot.GetNamespace(), snapshot.GetName(), snapshot.Status) 273 } 274 return nil 275 }, timeout, interval).Should(Succeed(), fmt.Sprintf("timed out waiting for the snapshot for the component %s/%s to be marked as successful", component.GetNamespace(), component.GetName())) 276 } 277 }) 278 279 if componentSpec.AdvancedBuildSpec != nil { 280 Describe(fmt.Sprintf("RHTAP Advanced build test for %s", componentSpec.Name), Label(devEnvTestLabel), Ordered, func() { 281 var managedNamespace string 282 283 var component *appservice.Component 284 var release *releaseApi.Release 285 var snapshot *appservice.Snapshot 286 var pipelineRun, testPipelinerun *tektonapi.PipelineRun 287 var integrationTestScenario *integrationv1beta1.IntegrationTestScenario 288 289 // PaC related variables 290 var prNumber int 291 var headSHA, pacBranchName, pacPurgeBranchName string 292 var mergeResult *github.PullRequestMergeResult 293 294 BeforeAll(func() { 295 if os.Getenv(constants.SKIP_PAC_TESTS_ENV) == "true" { 296 Skip("Skipping this test due to configuration issue with Spray proxy") 297 } 298 managedNamespace = fw.UserNamespace + "-managed" 299 component = componentList[0] 300 301 sharedSecret, err := fw.AsKubeAdmin.CommonController.GetSecret(constants.QuayRepositorySecretNamespace, constants.QuayRepositorySecretName) 302 Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("error when getting shared secret - make sure the secret %s in %s userNamespace is created", constants.QuayRepositorySecretName, constants.QuayRepositorySecretNamespace)) 303 createReleaseConfig(*fw, managedNamespace, component.GetName(), appTest.ApplicationName, sharedSecret.Data[".dockerconfigjson"]) 304 305 its := componentSpec.AdvancedBuildSpec.TestScenario 306 integrationTestScenario, err = fw.AsKubeAdmin.IntegrationController.CreateIntegrationTestScenario("", appTest.ApplicationName, fw.UserNamespace, its.GitURL, its.GitRevision, its.TestPath) 307 Expect(err).ShouldNot(HaveOccurred()) 308 309 pacBranchName = fmt.Sprintf("appstudio-%s", component.GetName()) 310 pacPurgeBranchName = fmt.Sprintf("appstudio-purge-%s", component.GetName()) 311 312 // JBS related config 313 _, err = fw.AsKubeAdmin.JvmbuildserviceController.CreateJBSConfig(constants.JBSConfigName, fw.UserNamespace) 314 Expect(err).ShouldNot(HaveOccurred()) 315 Expect(fw.AsKubeAdmin.JvmbuildserviceController.WaitForCache(fw.AsKubeAdmin.CommonController, fw.UserNamespace)).Should(Succeed()) 316 }) 317 AfterAll(func() { 318 if !CurrentSpecReport().Failed() { 319 Expect(fw.AsKubeAdmin.CommonController.DeleteNamespace(managedNamespace)).To(Succeed()) 320 Expect(fw.AsKubeAdmin.JvmbuildserviceController.DeleteJBSConfig(constants.JBSConfigName, fw.UserNamespace)).To(Succeed()) 321 } 322 323 // Delete new branch created by PaC and a testing branch used as a component's base branch 324 err = fw.AsKubeAdmin.CommonController.Github.DeleteRef(componentRepositoryName, pacBranchName) 325 if err != nil { 326 Expect(err.Error()).To(ContainSubstring("Reference does not exist")) 327 } 328 Expect(fw.AsKubeAdmin.CommonController.Github.DeleteRef(componentRepositoryName, componentNewBaseBranch)).To(Succeed()) 329 }) 330 When("Component is switched to Advanced Build mode", func() { 331 332 BeforeAll(func() { 333 component, err = fw.AsKubeAdmin.HasController.GetComponent(component.GetName(), fw.UserNamespace) 334 Expect(err).ShouldNot(HaveOccurred(), "failed to get component: %v", err) 335 336 component.Annotations["skip-initial-checks"] = "false" 337 for k, v := range constants.ComponentPaCRequestAnnotation { 338 component.Annotations[k] = v 339 } 340 Expect(fw.AsKubeAdmin.CommonController.KubeRest().Update(context.Background(), component)).To(Succeed()) 341 Expect(err).ShouldNot(HaveOccurred(), "failed to update component: %v", err) 342 }) 343 344 It("triggers creation of a PR in the sample repo", func() { 345 346 var prSHA string 347 Eventually(func() error { 348 prs, err := fw.AsKubeAdmin.CommonController.Github.ListPullRequests(componentRepositoryName) 349 Expect(err).ShouldNot(HaveOccurred()) 350 for _, pr := range prs { 351 if pr.Head.GetRef() == pacBranchName { 352 prNumber = pr.GetNumber() 353 prSHA = pr.GetHead().GetSHA() 354 return nil 355 } 356 } 357 return fmt.Errorf("could not get the expected PaC branch name %s", pacBranchName) 358 }, pullRequestCreationTimeout, defaultPollingInterval).Should(Succeed(), fmt.Sprintf("timed out when waiting for init PaC PR (branch %q) to be created against the %q repo", pacBranchName, componentRepositoryName)) 359 360 // We actually don't need the "on-pull-request" PipelineRun to complete, so we can delete it 361 Eventually(func() error { 362 pipelineRun, err = fw.AsKubeAdmin.HasController.GetComponentPipelineRun(component.GetName(), appTest.ApplicationName, fw.UserNamespace, prSHA) 363 if err == nil { 364 Expect(fw.AsKubeAdmin.TektonController.DeletePipelineRun(pipelineRun.Name, pipelineRun.Namespace)).To(Succeed()) 365 return nil 366 } 367 return err 368 }, pipelineRunStartedTimeout, constants.PipelineRunPollingInterval).Should(Succeed(), fmt.Sprintf("timed out when waiting for init PaC PipelineRun to be present in the user namespace %q for component %q with a label pointing to %q", fw.UserNamespace, component.GetName(), appTest.ApplicationName)) 369 370 }) 371 372 It("component build status is set correctly", func() { 373 var buildStatus *buildcontrollers.BuildStatus 374 Eventually(func() (bool, error) { 375 component, err := fw.AsKubeAdmin.HasController.GetComponent(component.GetName(), fw.UserNamespace) 376 if err != nil { 377 return false, err 378 } 379 380 statusBytes := []byte(component.Annotations[buildcontrollers.BuildStatusAnnotationName]) 381 382 err = json.Unmarshal(statusBytes, &buildStatus) 383 if err != nil { 384 return false, err 385 } 386 387 if buildStatus.PaC != nil { 388 GinkgoWriter.Printf("state: %s\n", buildStatus.PaC.State) 389 GinkgoWriter.Printf("mergeUrl: %s\n", buildStatus.PaC.MergeUrl) 390 GinkgoWriter.Printf("errId: %d\n", buildStatus.PaC.ErrId) 391 GinkgoWriter.Printf("errMessage: %s\n", buildStatus.PaC.ErrMessage) 392 GinkgoWriter.Printf("configurationTime: %s\n", buildStatus.PaC.ConfigurationTime) 393 } else { 394 GinkgoWriter.Println("build status does not have PaC field") 395 } 396 397 return buildStatus.PaC != nil && buildStatus.PaC.State == "enabled" && buildStatus.PaC.MergeUrl != "" && buildStatus.PaC.ErrId == 0 && buildStatus.PaC.ConfigurationTime != "", nil 398 }, timeout, interval).Should(BeTrue(), "component build status has unexpected content") 399 }) 400 It("should eventually lead to triggering another PipelineRun after merging the PaC init branch ", func() { 401 Eventually(func() error { 402 mergeResult, err = fw.AsKubeAdmin.CommonController.Github.MergePullRequest(componentRepositoryName, prNumber) 403 return err 404 }, mergePRTimeout).Should(BeNil(), fmt.Sprintf("error when merging PaC pull request: %+v\n", err)) 405 406 headSHA = mergeResult.GetSHA() 407 408 Eventually(func() error { 409 pipelineRun, err = fw.AsKubeAdmin.HasController.GetComponentPipelineRun(component.GetName(), appTest.ApplicationName, fw.UserNamespace, headSHA) 410 if err != nil { 411 GinkgoWriter.Printf("PipelineRun has not been created yet for component %s/%s\n", fw.UserNamespace, component.GetName()) 412 return err 413 } 414 if !pipelineRun.HasStarted() { 415 return fmt.Errorf("pipelinerun %s/%s hasn't started yet", pipelineRun.GetNamespace(), pipelineRun.GetName()) 416 } 417 return nil 418 }, pipelineRunStartedTimeout, constants.PipelineRunPollingInterval).Should(Succeed(), fmt.Sprintf("timed out when waiting for a PipelineRun in namespace %q with label component label %q and application label %q and sha label %q to start", fw.UserNamespace, component.GetName(), appTest.ApplicationName, headSHA)) 419 }) 420 }) 421 422 When("SLSA level 3 customizable PipelineRun is created", func() { 423 It("does not contain an annotation with a Snapshot Name", func() { 424 Expect(pipelineRun.Annotations["appstudio.openshift.io/snapshot"]).To(Equal("")) 425 }) 426 It("should eventually complete successfully", func() { 427 Expect(fw.AsKubeAdmin.HasController.WaitForComponentPipelineToBeFinished(component, headSHA, 428 fw.AsKubeAdmin.TektonController, &has.RetryOptions{Retries: 5, Always: true}, pipelineRun)).To(Succeed()) 429 430 // in case the first pipelineRun attempt has failed and was retried, we need to update the git branch head ref 431 headSHA = pipelineRun.Labels["pipelinesascode.tekton.dev/sha"] 432 }) 433 }) 434 435 When("SLSA level 3 customizable PipelineRun completes successfully", func() { 436 It("should be possible to download the SBOM file", func() { 437 var outputImage string 438 for _, p := range pipelineRun.Spec.Params { 439 if p.Name == "output-image" { 440 outputImage = p.Value.StringVal 441 } 442 } 443 Expect(outputImage).ToNot(BeEmpty(), "output image of a component could not be found") 444 445 _, _, err = build.GetParsedSbomFilesContentFromImage(outputImage) 446 Expect(err).NotTo(HaveOccurred()) 447 }) 448 449 It("should validate Tekton TaskRun test results successfully", func() { 450 pipelineRun, err = fw.AsKubeAdmin.HasController.GetComponentPipelineRun(component.GetName(), appTest.ApplicationName, fw.UserNamespace, headSHA) 451 Expect(err).ShouldNot(HaveOccurred()) 452 Expect(build.ValidateBuildPipelineTestResults(pipelineRun, fw.AsKubeAdmin.CommonController.KubeRest())).To(Succeed()) 453 }) 454 455 It("should validate pipelineRun is signed", func() { 456 pipelineRun, err = fw.AsKubeAdmin.HasController.GetComponentPipelineRun(component.GetName(), appTest.ApplicationName, fw.UserNamespace, headSHA) 457 Expect(err).ShouldNot(HaveOccurred()) 458 Expect(pipelineRun.Annotations["chains.tekton.dev/signed"]).To(Equal("true"), fmt.Sprintf("pipelinerun %s/%s does not have the expected value of annotation 'chains.tekton.dev/signed'", pipelineRun.GetNamespace(), pipelineRun.GetName())) 459 }) 460 461 It("should find the related Snapshot CR", func() { 462 Eventually(func() error { 463 snapshot, err = fw.AsKubeAdmin.IntegrationController.GetSnapshot("", pipelineRun.Name, "", fw.UserNamespace) 464 return err 465 }, snapshotTimeout, snapshotPollingInterval).Should(Succeed(), "timed out when trying to check if the Snapshot exists for PipelineRun %s/%s", fw.UserNamespace, pipelineRun.GetName()) 466 }) 467 468 It("should validate the pipelineRun is annotated with the name of the Snapshot", func() { 469 pipelineRun, err = fw.AsKubeAdmin.HasController.GetComponentPipelineRun(component.GetName(), appTest.ApplicationName, fw.UserNamespace, headSHA) 470 Expect(err).ShouldNot(HaveOccurred()) 471 Expect(pipelineRun.Annotations["appstudio.openshift.io/snapshot"]).To(Equal(snapshot.GetName())) 472 }) 473 474 It("should find the related Integration Test PipelineRun", func() { 475 Eventually(func() error { 476 testPipelinerun, err = fw.AsKubeAdmin.IntegrationController.GetIntegrationPipelineRun(integrationTestScenario.Name, snapshot.Name, fw.UserNamespace) 477 if err != nil { 478 GinkgoWriter.Printf("failed to get Integration test PipelineRun for a snapshot '%s' in '%s' namespace: %+v\n", snapshot.Name, fw.UserNamespace, err) 479 return err 480 } 481 if !testPipelinerun.HasStarted() { 482 return fmt.Errorf("pipelinerun %s/%s hasn't started yet", testPipelinerun.GetNamespace(), testPipelinerun.GetName()) 483 } 484 return nil 485 }, pipelineRunStartedTimeout, defaultPollingInterval).Should(Succeed()) 486 Expect(testPipelinerun.Labels["appstudio.openshift.io/snapshot"]).To(ContainSubstring(snapshot.Name)) 487 Expect(testPipelinerun.Labels["test.appstudio.openshift.io/scenario"]).To(ContainSubstring(integrationTestScenario.Name)) 488 }) 489 }) 490 491 When("Integration Test PipelineRun is created", func() { 492 It("should eventually complete successfully", func() { 493 Expect(fw.AsKubeAdmin.IntegrationController.WaitForIntegrationPipelineToBeFinished(integrationTestScenario, snapshot, fw.UserNamespace)).To(Succeed(), fmt.Sprintf("Error when waiting for a integration pipeline for snapshot %s/%s to finish", fw.UserNamespace, snapshot.GetName())) 494 }) 495 }) 496 497 When("Integration Test PipelineRun completes successfully", func() { 498 499 It("should lead to Snapshot CR being marked as passed", func() { 500 snapshot, err = fw.AsKubeAdmin.IntegrationController.GetSnapshot("", pipelineRun.Name, "", fw.UserNamespace) 501 Expect(err).ShouldNot(HaveOccurred()) 502 Eventually(func() bool { 503 return fw.AsKubeAdmin.CommonController.HaveTestsSucceeded(snapshot) 504 }, time.Minute*5, defaultPollingInterval).Should(BeTrue(), fmt.Sprintf("tests have not succeeded for snapshot %s/%s", snapshot.GetNamespace(), snapshot.GetName())) 505 }) 506 507 It("should trigger creation of Release CR", func() { 508 Eventually(func() error { 509 release, err = fw.AsKubeAdmin.ReleaseController.GetRelease("", snapshot.Name, fw.UserNamespace) 510 return err 511 }, releaseTimeout, releasePollingInterval).Should(Succeed(), fmt.Sprintf("timed out when trying to check if the release exists for snapshot %s/%s", fw.UserNamespace, snapshot.GetName())) 512 }) 513 }) 514 515 When("Release CR is created", func() { 516 It("triggers creation of Release PipelineRun", func() { 517 Eventually(func() error { 518 pipelineRun, err = fw.AsKubeAdmin.ReleaseController.GetPipelineRunInNamespace(managedNamespace, release.Name, release.Namespace) 519 if err != nil { 520 GinkgoWriter.Printf("pipelineRun for component '%s' in namespace '%s' not created yet: %+v\n", component.GetName(), managedNamespace, err) 521 return err 522 } 523 if !pipelineRun.HasStarted() { 524 return fmt.Errorf("pipelinerun %s/%s hasn't started yet", pipelineRun.GetNamespace(), pipelineRun.GetName()) 525 } 526 return nil 527 }, pipelineRunStartedTimeout, defaultPollingInterval).Should(Succeed(), fmt.Sprintf("failed to get pipelinerun named %q in namespace %q with label to release %q in namespace %q to start", pipelineRun.Name, managedNamespace, release.Name, release.Namespace)) 528 }) 529 }) 530 531 When("Release PipelineRun is triggered", func() { 532 It("should eventually succeed", func() { 533 Eventually(func() error { 534 pr, err := fw.AsKubeAdmin.ReleaseController.GetPipelineRunInNamespace(managedNamespace, release.Name, release.Namespace) 535 Expect(err).ShouldNot(HaveOccurred()) 536 Expect(tekton.HasPipelineRunFailed(pr)).NotTo(BeTrue(), fmt.Sprintf("did not expect PipelineRun %s/%s to fail", pr.GetNamespace(), pr.GetName())) 537 if !pr.IsDone() { 538 return fmt.Errorf("release pipelinerun %s/%s has not finished yet", pr.GetNamespace(), pr.GetName()) 539 } 540 Expect(tekton.HasPipelineRunSucceeded(pr)).To(BeTrue(), fmt.Sprintf("PipelineRun %s/%s did not succeed", pr.GetNamespace(), pr.GetName())) 541 return nil 542 }, releasePipelineTimeout, constants.PipelineRunPollingInterval).Should(Succeed(), fmt.Sprintf("failed to see pipelinerun %q in namespace %q with a label pointing to release %q in namespace %q to complete successfully", pipelineRun.Name, managedNamespace, release.Name, release.Namespace)) 543 }) 544 }) 545 When("Release PipelineRun is completed", func() { 546 It("should lead to Release CR being marked as succeeded", func() { 547 Eventually(func() error { 548 release, err = fw.AsKubeAdmin.ReleaseController.GetRelease(release.Name, "", fw.UserNamespace) 549 Expect(err).ShouldNot(HaveOccurred()) 550 if !release.IsReleased() { 551 return fmt.Errorf("release CR %s/%s is not marked as finished yet", release.GetNamespace(), release.GetName()) 552 } 553 return nil 554 }, customResourceUpdateTimeout, defaultPollingInterval).Should(Succeed(), fmt.Sprintf("failed to see release %q in namespace %q get marked as released", release.Name, fw.UserNamespace)) 555 }) 556 }) 557 558 When("JVM Build Service is used for rebuilding dependencies", func() { 559 It("should eventually rebuild of all artifacts and dependencies successfully", func() { 560 jvmClient := jvmclientSet.New(fw.AsKubeAdmin.JvmbuildserviceController.JvmbuildserviceClient().JvmbuildserviceV1alpha1().RESTClient()) 561 tektonClient := pipelineclientset.New(fw.AsKubeAdmin.TektonController.PipelineClient().TektonV1beta1().RESTClient()) 562 kubeClient := kubernetes.New(fw.AsKubeAdmin.CommonController.KubeInterface().CoreV1().RESTClient()) 563 //status report ends up in artifacts/redhat-appstudio-e2e/redhat-appstudio-e2e/artifacts/rp_preproc/attachments/xunit 564 defer e2e.GenerateStatusReport(fw.UserNamespace, jvmClient, kubeClient, tektonClient) 565 Eventually(func() error { 566 abList, err := fw.AsKubeAdmin.JvmbuildserviceController.ListArtifactBuilds(fw.UserNamespace) 567 Expect(err).ShouldNot(HaveOccurred()) 568 for _, ab := range abList.Items { 569 if ab.Status.State != v1alpha1.ArtifactBuildStateComplete { 570 return fmt.Errorf("artifactbuild %s not complete", ab.Spec.GAV) 571 } 572 } 573 dbList, err := fw.AsKubeAdmin.JvmbuildserviceController.ListDependencyBuilds(fw.UserNamespace) 574 Expect(err).ShouldNot(HaveOccurred()) 575 for _, db := range dbList.Items { 576 if db.Status.State != v1alpha1.DependencyBuildStateComplete { 577 return fmt.Errorf("dependencybuild %s not complete", db.Spec.ScmInfo.SCMURL) 578 } 579 } 580 return nil 581 }, jvmRebuildTimeout, jvmRebuildPollingInterval).Should(Succeed(), fmt.Sprintf("timed out when waiting for all artifactbuilds and dependencybuilds to complete in namespace %q", fw.UserNamespace)) 582 }) 583 }) 584 585 When("User switches to simple build", func() { 586 BeforeAll(func() { 587 Expect(fw.AsKubeAdmin.HasController.SetComponentAnnotation(component.GetName(), buildcontrollers.BuildRequestAnnotationName, buildcontrollers.BuildRequestUnconfigurePaCAnnotationValue, fw.UserNamespace)).To(Succeed()) 588 }) 589 AfterAll(func() { 590 // Delete the new branch created by sending purge PR while moving to simple build 591 err = fw.AsKubeAdmin.CommonController.Github.DeleteRef(componentRepositoryName, pacPurgeBranchName) 592 if err != nil { 593 Expect(err.Error()).To(ContainSubstring("Reference does not exist")) 594 } 595 }) 596 It("creates a pull request for removing PAC configuration", func() { 597 Eventually(func() error { 598 prs, err := fw.AsKubeAdmin.CommonController.Github.ListPullRequests(componentRepositoryName) 599 Expect(err).ShouldNot(HaveOccurred()) 600 for _, pr := range prs { 601 if pr.Head.GetRef() == pacPurgeBranchName { 602 return nil 603 } 604 } 605 return fmt.Errorf("could not get the expected PaC purge PR branch %s", pacPurgeBranchName) 606 }, time.Minute*1, defaultPollingInterval).Should(Succeed(), fmt.Sprintf("timed out when waiting for PaC purge PR to be created against the %q repo", componentRepositoryName)) 607 }) 608 It("component status annotation is set correctly", func() { 609 var buildStatus *buildcontrollers.BuildStatus 610 611 Eventually(func() (bool, error) { 612 component, err := fw.AsKubeAdmin.HasController.GetComponent(component.GetName(), fw.UserNamespace) 613 status := component.Annotations[buildcontrollers.BuildStatusAnnotationName] 614 615 if err != nil { 616 GinkgoWriter.Printf("cannot get the build status annotation: %v\n", err) 617 return false, err 618 } 619 620 statusBytes := []byte(status) 621 622 err = json.Unmarshal(statusBytes, &buildStatus) 623 if err != nil { 624 GinkgoWriter.Printf("cannot unmarshal build status: %v\n", err) 625 return false, err 626 } 627 628 return buildStatus.PaC.State != "enabled", nil 629 }, timeout, interval).Should(BeTrue(), "PaC is still enabled, even after unprovisioning") 630 }) 631 }) 632 }) 633 } 634 } 635 }) 636 } 637 } 638 }) 639 640 func createReleaseConfig(fw framework.Framework, managedNamespace, componentName, appName string, secretData []byte) { 641 var err error 642 _, err = fw.AsKubeAdmin.CommonController.CreateTestNamespace(managedNamespace) 643 Expect(err).ShouldNot(HaveOccurred()) 644 645 secret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: "release-pull-secret", Namespace: managedNamespace}, 646 Data: map[string][]byte{".dockerconfigjson": secretData}, 647 Type: corev1.SecretTypeDockerConfigJson, 648 } 649 _, err = fw.AsKubeAdmin.CommonController.CreateSecret(managedNamespace, secret) 650 Expect(err).ShouldNot(HaveOccurred()) 651 652 managedServiceAccount, err := fw.AsKubeAdmin.CommonController.CreateServiceAccount("release-service-account", managedNamespace, []corev1.ObjectReference{{Name: secret.Name}}, nil) 653 Expect(err).NotTo(HaveOccurred()) 654 655 _, err = fw.AsKubeAdmin.ReleaseController.CreateReleasePipelineRoleBindingForServiceAccount(fw.UserNamespace, managedServiceAccount) 656 Expect(err).NotTo(HaveOccurred()) 657 _, err = fw.AsKubeAdmin.ReleaseController.CreateReleasePipelineRoleBindingForServiceAccount(managedNamespace, managedServiceAccount) 658 Expect(err).NotTo(HaveOccurred()) 659 660 publicKey, err := fw.AsKubeAdmin.TektonController.GetTektonChainsPublicKey() 661 Expect(err).ToNot(HaveOccurred()) 662 663 Expect(fw.AsKubeAdmin.TektonController.CreateOrUpdateSigningSecret(publicKey, "cosign-public-key", managedNamespace)).To(Succeed()) 664 665 _, err = fw.AsKubeAdmin.ReleaseController.CreateReleasePlan("source-releaseplan", fw.UserNamespace, appName, managedNamespace, "", nil) 666 Expect(err).NotTo(HaveOccurred()) 667 668 defaultEcPolicy, err := fw.AsKubeAdmin.TektonController.GetEnterpriseContractPolicy("default", "enterprise-contract-service") 669 Expect(err).NotTo(HaveOccurred()) 670 ecPolicyName := componentName + "-policy" 671 defaultEcPolicySpec := ecp.EnterpriseContractPolicySpec{ 672 Description: "Red Hat's enterprise requirements", 673 PublicKey: string(publicKey), 674 Sources: defaultEcPolicy.Spec.Sources, 675 Configuration: &ecp.EnterpriseContractPolicyConfiguration{ 676 Collections: []string{"minimal"}, 677 Exclude: []string{"cve"}, 678 }, 679 } 680 _, err = fw.AsKubeAdmin.TektonController.CreateEnterpriseContractPolicy(ecPolicyName, managedNamespace, defaultEcPolicySpec) 681 Expect(err).NotTo(HaveOccurred()) 682 683 _, err = fw.AsKubeAdmin.ReleaseController.CreateReleasePlanAdmission("demo", managedNamespace, "", fw.UserNamespace, ecPolicyName, "release-service-account", []string{appName}, true, &tektonutils.PipelineRef{ 684 Resolver: "git", 685 Params: []tektonutils.Param{ 686 {Name: "url", Value: releasecommon.RelSvcCatalogURL}, 687 {Name: "revision", Value: releasecommon.RelSvcCatalogRevision}, 688 {Name: "pathInRepo", Value: "pipelines/e2e/e2e.yaml"}, 689 }, 690 }, nil) 691 Expect(err).NotTo(HaveOccurred()) 692 693 _, err = fw.AsKubeAdmin.TektonController.CreatePVCInAccessMode("release-pvc", managedNamespace, corev1.ReadWriteOnce) 694 Expect(err).NotTo(HaveOccurred()) 695 696 _, err = fw.AsKubeAdmin.CommonController.CreateRole("role-release-service-account", managedNamespace, map[string][]string{ 697 "apiGroupsList": {""}, 698 "roleResources": {"secrets"}, 699 "roleVerbs": {"get", "list", "watch"}, 700 }) 701 Expect(err).NotTo(HaveOccurred()) 702 703 _, err = fw.AsKubeAdmin.CommonController.CreateRoleBinding("role-release-service-account-binding", managedNamespace, "ServiceAccount", "release-service-account", managedNamespace, "Role", "role-release-service-account", "rbac.authorization.k8s.io") 704 Expect(err).NotTo(HaveOccurred()) 705 }