github.com/redhat-appstudio/e2e-tests@v0.0.0-20230619105049-9a422b2094d7/tests/build/jvm-build.go (about) 1 package build 2 3 import ( 4 "context" 5 "encoding/json" 6 "fmt" 7 v1 "k8s.io/api/apps/v1" 8 "os" 9 "strings" 10 "time" 11 12 "sigs.k8s.io/controller-runtime/pkg/client" 13 14 "github.com/devfile/library/pkg/util" 15 . "github.com/onsi/ginkgo/v2" 16 . "github.com/onsi/gomega" 17 appservice "github.com/redhat-appstudio/application-api/api/v1alpha1" 18 buildservice "github.com/redhat-appstudio/build-service/api/v1alpha1" 19 "github.com/redhat-appstudio/e2e-tests/pkg/constants" 20 "github.com/redhat-appstudio/e2e-tests/pkg/framework" 21 "github.com/redhat-appstudio/e2e-tests/pkg/utils" 22 "github.com/redhat-appstudio/jvm-build-service/pkg/apis/jvmbuildservice/v1alpha1" 23 "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1" 24 25 corev1 "k8s.io/api/core/v1" 26 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 27 "k8s.io/apimachinery/pkg/types" 28 ) 29 30 var ( 31 testProjectGitUrl = utils.GetEnv("JVM_BUILD_SERVICE_TEST_REPO_URL", "https://github.com/redhat-appstudio-qe/hacbs-test-project") 32 testProjectRevision = utils.GetEnv("JVM_BUILD_SERVICE_TEST_REPO_REVISION", "main") 33 ) 34 35 var _ = framework.JVMBuildSuiteDescribe("JVM Build Service E2E tests", Label("jvm-build", "HACBS"), func() { 36 var f *framework.Framework 37 var err error 38 39 defer GinkgoRecover() 40 41 var testNamespace, applicationName, componentName string 42 var componentPipelineRun *v1beta1.PipelineRun 43 var component *appservice.Component 44 var timeout, interval time.Duration 45 var doCollectLogs bool 46 47 AfterAll(func() { 48 abList, err := f.AsKubeAdmin.JvmbuildserviceController.ListArtifactBuilds(testNamespace) 49 if err != nil { 50 GinkgoWriter.Printf("got error fetching artifactbuilds: %s\n", err.Error()) 51 } 52 53 dbList, err := f.AsKubeAdmin.JvmbuildserviceController.ListDependencyBuilds(testNamespace) 54 if err != nil { 55 GinkgoWriter.Printf("got error fetching dependencybuilds: %s\n", err.Error()) 56 } 57 58 if CurrentSpecReport().Failed() || doCollectLogs { 59 var testLogsDir string 60 artifactDir := os.Getenv("ARTIFACT_DIR") 61 var storeLogsInFiles bool 62 63 if artifactDir != "" { 64 testLogsDir = fmt.Sprintf("%s/jvm-build-service-test", artifactDir) 65 err := os.MkdirAll(testLogsDir, 0755) 66 if err != nil && !os.IsExist(err) { 67 GinkgoWriter.Printf("cannot create a folder %s for storing test logs/resources: %+v\n", testLogsDir, err) 68 } else { 69 storeLogsInFiles = true 70 } 71 } 72 // get jvm-build-service logs 73 toDebug := map[string]string{} 74 75 jvmPodList, jerr := f.AsKubeAdmin.CommonController.KubeInterface().CoreV1().Pods("jvm-build-service").List(context.TODO(), metav1.ListOptions{}) 76 if jerr != nil { 77 GinkgoWriter.Printf("error listing jvm-build-service pods: %s\n", jerr.Error()) 78 } 79 GinkgoWriter.Printf("found %d pods in jvm-build-service namespace\n", len(jvmPodList.Items)) 80 for _, pod := range jvmPodList.Items { 81 var containers []corev1.Container 82 containers = append(containers, pod.Spec.InitContainers...) 83 containers = append(containers, pod.Spec.Containers...) 84 for _, c := range containers { 85 cLog, cerr := utils.GetContainerLogs(f.AsKubeAdmin.CommonController.KubeInterface(), pod.Name, c.Name, pod.Namespace) 86 if cerr != nil { 87 GinkgoWriter.Printf("error getting logs for pod/container %s/%s: %s\n", pod.Name, c.Name, cerr.Error()) 88 continue 89 } 90 filename := fmt.Sprintf("%s-pod-%s-%s.log", pod.Namespace, pod.Name, c.Name) 91 toDebug[filename] = cLog 92 } 93 } 94 // In case the test fails before the Component PipelineRun is created, 95 // we are unable to collect following resources 96 if componentPipelineRun != nil { 97 // let's make sure and print the pr that starts the analysis first 98 logs, err := f.AsKubeAdmin.TektonController.GetPipelineRunLogs(componentPipelineRun.Name, testNamespace) 99 if err != nil { 100 GinkgoWriter.Printf("got error fetching PR logs: %s\n", err.Error()) 101 } 102 filename := fmt.Sprintf("%s-pr-%s.log", testNamespace, componentPipelineRun.Name) 103 toDebug[filename] = logs 104 105 prList, err := f.AsKubeAdmin.TektonController.ListAllPipelineRuns(testNamespace) 106 if err != nil { 107 GinkgoWriter.Printf("got error fetching PR list: %s\n", err.Error()) 108 } 109 GinkgoWriter.Printf("total number of pipeline runs not pruned: %d\n", len(prList.Items)) 110 for _, pr := range prList.Items { 111 if pr.Name == componentPipelineRun.Name { 112 continue 113 } 114 prLog, err := f.AsKubeAdmin.TektonController.GetPipelineRunLogs(pr.Name, pr.Namespace) 115 if err != nil { 116 GinkgoWriter.Printf("got error fetching PR logs for %s: %s\n", pr.Name, err.Error()) 117 } 118 filename := fmt.Sprintf("%s-pr-%s.log", pr.Namespace, pr.Name) 119 toDebug[filename] = prLog 120 } 121 122 for _, ab := range abList.Items { 123 v, err := json.MarshalIndent(ab, "", " ") 124 if err != nil { 125 GinkgoWriter.Printf("error when marshalling content of %s from %s namespace: %+v\n", ab.Name, ab.Namespace, err) 126 } else { 127 filename := fmt.Sprintf("%s-ab-%s.json", ab.Namespace, ab.Name) 128 toDebug[filename] = string(v) 129 } 130 } 131 for _, db := range dbList.Items { 132 v, err := json.MarshalIndent(db, "", " ") 133 if err != nil { 134 GinkgoWriter.Printf("error when marshalling content of %s from %s namespace: %+v\n", db.Name, db.Namespace, err) 135 } else { 136 filename := fmt.Sprintf("%s-db-%s.json", db.Namespace, db.Name) 137 toDebug[filename] = string(v) 138 } 139 } 140 } 141 142 for file, content := range toDebug { 143 if storeLogsInFiles { 144 filename := fmt.Sprintf("%s/%s", testLogsDir, file) 145 if err := os.WriteFile(filename, []byte(content), 0644); err != nil { 146 GinkgoWriter.Printf("cannot write to %s: %+v\n", filename, err) 147 } else { 148 continue 149 } 150 } else { 151 GinkgoWriter.Printf("%s\n%s\n", file, content) 152 } 153 } 154 } else { 155 Expect(f.AsKubeAdmin.HasController.DeleteHasComponent(componentName, testNamespace, false)).To(Succeed()) 156 Expect(f.AsKubeAdmin.HasController.DeleteHasApplication(applicationName, testNamespace, false)).To(Succeed()) 157 Expect(f.AsKubeAdmin.TektonController.DeleteAllPipelineRunsInASpecificNamespace(testNamespace)).To(Succeed()) 158 Expect(f.SandboxController.DeleteUserSignup(f.UserName)).NotTo(BeFalse()) 159 } 160 // Cleanup artifact builds and dependency builds which are already 161 // archived in case of a failure 162 for _, ab := range abList.Items { 163 err := f.AsKubeAdmin.JvmbuildserviceController.DeleteArtifactBuild(ab.Name, ab.Namespace) 164 if err != nil { 165 GinkgoWriter.Printf("got error deleting AB %s: %s\n", ab.Name, err.Error()) 166 } 167 } 168 for _, db := range dbList.Items { 169 err := f.AsKubeAdmin.JvmbuildserviceController.DeleteDependencyBuild(db.Name, db.Namespace) 170 if err != nil { 171 GinkgoWriter.Printf("got error deleting DB %s: %s\n", db.Name, err.Error()) 172 } 173 } 174 }) 175 176 BeforeAll(func() { 177 f, err = framework.NewFramework(utils.GetGeneratedNamespace("jvm-build")) 178 Expect(err).NotTo(HaveOccurred()) 179 testNamespace = f.UserNamespace 180 Expect(testNamespace).NotTo(BeNil(), "failed to create sandbox user namespace") 181 182 GinkgoWriter.Printf("Test namespace: %s\n", testNamespace) 183 184 _, err = f.AsKubeAdmin.JvmbuildserviceController.CreateJBSConfig(constants.JBSConfigName, testNamespace) 185 Expect(err).ShouldNot(HaveOccurred()) 186 187 //TODO: not using SPI at the moment for auto created repos 188 //var SPITokenBinding *spi.SPIAccessTokenBinding 189 ////this should result in the creation of an SPIAccessTokenBinding 190 //Eventually(func() bool { 191 // SPITokenBinding, err = f.AsKubeDeveloper.SPIController.GetSPIAccessTokenBinding(constants.JVMBuildImageSecretName, testNamespace) 192 // 193 // if err != nil { 194 // return false 195 // } 196 // 197 // return SPITokenBinding.Status.Phase == spi.SPIAccessTokenBindingPhaseInjected 198 //}, 1*time.Minute, 5*time.Second).Should(BeTrue(), "Access token binding should be created") 199 200 //wait for the cache 201 202 WaitForCache(f.AsKubeAdmin, testNamespace) 203 204 customJavaPipelineBundleRef := os.Getenv(constants.CUSTOM_JAVA_PIPELINE_BUILD_BUNDLE_ENV) 205 if len(customJavaPipelineBundleRef) > 0 { 206 ps := &buildservice.BuildPipelineSelector{ 207 ObjectMeta: metav1.ObjectMeta{ 208 Name: "build-pipeline-selector", 209 Namespace: testNamespace, 210 }, 211 Spec: buildservice.BuildPipelineSelectorSpec{Selectors: []buildservice.PipelineSelector{ 212 { 213 Name: "custom java selector", 214 PipelineRef: v1beta1.PipelineRef{ 215 Name: "java-builder", 216 Bundle: customJavaPipelineBundleRef, 217 }, 218 WhenConditions: buildservice.WhenCondition{Language: "java"}, 219 }, 220 }}, 221 } 222 Expect(f.AsKubeAdmin.CommonController.KubeRest().Create(context.TODO(), ps)).To(Succeed()) 223 } 224 225 timeout = time.Minute * 20 226 interval = time.Second * 10 227 228 applicationName = fmt.Sprintf("jvm-build-suite-application-%s", util.GenerateRandomString(4)) 229 app, err := f.AsKubeAdmin.HasController.CreateHasApplication(applicationName, testNamespace) 230 Expect(err).NotTo(HaveOccurred()) 231 Expect(utils.WaitUntil(f.AsKubeAdmin.HasController.ApplicationGitopsRepoExists(app.Status.Devfile), 30*time.Second)).To( 232 Succeed(), fmt.Sprintf("timed out waiting for gitops content to be created for app %s in namespace %s: %+v", app.Name, app.Namespace, err), 233 ) 234 235 componentName = fmt.Sprintf("jvm-build-suite-component-%s", util.GenerateRandomString(4)) 236 237 // Create a component with Git Source URL being defined 238 component, err = f.AsKubeAdmin.HasController.CreateComponent(applicationName, componentName, testNamespace, testProjectGitUrl, testProjectRevision, "", "", "", true) 239 Expect(err).ShouldNot(HaveOccurred()) 240 }) 241 242 When("the Component with s2i-java component is created", func() { 243 It("a PipelineRun is triggered", func() { 244 Eventually(func() bool { 245 componentPipelineRun, err = f.AsKubeAdmin.HasController.GetComponentPipelineRun(componentName, applicationName, testNamespace, "") 246 if err != nil { 247 GinkgoWriter.Println("PipelineRun has not been created yet") 248 return false 249 } 250 return componentPipelineRun.HasStarted() 251 }, timeout, interval).Should(BeTrue(), "timed out when waiting for the PipelineRun to start") 252 }) 253 254 It("the build-container task from component pipelinerun references a correct analyzer image", func() { 255 ciAnalyzerImage := os.Getenv("JVM_BUILD_SERVICE_REQPROCESSOR_IMAGE") 256 257 if ciAnalyzerImage == "" { 258 Skip("JVM_BUILD_SERVICE_REQPROCESSOR_IMAGE env var is not exported, skipping the test...") 259 } 260 261 Eventually(func() bool { 262 pr, err := f.AsKubeAdmin.HasController.GetComponentPipelineRun(componentName, applicationName, testNamespace, "") 263 if err != nil { 264 GinkgoWriter.Printf("get pr for the component %s produced err: %s\n", componentName, err.Error()) 265 return false 266 } 267 268 for _, chr := range pr.Status.ChildReferences { 269 taskRun := &v1beta1.TaskRun{} 270 taskRunKey := types.NamespacedName{Namespace: pr.Namespace, Name: chr.Name} 271 err := f.AsKubeAdmin.CommonController.KubeRest().Get(context.TODO(), taskRunKey, taskRun) 272 Expect(err).ShouldNot(HaveOccurred()) 273 274 prTrStatus := &v1beta1.PipelineRunTaskRunStatus{ 275 PipelineTaskName: chr.PipelineTaskName, 276 Status: &taskRun.Status, 277 } 278 279 if chr.PipelineTaskName == "build-container" && prTrStatus.Status != nil && prTrStatus.Status.TaskSpec != nil && prTrStatus.Status.TaskSpec.Steps != nil { 280 for _, step := range prTrStatus.Status.TaskSpec.Steps { 281 if step.Name == "analyse-dependencies-java-sbom" { 282 if step.Image != ciAnalyzerImage { 283 Fail(fmt.Sprintf("the build-container task from component pipelinerun doesn't reference the correct request processor image. expected: %v, actual: %v", ciAnalyzerImage, step.Image)) 284 } else { 285 return true 286 } 287 } 288 } 289 } 290 } 291 return false 292 }, timeout, interval).Should(BeTrue(), "timed out when verifying the request processor image reference in pipelinerun") 293 }) 294 295 It("that PipelineRun completes successfully", func() { 296 Expect(f.AsKubeAdmin.HasController.WaitForComponentPipelineToBeFinished(component, "", 2)).To(Succeed()) 297 }) 298 299 It("artifactbuilds and dependencybuilds are generated", func() { 300 Eventually(func() bool { 301 abList, err := f.AsKubeAdmin.JvmbuildserviceController.ListArtifactBuilds(testNamespace) 302 if err != nil { 303 GinkgoWriter.Printf("error listing artifactbuilds: %s\n", err.Error()) 304 return false 305 } 306 gotABs := false 307 if len(abList.Items) > 0 { 308 gotABs = true 309 } 310 dbList, err := f.AsKubeAdmin.JvmbuildserviceController.ListDependencyBuilds(testNamespace) 311 if err != nil { 312 GinkgoWriter.Printf("error listing dependencybuilds: %s\n", err.Error()) 313 return false 314 } 315 gotDBs := false 316 if len(dbList.Items) > 0 { 317 gotDBs = true 318 } 319 if gotABs && gotDBs { 320 return true 321 } 322 return false 323 }, timeout, interval).Should(BeTrue(), "timed out when waiting for the generation of artifactbuilds and dependencybuilds") 324 }) 325 326 It("some artifactbuilds and dependencybuilds complete", func() { 327 Eventually(func() bool { 328 abList, err := f.AsKubeAdmin.JvmbuildserviceController.ListArtifactBuilds(testNamespace) 329 if err != nil { 330 GinkgoWriter.Printf("error listing artifactbuilds: %s\n", err.Error()) 331 return false 332 } 333 abComplete := false 334 for _, ab := range abList.Items { 335 if ab.Status.State == v1alpha1.ArtifactBuildStateComplete { 336 abComplete = true 337 break 338 } 339 } 340 dbList, err := f.AsKubeAdmin.JvmbuildserviceController.ListDependencyBuilds(testNamespace) 341 if err != nil { 342 GinkgoWriter.Printf("error listing dependencybuilds: %s\n", err.Error()) 343 return false 344 } 345 dbComplete := false 346 for _, db := range dbList.Items { 347 if db.Status.State == v1alpha1.DependencyBuildStateComplete { 348 dbComplete = true 349 break 350 } 351 } 352 if abComplete && dbComplete { 353 return true 354 } 355 return false 356 }, timeout, interval).Should(BeTrue(), "timed out when waiting for some artifactbuilds and dependencybuilds to complete") 357 }) 358 359 It("all artifactbuild and dependencybuilds complete", func() { 360 Eventually(func() bool { 361 abList, err := f.AsKubeAdmin.JvmbuildserviceController.ListArtifactBuilds(testNamespace) 362 Expect(err).ShouldNot(HaveOccurred(), "error in listing artifact builds") 363 // we want to make sure there is more than one ab and that they are all complete 364 abComplete := len(abList.Items) > 0 365 GinkgoWriter.Printf("number of artifactbuilds: %d\n", len(abList.Items)) 366 for _, ab := range abList.Items { 367 if ab.Status.State != v1alpha1.ArtifactBuildStateComplete { 368 GinkgoWriter.Printf("artifactbuild %s not complete\n", ab.Spec.GAV) 369 abComplete = false 370 break 371 } 372 } 373 dbList, err := f.AsKubeAdmin.JvmbuildserviceController.ListDependencyBuilds(testNamespace) 374 Expect(err).ShouldNot(HaveOccurred(), "error in listing dependency builds") 375 dbComplete := len(dbList.Items) > 0 376 GinkgoWriter.Printf("number of dependencybuilds: %d\n", len(dbList.Items)) 377 for _, db := range dbList.Items { 378 if db.Status.State != v1alpha1.DependencyBuildStateComplete { 379 GinkgoWriter.Printf("dependencybuild %s not complete\n", db.Spec.ScmInfo.SCMURL) 380 dbComplete = false 381 break 382 } else if db.Status.State == v1alpha1.DependencyBuildStateFailed { 383 Fail(fmt.Sprintf("dependencybuild %s FAILED", db.Spec.ScmInfo.SCMURL)) 384 } 385 } 386 if abComplete && dbComplete { 387 return true 388 } 389 return false 390 }, 2*timeout, interval).Should(BeTrue(), "timed out when waiting for all artifactbuilds and dependencybuilds to complete") 391 }) 392 393 It("does rebuild use cached dependencies", func() { 394 prun := &v1beta1.PipelineRun{} 395 396 component, err := f.AsKubeAdmin.HasController.GetHasComponent(componentName, testNamespace) 397 Expect(err).ShouldNot(HaveOccurred(), "could not get component") 398 399 annotations := component.GetAnnotations() 400 delete(annotations, constants.ComponentInitialBuildAnnotationKey) 401 component.SetAnnotations(annotations) 402 Expect(f.AsKubeAdmin.CommonController.KubeRest().Update(context.TODO(), component, &client.UpdateOptions{})).To(Succeed()) 403 404 Eventually(func() bool { 405 prun, err = f.AsKubeAdmin.HasController.GetComponentPipelineRun(componentName, applicationName, testNamespace, "") 406 return err == nil 407 }, timeout, interval).Should(BeTrue()) 408 409 ctx := context.TODO() 410 411 watch, err := f.AsKubeAdmin.TektonController.WatchPipelineRun(ctx, testNamespace) 412 Expect(err).ShouldNot(HaveOccurred(), "watch pipelinerun failed") 413 414 exitForLoop := false 415 416 for { 417 select { 418 case <-time.After(15 * time.Minute): 419 Fail("timed out waiting for second build to complete") 420 case event := <-watch.ResultChan(): 421 if event.Object == nil { 422 continue 423 } 424 pr, ok := event.Object.(*v1beta1.PipelineRun) 425 if !ok { 426 continue 427 } 428 if prun.Name != pr.Name { 429 if pr.IsDone() { 430 GinkgoWriter.Printf("got event for pipelinerun %s in a terminal state\n", pr.Name) 431 continue 432 } 433 Fail("another non-completed pipeline run was generated when it should not") 434 } 435 GinkgoWriter.Printf("done processing event for pr %s\n", pr.Name) 436 if pr.IsDone() { 437 GinkgoWriter.Println("pr is done") 438 439 podClient := f.AsKubeAdmin.CommonController.KubeInterface().CoreV1().Pods(testNamespace) 440 listOptions := metav1.ListOptions{ 441 LabelSelector: fmt.Sprintf("tekton.dev/pipelineRun=%s", pr.Name), 442 } 443 podList, err := podClient.List(context.TODO(), listOptions) 444 Expect(err).ShouldNot(HaveOccurred(), "error listing pr pods") 445 446 pods := podList.Items 447 448 if len(pods) == 0 { 449 Fail("pod for pipeline run unexpectedly missing") 450 } 451 452 containers := []corev1.Container{} 453 containers = append(containers, pods[0].Spec.InitContainers...) 454 containers = append(containers, pods[0].Spec.Containers...) 455 456 for _, container := range containers { 457 if !strings.Contains(container.Name, "analyse-dependecies") { 458 continue 459 } 460 cLog, err := utils.GetContainerLogs(f.AsKubeAdmin.CommonController.KubeInterface(), pods[0].Name, container.Name, testNamespace) 461 Expect(err).ShouldNot(HaveOccurred(), "getting container logs failed") 462 if strings.Contains(cLog, "\"publisher\" : \"central\"") { 463 Fail(fmt.Sprintf("pipelinerun %s has container %s with dep analysis still pointing to central %s", pr.Name, container.Name, cLog)) 464 } 465 if !strings.Contains(cLog, "\"publisher\" : \"rebuilt\"") { 466 Fail(fmt.Sprintf("pipelinerun %s has container %s with dep analysis that does not access rebuilt %s", pr.Name, container.Name, cLog)) 467 } 468 if !strings.Contains(cLog, "\"java:scm-uri\" : \"https://github.com/stuartwdouglas/hacbs-test-simple-jdk8.git\"") { 469 Fail(fmt.Sprintf("pipelinerun %s has container %s with dep analysis did not include java:scm-uri %s", pr.Name, container.Name, cLog)) 470 } 471 if !strings.Contains(cLog, "\"java:scm-commit\" : \"") { 472 Fail(fmt.Sprintf("pipelinerun %s has container %s with dep analysis did not include java:scm-commit %s", pr.Name, container.Name, cLog)) 473 } 474 break 475 } 476 GinkgoWriter.Println("pr is done and has correct analyse-dependecies output, exiting") 477 exitForLoop = true 478 } 479 } 480 if exitForLoop { 481 break 482 } 483 } 484 }) 485 }) 486 }) 487 488 func WaitForCache(client *framework.ControllerHub, testNamespace string) bool { 489 return Eventually(func() bool { 490 cache, err := client.CommonController.GetDeployment(v1alpha1.CacheDeploymentName, testNamespace) 491 if err != nil { 492 GinkgoWriter.Printf("get of cache: %s", err.Error()) 493 return false 494 } 495 if cache.Status.AvailableReplicas > 0 { 496 GinkgoWriter.Printf("Cache is available") 497 return true 498 } 499 for _, cond := range cache.Status.Conditions { 500 if cond.Type == v1.DeploymentProgressing && cond.Status == "False" { 501 panic("cache deployment failed") 502 } 503 504 } 505 GinkgoWriter.Printf("Cache is progressing") 506 return false 507 }, 5*time.Minute, 5*time.Second).Should(BeTrue(), "Cache should be created and ready") 508 }