github.com/redhat-appstudio/e2e-tests@v0.0.0-20230619105049-9a422b2094d7/tests/build/build_templates.go (about)

     1  package build
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"os"
     7  	"time"
     8  
     9  	"github.com/devfile/library/pkg/util"
    10  	ecp "github.com/enterprise-contract/enterprise-contract-controller/api/v1alpha1"
    11  	. "github.com/onsi/ginkgo/v2"
    12  	. "github.com/onsi/gomega"
    13  	kubeapi "github.com/redhat-appstudio/e2e-tests/pkg/apis/kubernetes"
    14  	"github.com/redhat-appstudio/e2e-tests/pkg/constants"
    15  	"github.com/redhat-appstudio/e2e-tests/pkg/framework"
    16  	"github.com/redhat-appstudio/e2e-tests/pkg/utils"
    17  	"github.com/redhat-appstudio/e2e-tests/pkg/utils/build"
    18  	"github.com/redhat-appstudio/e2e-tests/pkg/utils/pipeline"
    19  	"github.com/redhat-appstudio/e2e-tests/pkg/utils/tekton"
    20  	"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1beta1"
    21  	"k8s.io/apimachinery/pkg/api/errors"
    22  	"k8s.io/apimachinery/pkg/util/rand"
    23  	"k8s.io/apimachinery/pkg/util/wait"
    24  )
    25  
    26  var _ = framework.BuildSuiteDescribe("Build service E2E tests", Label("build", "HACBS"), func() {
    27  	var f *framework.Framework
    28  	var err error
    29  
    30  	defer GinkgoRecover()
    31  	Describe("HACBS pipelines", Ordered, Label("pipeline"), func() {
    32  
    33  		var applicationName, componentName, testNamespace string
    34  		var kubeadminClient *framework.ControllerHub
    35  		pipelineCreatedRetryInterval := time.Second * 5
    36  		pipelineCreatedTimeout := time.Minute * 5
    37  
    38  		BeforeAll(func() {
    39  			if os.Getenv("APP_SUFFIX") != "" {
    40  				applicationName = fmt.Sprintf("test-app-%s", os.Getenv("APP_SUFFIX"))
    41  			} else {
    42  				applicationName = fmt.Sprintf("test-app-%s", util.GenerateRandomString(4))
    43  			}
    44  			testNamespace = os.Getenv(constants.E2E_APPLICATIONS_NAMESPACE_ENV)
    45  			if len(testNamespace) > 0 {
    46  				asAdminClient, err := kubeapi.NewAdminKubernetesClient()
    47  				Expect(err).ShouldNot(HaveOccurred())
    48  				kubeadminClient, err = framework.InitControllerHub(asAdminClient)
    49  				Expect(err).ShouldNot(HaveOccurred())
    50  				_, err = kubeadminClient.CommonController.CreateTestNamespace(testNamespace)
    51  				Expect(err).ShouldNot(HaveOccurred())
    52  			} else {
    53  				f, err = framework.NewFramework(utils.GetGeneratedNamespace("build-e2e"))
    54  				Expect(err).NotTo(HaveOccurred())
    55  				testNamespace = f.UserNamespace
    56  				Expect(f.UserNamespace).NotTo(BeNil())
    57  				kubeadminClient = f.AsKubeAdmin
    58  			}
    59  
    60  			_, err = kubeadminClient.HasController.GetHasApplication(applicationName, testNamespace)
    61  			// In case the app with the same name exist in the selected namespace, delete it first
    62  			if err == nil {
    63  				Expect(kubeadminClient.HasController.DeleteHasApplication(applicationName, testNamespace, false)).To(Succeed())
    64  				Eventually(func() bool {
    65  					_, err := kubeadminClient.HasController.GetHasApplication(applicationName, testNamespace)
    66  					return errors.IsNotFound(err)
    67  				}, time.Minute*5, time.Second*1).Should(BeTrue(), "timed out when waiting for the app %s to be deleted in %s namespace", applicationName, testNamespace)
    68  			}
    69  			app, err := kubeadminClient.HasController.CreateHasApplication(applicationName, testNamespace)
    70  			Expect(err).NotTo(HaveOccurred())
    71  			Expect(utils.WaitUntil(kubeadminClient.HasController.ApplicationGitopsRepoExists(app.Status.Devfile), 30*time.Second)).To(
    72  				Succeed(), fmt.Sprintf("timed out waiting for gitops content to be created for app %s in namespace %s: %+v", app.Name, app.Namespace, err),
    73  			)
    74  
    75  			for _, gitUrl := range componentUrls {
    76  				gitUrl := gitUrl
    77  				componentName = fmt.Sprintf("%s-%s", "test-comp", util.GenerateRandomString(4))
    78  				// Create a component with Git Source URL being defined
    79  				// using cdq since git ref is not known
    80  				cdq, err := kubeadminClient.HasController.CreateComponentDetectionQuery(componentName, testNamespace, gitUrl, "", "", "", false)
    81  				Expect(err).ShouldNot(HaveOccurred())
    82  				Expect(len(cdq.Status.ComponentDetected)).To(Equal(1), "Expected length of the detected Components was not 1")
    83  
    84  				for _, compDetected := range cdq.Status.ComponentDetected {
    85  					c, err := kubeadminClient.HasController.CreateComponentFromStubSkipInitialChecks(compDetected, testNamespace, "", "", applicationName, false)
    86  					Expect(err).ShouldNot(HaveOccurred())
    87  					componentNames = append(componentNames, c.Name)
    88  				}
    89  			}
    90  		})
    91  
    92  		AfterAll(func() {
    93  			// Do cleanup only in case the test succeeded
    94  			if !CurrentSpecReport().Failed() {
    95  				// Clean up only Application CR (Component and Pipelines are included) in case we are targeting specific namespace
    96  				// Used e.g. in build-definitions e2e tests, where we are targeting build-templates-e2e namespace
    97  				if os.Getenv(constants.E2E_APPLICATIONS_NAMESPACE_ENV) != "" {
    98  					DeferCleanup(kubeadminClient.HasController.DeleteHasApplication, applicationName, testNamespace, false)
    99  				} else {
   100  					Expect(kubeadminClient.TektonController.DeleteAllPipelineRunsInASpecificNamespace(testNamespace)).To(Succeed())
   101  					Expect(f.SandboxController.DeleteUserSignup(f.UserName)).NotTo(BeFalse())
   102  					Expect(kubeadminClient.CommonController.DeleteProxyPlugin("tekton-results", "toolchain-host-operator")).NotTo(BeFalse())
   103  				}
   104  			}
   105  		})
   106  
   107  		for i, gitUrl := range componentUrls {
   108  			i := i
   109  			gitUrl := gitUrl
   110  			It(fmt.Sprintf("triggers PipelineRun for component with source URL %s", gitUrl), Label(buildTemplatesTestLabel), func() {
   111  				timeout := time.Minute * 25
   112  				interval := time.Second * 1
   113  
   114  				Eventually(func() bool {
   115  					pipelineRun, err := kubeadminClient.HasController.GetComponentPipelineRun(componentNames[i], applicationName, testNamespace, "")
   116  					if err != nil {
   117  						GinkgoWriter.Println("PipelineRun has not been created yet")
   118  						return false
   119  					}
   120  					return pipelineRun.HasStarted()
   121  				}, timeout, interval).Should(BeTrue(), "timed out when waiting for the %s PipelineRun to start", componentNames[i])
   122  			})
   123  		}
   124  
   125  		for i, gitUrl := range componentUrls {
   126  			i := i
   127  			gitUrl := gitUrl
   128  
   129  			It(fmt.Sprintf("should eventually finish successfully for component with source URL %s", gitUrl), Label(buildTemplatesTestLabel), func() {
   130  				component, err := kubeadminClient.HasController.GetHasComponent(componentNames[i], testNamespace)
   131  				Expect(err).ShouldNot(HaveOccurred())
   132  				Expect(kubeadminClient.HasController.WaitForComponentPipelineToBeFinished(component, "", 2)).To(Succeed())
   133  			})
   134  
   135  			It("should ensure SBOM is shown", Label(buildTemplatesTestLabel), func() {
   136  				pipelineRun, err := kubeadminClient.HasController.GetComponentPipelineRun(componentNames[i], applicationName, testNamespace, "")
   137  				Expect(err).ShouldNot(HaveOccurred())
   138  				Expect(pipelineRun).ToNot(BeNil(), "component pipelinerun not found")
   139  
   140  				logs, err := kubeadminClient.TektonController.GetTaskRunLogs(pipelineRun.Name, "show-sbom", testNamespace)
   141  				Expect(err).ShouldNot(HaveOccurred())
   142  				Expect(logs).To(HaveLen(1))
   143  				var sbomTaskLog string
   144  				for _, log := range logs {
   145  					sbomTaskLog = log
   146  				}
   147  
   148  				sbom := &build.SbomCyclonedx{}
   149  				err = json.Unmarshal([]byte(sbomTaskLog), sbom)
   150  				Expect(err).NotTo(HaveOccurred(), "failed to parse SBOM from show-sbom task output")
   151  				Expect(sbom.BomFormat).ToNot(BeEmpty())
   152  				Expect(sbom.SpecVersion).ToNot(BeEmpty())
   153  				Expect(len(sbom.Components)).To(BeNumerically(">=", 1))
   154  			})
   155  
   156  			When("Pipeline Results are stored", Label("pipeline"), func() {
   157  				var resultClient *pipeline.ResultClient
   158  				var pipelineRun *v1beta1.PipelineRun
   159  
   160  				BeforeAll(func() {
   161  					// create the proxyplugin for tekton-results
   162  					_, err = kubeadminClient.CommonController.CreateProxyPlugin("tekton-results", "toolchain-host-operator", "tekton-results", "tekton-results")
   163  					Expect(err).NotTo(HaveOccurred())
   164  
   165  					regProxyUrl := fmt.Sprintf("%s/plugins/tekton-results", f.ProxyUrl)
   166  					resultClient = pipeline.NewClient(regProxyUrl, f.UserToken)
   167  
   168  					err = wait.Poll(pipelineCreatedRetryInterval, pipelineCreatedTimeout, func() (done bool, err error) {
   169  						pipelineRun, err = f.AsKubeDeveloper.HasController.GetComponentPipelineRun(componentNames[i], applicationName, testNamespace, "")
   170  						if err != nil {
   171  							time.Sleep(time.Millisecond * time.Duration(rand.IntnRange(10, 200)))
   172  							return false, fmt.Errorf("deletion of PipelineRun has been timedout: %v", err)
   173  						}
   174  						return true, nil
   175  					})
   176  					Expect(err).ShouldNot(HaveOccurred())
   177  				})
   178  
   179  				It("should have Pipeline Records", func() {
   180  					records, err := resultClient.GetRecords(testNamespace, string(pipelineRun.GetUID()))
   181  					// temporary logs due to RHTAPBUGS-213
   182  					GinkgoWriter.Printf("records for PipelineRun %s:\n%s\n", pipelineRun.Name, records)
   183  					Expect(err).NotTo(HaveOccurred(), "got error getting records for PipelineRun %s: %v", pipelineRun.Name, err)
   184  					Expect(len(records.Record)).NotTo(BeZero(), "No records found for PipelineRun %s", pipelineRun.Name)
   185  				})
   186  
   187  				It("should have Pipeline Logs", func() {
   188  					// Verify if result is stored in Database
   189  					// temporary logs due to RHTAPBUGS-213
   190  					logs, err := resultClient.GetLogs(testNamespace, string(pipelineRun.GetUID()))
   191  					GinkgoWriter.Printf("logs for PipelineRun %s:\n%s\n", pipelineRun.Name, logs)
   192  					Expect(err).NotTo(HaveOccurred(), "got error getting logs for PipelineRun %s: %v", pipelineRun.Name, err)
   193  
   194  					timeout := time.Minute * 2
   195  					interval := time.Second * 10
   196  					// temporary timeout  due to RHTAPBUGS-213
   197  					Eventually(func() (bool, error) {
   198  						// temporary logs due to RHTAPBUGS-213
   199  						logs, err = resultClient.GetLogs(testNamespace, string(pipelineRun.GetUID()))
   200  						GinkgoWriter.Printf("logs for PipelineRun %s:\n%s\n", pipelineRun.Name, logs)
   201  						Expect(err).NotTo(HaveOccurred(), "got error getting logs for PipelineRun %s: %v", pipelineRun.Name, err)
   202  
   203  						return len(logs.Record) != 0, err
   204  					}, timeout, interval).Should(BeTrue(), fmt.Sprintf("timed out when getting logs for PipelineRun %s", pipelineRun.Name))
   205  
   206  					// Verify if result is stored in S3
   207  					// temporary logs due to RHTAPBUGS-213
   208  					log, err := resultClient.GetLogByName(logs.Record[0].Name)
   209  					GinkgoWriter.Printf("log for record %s:\n%s\n", logs.Record[0].Name, log)
   210  					Expect(err).NotTo(HaveOccurred(), "got error getting log '%s' for PipelineRun %s: %v", logs.Record[0].Name, pipelineRun.Name, err)
   211  					Expect(len(log)).NotTo(BeZero(), "no log content '%s' found for PipelineRun %s", logs.Record[0].Name, pipelineRun.Name)
   212  				})
   213  			})
   214  
   215  			It("should validate tekton taskrun test results", Label(buildTemplatesTestLabel), func() {
   216  				pipelineRun, err := kubeadminClient.HasController.GetComponentPipelineRun(componentNames[i], applicationName, testNamespace, "")
   217  				Expect(err).ShouldNot(HaveOccurred())
   218  				Expect(build.ValidateBuildPipelineTestResults(pipelineRun, kubeadminClient.CommonController.KubeRest())).To(Succeed())
   219  			})
   220  
   221  			When("the container image is created and pushed to container registry", Label("sbom", "slow"), func() {
   222  				var outputImage string
   223  				var kubeController tekton.KubeController
   224  				BeforeAll(func() {
   225  					pipelineRun, err := kubeadminClient.HasController.GetComponentPipelineRun(componentNames[i], applicationName, testNamespace, "")
   226  					Expect(err).ShouldNot(HaveOccurred())
   227  
   228  					for _, p := range pipelineRun.Spec.Params {
   229  						if p.Name == "output-image" {
   230  							outputImage = p.Value.StringVal
   231  						}
   232  					}
   233  					Expect(outputImage).ToNot(BeEmpty(), "output image of a component could not be found")
   234  
   235  					kubeController = tekton.KubeController{
   236  						Commonctrl: *kubeadminClient.CommonController,
   237  						Tektonctrl: *kubeadminClient.TektonController,
   238  						Namespace:  testNamespace,
   239  					}
   240  				})
   241  				It("verify-enterprice-contract check should pass", Label(buildTemplatesTestLabel), func() {
   242  					Skip("Skip until RHTAP bug is solved: https://issues.redhat.com/browse/RHTAPBUGS-352")
   243  					cm, err := kubeController.Commonctrl.GetConfigMap("ec-defaults", "enterprise-contract-service")
   244  					Expect(err).ToNot(HaveOccurred())
   245  
   246  					verifyECTaskBundle := cm.Data["verify_ec_task_bundle"]
   247  					Expect(verifyECTaskBundle).ToNot(BeEmpty())
   248  
   249  					publicSecretName := "cosign-public-key"
   250  					publicKey, err := kubeController.GetTektonChainsPublicKey()
   251  					Expect(err).ToNot(HaveOccurred())
   252  
   253  					Expect(kubeController.CreateOrUpdateSigningSecret(
   254  						publicKey, publicSecretName, testNamespace)).To(Succeed())
   255  
   256  					defaultEcp, err := kubeController.GetEnterpriseContractPolicy("default", "enterprise-contract-service")
   257  					Expect(err).NotTo(HaveOccurred())
   258  
   259  					policySource := defaultEcp.Spec.Sources
   260  					policy := ecp.EnterpriseContractPolicySpec{
   261  						Sources: policySource,
   262  						Configuration: &ecp.EnterpriseContractPolicyConfiguration{
   263  							// The BuildahDemo pipeline used to generate the test data does not
   264  							// include the required test tasks, so this policy should always fail.
   265  							Collections: []string{"slsa2"},
   266  							Exclude:     []string{"cve"},
   267  						},
   268  					}
   269  					Expect(kubeController.CreateOrUpdatePolicyConfiguration(testNamespace, policy)).To(Succeed())
   270  
   271  					generator := tekton.VerifyEnterpriseContract{
   272  						ApplicationName:     applicationName,
   273  						Bundle:              verifyECTaskBundle,
   274  						ComponentName:       componentNames[i],
   275  						Image:               outputImage,
   276  						Name:                "verify-enterprise-contract",
   277  						Namespace:           testNamespace,
   278  						PolicyConfiguration: "ec-policy",
   279  						PublicKey:           fmt.Sprintf("k8s://%s/%s", testNamespace, publicSecretName),
   280  						SSLCertDir:          "/var/run/secrets/kubernetes.io/serviceaccount",
   281  						Strict:              true,
   282  						EffectiveTime:       "now",
   283  					}
   284  					ecPipelineRunTimeout := int(time.Duration(10 * time.Minute).Seconds())
   285  					pr, err := kubeController.RunPipeline(generator, ecPipelineRunTimeout)
   286  					Expect(err).NotTo(HaveOccurred())
   287  
   288  					Expect(kubeController.WatchPipelineRun(pr.Name, ecPipelineRunTimeout)).To(Succeed())
   289  
   290  					pr, err = kubeController.Tektonctrl.GetPipelineRun(pr.Name, pr.Namespace)
   291  					Expect(err).NotTo(HaveOccurred())
   292  
   293  					tr, err := kubeController.GetTaskRunStatus(kubeadminClient.CommonController.KubeRest(), pr, "verify-enterprise-contract")
   294  					Expect(err).NotTo(HaveOccurred())
   295  					Expect(tekton.DidTaskSucceed(tr)).To(BeTrue())
   296  					Expect(tr.Status.TaskRunResults).Should(Or(
   297  						// TODO: delete the first option after https://issues.redhat.com/browse/RHTAP-810 is completed
   298  						ContainElements(tekton.MatchTaskRunResultWithJSONPathValue(constants.OldTektonTaskTestOutputName, "{$.result}", `["SUCCESS"]`)),
   299  						ContainElements(tekton.MatchTaskRunResultWithJSONPathValue(constants.TektonTaskTestOutputName, "{$.result}", `["SUCCESS"]`)),
   300  					))
   301  				})
   302  				It("contains non-empty sbom files", Label(buildTemplatesTestLabel), func() {
   303  
   304  					purl, cyclonedx, err := build.GetParsedSbomFilesContentFromImage(outputImage)
   305  					Expect(err).NotTo(HaveOccurred())
   306  
   307  					Expect(cyclonedx.BomFormat).To(Equal("CycloneDX"))
   308  					Expect(cyclonedx.SpecVersion).ToNot(BeEmpty())
   309  					Expect(cyclonedx.Version).ToNot(BeZero())
   310  					Expect(cyclonedx.Components).ToNot(BeEmpty())
   311  
   312  					numberOfLibraryComponents := 0
   313  					for _, component := range cyclonedx.Components {
   314  						Expect(component.Name).ToNot(BeEmpty())
   315  						Expect(component.Type).ToNot(BeEmpty())
   316  						Expect(component.Version).ToNot(BeEmpty())
   317  
   318  						if component.Type == "library" {
   319  							Expect(component.Purl).ToNot(BeEmpty())
   320  							numberOfLibraryComponents++
   321  						}
   322  					}
   323  
   324  					Expect(purl.ImageContents.Dependencies).ToNot(BeEmpty())
   325  					Expect(len(purl.ImageContents.Dependencies)).To(Equal(numberOfLibraryComponents))
   326  
   327  					for _, dependency := range purl.ImageContents.Dependencies {
   328  						Expect(dependency.Purl).ToNot(BeEmpty())
   329  					}
   330  				})
   331  			})
   332  		}
   333  	})
   334  })