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 })