github.com/redhat-appstudio/e2e-tests@v0.0.0-20240520140907-9709f6f59323/tests/build/jvm-build.go (about)

     1  package build
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  	"strings"
     8  	"time"
     9  
    10  	"sigs.k8s.io/controller-runtime/pkg/client"
    11  
    12  	"github.com/devfile/library/v2/pkg/util"
    13  	. "github.com/onsi/ginkgo/v2"
    14  	. "github.com/onsi/gomega"
    15  	appservice "github.com/redhat-appstudio/application-api/api/v1alpha1"
    16  	buildservice "github.com/redhat-appstudio/build-service/api/v1alpha1"
    17  	"github.com/redhat-appstudio/e2e-tests/pkg/clients/has"
    18  	"github.com/redhat-appstudio/e2e-tests/pkg/constants"
    19  	"github.com/redhat-appstudio/e2e-tests/pkg/framework"
    20  	"github.com/redhat-appstudio/e2e-tests/pkg/utils"
    21  	"github.com/redhat-appstudio/e2e-tests/pkg/utils/tekton"
    22  	"github.com/redhat-appstudio/jvm-build-service/openshift-with-appstudio-test/e2e"
    23  	"github.com/redhat-appstudio/jvm-build-service/pkg/apis/jvmbuildservice/v1alpha1"
    24  	jvmclientSet "github.com/redhat-appstudio/jvm-build-service/pkg/client/clientset/versioned"
    25  	pipeline "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
    26  	pipelineclientset "github.com/tektoncd/pipeline/pkg/client/clientset/versioned"
    27  	"k8s.io/client-go/kubernetes"
    28  
    29  	corev1 "k8s.io/api/core/v1"
    30  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    31  	"k8s.io/apimachinery/pkg/types"
    32  )
    33  
    34  var (
    35  	testProjectGitUrl   = utils.GetEnv("JVM_BUILD_SERVICE_TEST_REPO_URL", "https://github.com/redhat-appstudio-qe/hacbs-test-project")
    36  	testProjectRevision = utils.GetEnv("JVM_BUILD_SERVICE_TEST_REPO_REVISION", "34da5a8f51fba6a8b7ec75a727d3c72ebb5e1274")
    37  )
    38  
    39  var _ = framework.JVMBuildSuiteDescribe("JVM Build Service E2E tests", Label("jvm-build", "HACBS"), func() {
    40  	var f *framework.Framework
    41  	AfterEach(framework.ReportFailure(&f))
    42  	var err error
    43  
    44  	defer GinkgoRecover()
    45  
    46  	var testNamespace, applicationName, componentName string
    47  	var component *appservice.Component
    48  	var timeout, interval time.Duration
    49  
    50  	AfterAll(func() {
    51  		jvmClient := jvmclientSet.New(f.AsKubeAdmin.JvmbuildserviceController.JvmbuildserviceClient().JvmbuildserviceV1alpha1().RESTClient())
    52  		tektonClient := pipelineclientset.New(f.AsKubeAdmin.TektonController.PipelineClient().TektonV1().RESTClient())
    53  		kubeClient := kubernetes.New(f.AsKubeAdmin.CommonController.KubeInterface().CoreV1().RESTClient())
    54  		//status report ends up in artifacts/redhat-appstudio-e2e/redhat-appstudio-e2e/artifacts/rp_preproc/attachments/xunit
    55  		e2e.GenerateStatusReport(testNamespace, jvmClient, kubeClient, tektonClient)
    56  		if !CurrentSpecReport().Failed() {
    57  			Expect(f.AsKubeAdmin.HasController.DeleteComponent(componentName, testNamespace, false)).To(Succeed())
    58  			Expect(f.AsKubeAdmin.HasController.DeleteApplication(applicationName, testNamespace, false)).To(Succeed())
    59  			Expect(f.AsKubeAdmin.TektonController.DeleteAllPipelineRunsInASpecificNamespace(testNamespace)).To(Succeed())
    60  			Expect(f.SandboxController.DeleteUserSignup(f.UserName)).To(BeTrue())
    61  			Expect(f.AsKubeAdmin.JvmbuildserviceController.DeleteJBSConfig(constants.JBSConfigName, testNamespace)).To(Succeed())
    62  		} else {
    63  			Expect(f.AsKubeAdmin.CommonController.StoreAllPods(testNamespace)).To(Succeed())
    64  			Expect(f.AsKubeAdmin.TektonController.StoreAllPipelineRuns(testNamespace)).To(Succeed())
    65  		}
    66  	})
    67  
    68  	BeforeAll(func() {
    69  		f, err = framework.NewFramework(utils.GetGeneratedNamespace("jvm-build"))
    70  		Expect(err).NotTo(HaveOccurred())
    71  		testNamespace = f.UserNamespace
    72  		Expect(testNamespace).NotTo(BeNil(), "failed to create sandbox user namespace")
    73  
    74  		_, err = f.AsKubeAdmin.JvmbuildserviceController.CreateJBSConfig(constants.JBSConfigName, testNamespace)
    75  		Expect(err).ShouldNot(HaveOccurred())
    76  
    77  		//TODO: not using SPI at the moment for auto created repos
    78  		//var SPITokenBinding *spi.SPIAccessTokenBinding
    79  		////this should result in the creation of an SPIAccessTokenBinding
    80  		//Eventually(func() bool {
    81  		//	SPITokenBinding, err = f.AsKubeDeveloper.SPIController.GetSPIAccessTokenBinding(constants.JVMBuildImageSecretName, testNamespace)
    82  		//
    83  		//	if err != nil {
    84  		//		return false
    85  		//	}
    86  		//
    87  		//	return SPITokenBinding.Status.Phase == spi.SPIAccessTokenBindingPhaseInjected
    88  		//}, 1*time.Minute, 5*time.Second).Should(BeTrue(), "Access token binding should be created")
    89  
    90  		//wait for the cache
    91  
    92  		Expect(f.AsKubeAdmin.JvmbuildserviceController.WaitForCache(f.AsKubeAdmin.CommonController, testNamespace)).Should(Succeed())
    93  
    94  		customJavaPipelineBundleRef := os.Getenv(constants.CUSTOM_JAVA_PIPELINE_BUILD_BUNDLE_ENV)
    95  		if len(customJavaPipelineBundleRef) > 0 {
    96  			ps := &buildservice.BuildPipelineSelector{
    97  				ObjectMeta: metav1.ObjectMeta{
    98  					Name:      "build-pipeline-selector",
    99  					Namespace: testNamespace,
   100  				},
   101  				Spec: buildservice.BuildPipelineSelectorSpec{Selectors: []buildservice.PipelineSelector{
   102  					{
   103  						Name:           "custom java selector",
   104  						PipelineRef:    *tekton.NewBundleResolverPipelineRef("java-builder", customJavaPipelineBundleRef),
   105  						WhenConditions: buildservice.WhenCondition{Language: "java"},
   106  					},
   107  				}},
   108  			}
   109  			Expect(f.AsKubeAdmin.CommonController.KubeRest().Create(context.Background(), ps)).To(Succeed())
   110  		}
   111  
   112  		timeout = time.Minute * 20
   113  		interval = time.Second * 10
   114  
   115  		applicationName = fmt.Sprintf("jvm-build-suite-application-%s", util.GenerateRandomString(4))
   116  		_, err = f.AsKubeAdmin.HasController.CreateApplication(applicationName, testNamespace)
   117  		Expect(err).NotTo(HaveOccurred())
   118  
   119  		componentName = fmt.Sprintf("jvm-build-suite-component-%s", util.GenerateRandomString(6))
   120  
   121  		// Create a component with Git Source URL being defined
   122  		componentObj := appservice.ComponentSpec{
   123  			ComponentName: componentName,
   124  			Source: appservice.ComponentSource{
   125  				ComponentSourceUnion: appservice.ComponentSourceUnion{
   126  					GitSource: &appservice.GitSource{
   127  						URL:      testProjectGitUrl,
   128  						Revision: testProjectRevision,
   129  					},
   130  				},
   131  			},
   132  		}
   133  		component, err = f.AsKubeAdmin.HasController.CreateComponent(componentObj, testNamespace, "", "", applicationName, true, map[string]string{})
   134  		Expect(err).ShouldNot(HaveOccurred())
   135  	})
   136  
   137  	When("the Component with s2i-java component is created", func() {
   138  		It("a PipelineRun is triggered", func() {
   139  			Eventually(func() error {
   140  				pr, err := f.AsKubeAdmin.HasController.GetComponentPipelineRun(componentName, applicationName, testNamespace, "")
   141  				if err != nil {
   142  					GinkgoWriter.Printf("PipelineRun has not been created yet for the component %s/%s", testNamespace, componentName)
   143  					return err
   144  				}
   145  				if !pr.HasStarted() {
   146  					return fmt.Errorf("pipelinerun %s/%s hasn't started yet", pr.GetNamespace(), pr.GetName())
   147  				}
   148  				return nil
   149  			}, timeout, constants.PipelineRunPollingInterval).Should(Succeed(), fmt.Sprintf("timed out when waiting for the PipelineRun to start for the component %s/%s", testNamespace, componentName))
   150  		})
   151  
   152  		It("the build-container task from component pipelinerun references a correct analyzer image", func() {
   153  			ciAnalyzerImage := os.Getenv("JVM_BUILD_SERVICE_REQPROCESSOR_IMAGE")
   154  			matchingTaskStep := "analyse-dependencies-java-sbom"
   155  
   156  			if ciAnalyzerImage == "" {
   157  				Skip("JVM_BUILD_SERVICE_REQPROCESSOR_IMAGE env var is not exported, skipping the test...")
   158  			}
   159  
   160  			Eventually(func() error {
   161  				pr, err := f.AsKubeAdmin.HasController.GetComponentPipelineRun(componentName, applicationName, testNamespace, "")
   162  				Expect(err).ShouldNot(HaveOccurred())
   163  
   164  				for _, chr := range pr.Status.ChildReferences {
   165  					taskRun := &pipeline.TaskRun{}
   166  					taskRunKey := types.NamespacedName{Namespace: pr.Namespace, Name: chr.Name}
   167  					err := f.AsKubeAdmin.CommonController.KubeRest().Get(context.Background(), taskRunKey, taskRun)
   168  					Expect(err).ShouldNot(HaveOccurred())
   169  
   170  					prTrStatus := &pipeline.PipelineRunTaskRunStatus{
   171  						PipelineTaskName: chr.PipelineTaskName,
   172  						Status:           &taskRun.Status,
   173  					}
   174  
   175  					if chr.PipelineTaskName == constants.BuildTaskRunName && prTrStatus.Status != nil && prTrStatus.Status.TaskSpec != nil && prTrStatus.Status.TaskSpec.Steps != nil {
   176  						for _, step := range prTrStatus.Status.TaskSpec.Steps {
   177  							if step.Name == matchingTaskStep {
   178  								if step.Image != ciAnalyzerImage {
   179  									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))
   180  								} else {
   181  									return nil
   182  								}
   183  							}
   184  						}
   185  					}
   186  				}
   187  				return fmt.Errorf("couldn't find a matching step %s in task %s in PipelineRun %s/%s", matchingTaskStep, constants.BuildTaskRunName, testNamespace, pr.GetName())
   188  			}, timeout, interval).Should(Succeed(), "timed out when verifying the request processor image reference in pipelinerun")
   189  		})
   190  
   191  		It("that PipelineRun completes successfully", func() {
   192  			Expect(f.AsKubeAdmin.HasController.WaitForComponentPipelineToBeFinished(component, "",
   193  				f.AsKubeAdmin.TektonController, &has.RetryOptions{Retries: 2, Always: true}, nil)).To(Succeed())
   194  
   195  			pr, err := f.AsKubeAdmin.HasController.GetComponentPipelineRun(componentName, applicationName, testNamespace, "")
   196  			Expect(err).ShouldNot(HaveOccurred())
   197  			//now delete it so it can't interfere with later test logic
   198  			Expect(f.AsKubeAdmin.TektonController.DeletePipelineRun(pr.Name, testNamespace)).Should(Succeed())
   199  		})
   200  
   201  		It("artifactbuilds and dependencybuilds are generated", func() {
   202  			Eventually(func() bool {
   203  				var gotABs, gotDBs bool
   204  				abList, err := f.AsKubeAdmin.JvmbuildserviceController.ListArtifactBuilds(testNamespace)
   205  				if err != nil {
   206  					GinkgoWriter.Printf("error listing artifactbuilds: %s\n", err.Error())
   207  					return false
   208  				}
   209  				if len(abList.Items) > 0 {
   210  					gotABs = true
   211  				}
   212  				dbList, err := f.AsKubeAdmin.JvmbuildserviceController.ListDependencyBuilds(testNamespace)
   213  				if err != nil {
   214  					GinkgoWriter.Printf("error listing dependencybuilds: %s\n", err.Error())
   215  					return false
   216  				}
   217  				if len(dbList.Items) > 0 {
   218  					gotDBs = true
   219  				}
   220  				GinkgoWriter.Printf("got artifactbuilds: %t, got dependencybuilds: %t\n", gotABs, gotDBs)
   221  				if !gotABs || !gotDBs {
   222  					return false
   223  				}
   224  				return true
   225  			}, timeout, interval).Should(BeTrue(), "timed out when waiting for the generation of artifactbuilds and dependencybuilds")
   226  		})
   227  
   228  		It("some artifactbuilds and dependencybuilds complete", func() {
   229  			Eventually(func() bool {
   230  				var abComplete, dbComplete bool
   231  				abList, err := f.AsKubeAdmin.JvmbuildserviceController.ListArtifactBuilds(testNamespace)
   232  				if err != nil {
   233  					GinkgoWriter.Printf("error listing artifactbuilds: %s\n", err.Error())
   234  					return false
   235  				}
   236  				for _, ab := range abList.Items {
   237  					if ab.Status.State == v1alpha1.ArtifactBuildStateComplete {
   238  						abComplete = true
   239  						break
   240  					}
   241  				}
   242  				dbList, err := f.AsKubeAdmin.JvmbuildserviceController.ListDependencyBuilds(testNamespace)
   243  				if err != nil {
   244  					GinkgoWriter.Printf("error listing dependencybuilds: %s\n", err.Error())
   245  					return false
   246  				}
   247  				for _, db := range dbList.Items {
   248  					if db.Status.State == v1alpha1.DependencyBuildStateComplete {
   249  						dbComplete = true
   250  						break
   251  					}
   252  				}
   253  				GinkgoWriter.Printf("some artifactbuilds completed: %t, some dependencybuilds completed: %t\n", abComplete, dbComplete)
   254  				if !abComplete || !dbComplete {
   255  					return false
   256  				}
   257  				return true
   258  			}, timeout, interval).Should(BeTrue(), fmt.Sprintf("timed out when waiting for some artifactbuilds and dependencybuilds to complete in %s namespace", testNamespace))
   259  		})
   260  
   261  		It("all artifactbuild and dependencybuilds complete", func() {
   262  			Eventually(func() bool {
   263  				abList, err := f.AsKubeAdmin.JvmbuildserviceController.ListArtifactBuilds(testNamespace)
   264  				Expect(err).ShouldNot(HaveOccurred(), "error in listing artifact builds")
   265  				// we want to make sure there is more than one ab and that they are all complete
   266  				allAbCompleted := len(abList.Items) > 0
   267  				GinkgoWriter.Printf("number of artifactbuilds: %d\n", len(abList.Items))
   268  				for _, ab := range abList.Items {
   269  					if ab.Status.State != v1alpha1.ArtifactBuildStateComplete {
   270  						GinkgoWriter.Printf("artifactbuild %s not complete\n", ab.Spec.GAV)
   271  						allAbCompleted = false
   272  						break
   273  					}
   274  				}
   275  				dbList, err := f.AsKubeAdmin.JvmbuildserviceController.ListDependencyBuilds(testNamespace)
   276  				Expect(err).ShouldNot(HaveOccurred(), "error in listing dependency builds")
   277  				allDbCompleted := len(dbList.Items) > 0
   278  				GinkgoWriter.Printf("number of dependencybuilds: %d\n", len(dbList.Items))
   279  				for _, db := range dbList.Items {
   280  					if db.Status.State != v1alpha1.DependencyBuildStateComplete {
   281  						GinkgoWriter.Printf("dependencybuild %s not complete\n", db.Spec.ScmInfo.SCMURL)
   282  						allDbCompleted = false
   283  						break
   284  					} else if db.Status.State == v1alpha1.DependencyBuildStateFailed {
   285  						Fail(fmt.Sprintf("dependencybuild %s FAILED", db.Spec.ScmInfo.SCMURL))
   286  					}
   287  				}
   288  				if allAbCompleted && allDbCompleted {
   289  					return true
   290  				}
   291  				return false
   292  			}, 2*timeout, interval).Should(BeTrue(), fmt.Sprintf("timed out when waiting for all artifactbuilds and dependencybuilds to complete in %s namespace", testNamespace))
   293  		})
   294  
   295  		It("does rebuild using cached dependencies", func() {
   296  			prun := &pipeline.PipelineRun{}
   297  
   298  			component, err := f.AsKubeAdmin.HasController.GetComponent(componentName, testNamespace)
   299  			Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("could not get component %s/%s", testNamespace, componentName))
   300  
   301  			annotations := utils.MergeMaps(component.GetAnnotations(), constants.ComponentTriggerSimpleBuildAnnotation)
   302  			component.SetAnnotations(annotations)
   303  			Expect(f.AsKubeAdmin.CommonController.KubeRest().Update(context.Background(), component, &client.UpdateOptions{})).To(Succeed())
   304  
   305  			Eventually(func() error {
   306  				prun, err = f.AsKubeAdmin.HasController.GetComponentPipelineRun(componentName, applicationName, testNamespace, "")
   307  				return err
   308  			}, 1*time.Minute, constants.PipelineRunPollingInterval).Should(BeNil(), fmt.Sprintf("timed out when getting the pipelinerun for %s/%s component", testNamespace, componentName))
   309  
   310  			ctx := context.Background()
   311  
   312  			watch, err := f.AsKubeAdmin.TektonController.GetPipelineRunWatch(ctx, testNamespace)
   313  			Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("watch pipelinerun failed in %s namespace", testNamespace))
   314  
   315  			exitForLoop := false
   316  
   317  			for {
   318  				select {
   319  				case <-time.After(15 * time.Minute):
   320  					Fail(fmt.Sprintf("timed out waiting for second build to complete in %s namespace", testNamespace))
   321  				case event := <-watch.ResultChan():
   322  					if event.Object == nil {
   323  						continue
   324  					}
   325  					pr, ok := event.Object.(*pipeline.PipelineRun)
   326  					if !ok {
   327  						continue
   328  					}
   329  					if prun.Name != pr.Name {
   330  						if pr.IsDone() {
   331  							GinkgoWriter.Printf("got event for pipelinerun %s in a terminal state\n", pr.GetName())
   332  							continue
   333  						}
   334  						Fail(fmt.Sprintf("another non-completed pipeline run %s/%s was generated when it should not", pr.GetNamespace(), pr.GetName()))
   335  					}
   336  					GinkgoWriter.Printf("done processing event for pr %s\n", pr.GetName())
   337  					if pr.IsDone() {
   338  						GinkgoWriter.Println("pr is done")
   339  
   340  						podClient := f.AsKubeAdmin.CommonController.KubeInterface().CoreV1().Pods(testNamespace)
   341  						listOptions := metav1.ListOptions{
   342  							LabelSelector: fmt.Sprintf("tekton.dev/pipelineRun=%s", pr.GetName()),
   343  						}
   344  						podList, err := podClient.List(context.Background(), listOptions)
   345  						Expect(err).ShouldNot(HaveOccurred(), "error listing pr pods")
   346  
   347  						pods := podList.Items
   348  
   349  						if len(pods) == 0 {
   350  							Fail(fmt.Sprintf("pod for pipeline run %s/%s unexpectedly missing", pr.GetNamespace(), pr.GetName()))
   351  						}
   352  
   353  						containers := []corev1.Container{}
   354  						containers = append(containers, pods[0].Spec.InitContainers...)
   355  						containers = append(containers, pods[0].Spec.Containers...)
   356  
   357  						for _, container := range containers {
   358  							if !strings.Contains(container.Name, "analyse-dependecies") {
   359  								continue
   360  							}
   361  							cLog, err := utils.GetContainerLogs(f.AsKubeAdmin.CommonController.KubeInterface(), pods[0].Name, container.Name, testNamespace)
   362  							Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("getting container logs for %s container from %s pod in %s namespace failed", container.Name, pods[0].GetName(), testNamespace))
   363  							if strings.Contains(cLog, "\"publisher\" : \"central\"") {
   364  								Fail(fmt.Sprintf("pipelinerun %s has container %s with dep analysis still pointing to central %s", pr.Name, container.Name, cLog))
   365  							}
   366  							if !strings.Contains(cLog, "\"publisher\" : \"rebuilt\"") {
   367  								Fail(fmt.Sprintf("pipelinerun %s has container %s with dep analysis that does not access rebuilt %s", pr.Name, container.Name, cLog))
   368  							}
   369  							if !strings.Contains(cLog, "\"java:scm-uri\" : \"https://github.com/stuartwdouglas/hacbs-test-simple-jdk8.git\"") {
   370  								Fail(fmt.Sprintf("pipelinerun %s has container %s with dep analysis did not include java:scm-uri %s", pr.Name, container.Name, cLog))
   371  							}
   372  							if !strings.Contains(cLog, "\"java:scm-commit\" : \"") {
   373  								Fail(fmt.Sprintf("pipelinerun %s has container %s with dep analysis did not include java:scm-commit %s", pr.Name, container.Name, cLog))
   374  							}
   375  							break
   376  						}
   377  						GinkgoWriter.Println("pr is done and has correct analyse-dependecies output, exiting")
   378  						exitForLoop = true
   379  					}
   380  				}
   381  				if exitForLoop {
   382  					break
   383  				}
   384  			}
   385  		})
   386  
   387  		It("All rebuilt images are signed and attested", func() {
   388  			seen := map[string]bool{}
   389  			rebuilt, err := f.AsKubeAdmin.JvmbuildserviceController.ListRebuiltArtifacts(testNamespace)
   390  			Expect(err).NotTo(HaveOccurred())
   391  			for _, i := range rebuilt.Items {
   392  				if seen[i.Spec.Image] {
   393  					continue
   394  				}
   395  				seen[i.Spec.Image] = true
   396  
   397  				imageWithDigest := i.Spec.Image + "@" + i.Spec.Digest
   398  
   399  				Expect(f.AsKubeAdmin.TektonController.AwaitAttestationAndSignature(imageWithDigest, constants.ChainsAttestationTimeout)).To(
   400  					Succeed(),
   401  					"Could not find .att or .sig ImageStreamTags within the 1 minute timeout. "+
   402  						"Most likely the chains-controller did not create those in time. "+
   403  						"Look at the chains-controller logs.")
   404  				GinkgoWriter.Printf("Cosign verify pass with .att and .sig ImageStreamTags found for %s\n", imageWithDigest)
   405  
   406  			}
   407  		})
   408  	})
   409  })