github.com/redhat-appstudio/e2e-tests@v0.0.0-20240520140907-9709f6f59323/tests/enterprise-contract/contract.go (about) 1 package enterprisecontract 2 3 import ( 4 "fmt" 5 "os" 6 "time" 7 8 "github.com/devfile/library/v2/pkg/util" 9 ecp "github.com/enterprise-contract/enterprise-contract-controller/api/v1alpha1" 10 . "github.com/onsi/ginkgo/v2" 11 . "github.com/onsi/gomega" 12 "github.com/redhat-appstudio/e2e-tests/pkg/clients/common" 13 kubeapi "github.com/redhat-appstudio/e2e-tests/pkg/clients/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/contract" 18 "github.com/redhat-appstudio/e2e-tests/pkg/utils/tekton" 19 pipeline "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" 20 corev1 "k8s.io/api/core/v1" 21 k8sErrors "k8s.io/apimachinery/pkg/api/errors" 22 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 23 "sigs.k8s.io/yaml" 24 ) 25 26 var _ = framework.EnterpriseContractSuiteDescribe("Enterprise Contract E2E tests", Label("ec", "HACBS"), func() { 27 28 defer GinkgoRecover() 29 30 var namespace string 31 var kubeClient *framework.ControllerHub 32 var fwk *framework.Framework 33 34 AfterEach(framework.ReportFailure(&fwk)) 35 36 BeforeAll(func() { 37 // Allow the use of a custom namespace for testing. 38 namespace = os.Getenv(constants.E2E_APPLICATIONS_NAMESPACE_ENV) 39 if len(namespace) > 0 { 40 adminClient, err := kubeapi.NewAdminKubernetesClient() 41 Expect(err).ShouldNot(HaveOccurred()) 42 kubeClient, err = framework.InitControllerHub(adminClient) 43 Expect(err).ShouldNot(HaveOccurred()) 44 _, err = kubeClient.CommonController.CreateTestNamespace(namespace) 45 Expect(err).ShouldNot(HaveOccurred()) 46 } else { 47 var err error 48 fwk, err = framework.NewFramework(utils.GetGeneratedNamespace(constants.TEKTON_CHAINS_E2E_USER)) 49 Expect(err).NotTo(HaveOccurred()) 50 Expect(fwk.UserNamespace).NotTo(BeNil(), "failed to create sandbox user") 51 namespace = fwk.UserNamespace 52 kubeClient = fwk.AsKubeAdmin 53 } 54 }) 55 56 Context("infrastructure is running", Label("pipeline"), func() { 57 It("verifies if the chains controller is running", func() { 58 err := kubeClient.CommonController.WaitForPodSelector(kubeClient.CommonController.IsPodRunning, constants.TEKTON_CHAINS_NS, "app", "tekton-chains-controller", 60, 100) 59 Expect(err).NotTo(HaveOccurred()) 60 }) 61 62 It("verifies the signing secret is present", func() { 63 timeout := time.Minute * 5 64 interval := time.Second * 1 65 66 Eventually(func() bool { 67 config, err := kubeClient.CommonController.GetSecret(constants.TEKTON_CHAINS_NS, constants.TEKTON_CHAINS_SIGNING_SECRETS_NAME) 68 Expect(err).NotTo(HaveOccurred()) 69 70 _, private := config.Data["cosign.key"] 71 _, public := config.Data["cosign.pub"] 72 _, password := config.Data["cosign.password"] 73 74 return private && public && password 75 }, timeout, interval).Should(BeTrue(), fmt.Sprintf("timed out when waiting for Tekton Chains signing secret %q to be present in %q namespace", constants.TEKTON_CHAINS_SIGNING_SECRETS_NAME, constants.TEKTON_CHAINS_NS)) 76 }) 77 }) 78 79 Context("test creating and signing an image and task", Label("pipeline"), func() { 80 // Make the PipelineRun name and namespace predictable. For convenience, the name of the 81 // PipelineRun that builds an image, is the same as the repository where the image is 82 // pushed to. 83 var buildPipelineRunName, image, imageWithDigest string 84 var pipelineRunTimeout int 85 var defaultECP *ecp.EnterpriseContractPolicy 86 87 BeforeAll(func() { 88 buildPipelineRunName = fmt.Sprintf("buildah-demo-%s", util.GenerateRandomString(10)) 89 image = fmt.Sprintf("quay.io/%s/test-images:%s", utils.GetQuayIOOrganization(), buildPipelineRunName) 90 sharedSecret, err := fwk.AsKubeAdmin.CommonController.GetSecret(constants.QuayRepositorySecretNamespace, constants.QuayRepositorySecretName) 91 Expect(err).ShouldNot(HaveOccurred(), fmt.Sprintf("error when getting shared secret - make sure the secret %s in %s namespace is created", constants.QuayRepositorySecretName, constants.QuayRepositorySecretNamespace)) 92 93 _, err = fwk.AsKubeAdmin.CommonController.GetSecret(namespace, constants.QuayRepositorySecretName) 94 if err == nil { 95 err = fwk.AsKubeAdmin.CommonController.DeleteSecret(namespace, constants.QuayRepositorySecretName) 96 Expect(err).ToNot(HaveOccurred()) 97 } else if !k8sErrors.IsNotFound(err) { 98 Expect(err).ToNot(HaveOccurred()) 99 } 100 101 repositorySecret := &corev1.Secret{ObjectMeta: metav1.ObjectMeta{Name: constants.QuayRepositorySecretName, Namespace: namespace}, 102 Type: corev1.SecretTypeDockerConfigJson, 103 Data: map[string][]byte{corev1.DockerConfigJsonKey: sharedSecret.Data[".dockerconfigjson"]}} 104 _, err = fwk.AsKubeAdmin.CommonController.CreateSecret(namespace, repositorySecret) 105 Expect(err).ShouldNot(HaveOccurred()) 106 err = fwk.AsKubeAdmin.CommonController.LinkSecretToServiceAccount(namespace, constants.QuayRepositorySecretName, constants.DefaultPipelineServiceAccount, true) 107 Expect(err).ToNot(HaveOccurred()) 108 109 pipelineRunTimeout = int(time.Duration(20) * time.Minute) 110 111 defaultECP, err = fwk.AsKubeAdmin.TektonController.GetEnterpriseContractPolicy("default", "enterprise-contract-service") 112 Expect(err).NotTo(HaveOccurred()) 113 114 // Trigger a demo task to generate an image that has been signed by Tekton Chains within the context for each spec 115 bundles, err := kubeClient.TektonController.NewBundles() 116 Expect(err).ShouldNot(HaveOccurred()) 117 dockerBuildBundle := bundles.DockerBuildBundle 118 Expect(dockerBuildBundle).NotTo(Equal(""), "Can't continue without a docker-build pipeline got from selector config") 119 pr, err := fwk.AsKubeAdmin.TektonController.RunPipeline(tekton.BuildahDemo{Image: image, Bundle: dockerBuildBundle, Namespace: namespace, Name: buildPipelineRunName}, namespace, pipelineRunTimeout) 120 Expect(err).NotTo(HaveOccurred()) 121 // Verify that the build task was created as expected. 122 Expect(pr.ObjectMeta.Name).To(Equal(buildPipelineRunName)) 123 Expect(pr.ObjectMeta.Namespace).To(Equal(namespace)) 124 Expect(fwk.AsKubeAdmin.TektonController.WatchPipelineRun(pr.Name, namespace, pipelineRunTimeout)).To(Succeed()) 125 GinkgoWriter.Printf("The pipeline named %q in namespace %q succeeded\n", pr.ObjectMeta.Name, pr.ObjectMeta.Namespace) 126 127 // The PipelineRun resource has been updated, refresh our reference. 128 pr, err = fwk.AsKubeAdmin.TektonController.GetPipelineRun(pr.ObjectMeta.Name, pr.ObjectMeta.Namespace) 129 Expect(err).NotTo(HaveOccurred()) 130 131 // Verify TaskRun has the type hinting required by Tekton Chains 132 digest, err := fwk.AsKubeAdmin.TektonController.GetTaskRunResult(fwk.AsKubeAdmin.CommonController.KubeRest(), pr, "build-container", "IMAGE_DIGEST") 133 Expect(err).NotTo(HaveOccurred()) 134 i, err := fwk.AsKubeAdmin.TektonController.GetTaskRunResult(fwk.AsKubeAdmin.CommonController.KubeRest(), pr, "build-container", "IMAGE_URL") 135 Expect(err).NotTo(HaveOccurred()) 136 Expect(i).To(Equal(image)) 137 138 // Specs now have a deterministic image reference for validation \o/ 139 imageWithDigest = fmt.Sprintf("%s@%s", image, digest) 140 141 GinkgoWriter.Printf("The image signed by Tekton Chains is %s\n", imageWithDigest) 142 }) 143 144 It("creates signature and attestation", func() { 145 err := fwk.AsKubeAdmin.TektonController.AwaitAttestationAndSignature(imageWithDigest, constants.ChainsAttestationTimeout) 146 Expect(err).NotTo( 147 HaveOccurred(), 148 "Could not find .att or .sig ImageStreamTags within the %s timeout. "+ 149 "Most likely the chains-controller did not create those in time. "+ 150 "Look at the chains-controller logs.", 151 constants.ChainsAttestationTimeout.String(), 152 ) 153 GinkgoWriter.Printf("Cosign verify pass with .att and .sig ImageStreamTags found for %s\n", imageWithDigest) 154 }) 155 156 Context("verify-enterprise-contract task", func() { 157 var generator tekton.VerifyEnterpriseContract 158 var rekorHost string 159 var verifyECTaskBundle string 160 publicSecretName := "cosign-public-key" 161 162 BeforeAll(func() { 163 // Copy the public key from openshift-pipelines/signing-secrets to a new 164 // secret that contains just the public key to ensure that access 165 // to password and private key are not needed. 166 publicKey, err := fwk.AsKubeAdmin.TektonController.GetTektonChainsPublicKey() 167 Expect(err).ToNot(HaveOccurred()) 168 GinkgoWriter.Printf("Copy public key from %s/signing-secrets to a new secret\n", constants.TEKTON_CHAINS_NS) 169 Expect(fwk.AsKubeAdmin.TektonController.CreateOrUpdateSigningSecret( 170 publicKey, publicSecretName, namespace)).To(Succeed()) 171 172 rekorHost, err = fwk.AsKubeAdmin.TektonController.GetRekorHost() 173 Expect(err).ToNot(HaveOccurred()) 174 GinkgoWriter.Printf("Configured Rekor host: %s\n", rekorHost) 175 176 cm, err := fwk.AsKubeAdmin.CommonController.GetConfigMap("ec-defaults", "enterprise-contract-service") 177 Expect(err).ToNot(HaveOccurred()) 178 verifyECTaskBundle = cm.Data["verify_ec_task_bundle"] 179 Expect(verifyECTaskBundle).ToNot(BeEmpty()) 180 GinkgoWriter.Printf("Using verify EC task bundle: %s\n", verifyECTaskBundle) 181 }) 182 183 BeforeEach(func() { 184 generator = tekton.VerifyEnterpriseContract{ 185 TaskBundle: verifyECTaskBundle, 186 Name: "verify-enterprise-contract", 187 Namespace: namespace, 188 PolicyConfiguration: "ec-policy", 189 PublicKey: fmt.Sprintf("k8s://%s/%s", namespace, publicSecretName), 190 Strict: true, 191 EffectiveTime: "now", 192 IgnoreRekor: true, 193 } 194 generator.WithComponentImage(imageWithDigest) 195 196 // Since specs could update the config policy, make sure it has a consistent 197 // baseline at the start of each spec. 198 baselinePolicies := contract.PolicySpecWithSourceConfig( 199 // A simple policy that should always succeed in a cluster where 200 // Tekton Chains is properly setup. 201 defaultECP.Spec, ecp.SourceConfig{Include: []string{"slsa_provenance_available"}}) 202 Expect(fwk.AsKubeAdmin.TektonController.CreateOrUpdatePolicyConfiguration(namespace, baselinePolicies)).To(Succeed()) 203 // printPolicyConfiguration(baselinePolicies) 204 }) 205 206 It("succeeds when policy is met", func() { 207 pr, err := fwk.AsKubeAdmin.TektonController.RunPipeline(generator, namespace, pipelineRunTimeout) 208 Expect(err).NotTo(HaveOccurred()) 209 Expect(fwk.AsKubeAdmin.TektonController.WatchPipelineRun(pr.Name, namespace, pipelineRunTimeout)).To(Succeed()) 210 211 // Refresh our copy of the PipelineRun for latest results 212 pr, err = fwk.AsKubeAdmin.TektonController.GetPipelineRun(pr.Name, pr.Namespace) 213 Expect(err).NotTo(HaveOccurred()) 214 215 tr, err := fwk.AsKubeAdmin.TektonController.GetTaskRunStatus(fwk.AsKubeAdmin.CommonController.KubeRest(), pr, "verify-enterprise-contract") 216 Expect(err).NotTo(HaveOccurred()) 217 printTaskRunStatus(tr, namespace, *kubeClient.CommonController) 218 GinkgoWriter.Printf("Make sure TaskRun %s of PipelineRun %s succeeded\n", tr.PipelineTaskName, pr.Name) 219 Expect(tekton.DidTaskRunSucceed(tr)).To(BeTrue()) 220 GinkgoWriter.Printf("Make sure result for TaskRun %q succeeded\n", tr.PipelineTaskName) 221 Expect(tr.Status.TaskRunStatusFields.Results).Should(Or( 222 // TODO: delete the first option after https://issues.redhat.com/browse/RHTAP-810 is completed 223 ContainElements(tekton.MatchTaskRunResultWithJSONPathValue(constants.OldTektonTaskTestOutputName, "{$.result}", `["SUCCESS"]`)), 224 ContainElements(tekton.MatchTaskRunResultWithJSONPathValue(constants.TektonTaskTestOutputName, "{$.result}", `["SUCCESS"]`)), 225 )) 226 }) 227 228 It("does not pass when tests are not satisfied on non-strict mode", func() { 229 policy := contract.PolicySpecWithSourceConfig( 230 // The BuildahDemo pipeline used to generate the test data does not 231 // include the required test tasks, so this policy should always fail. 232 defaultECP.Spec, ecp.SourceConfig{Include: []string{"test"}}) 233 Expect(fwk.AsKubeAdmin.TektonController.CreateOrUpdatePolicyConfiguration(namespace, policy)).To(Succeed()) 234 // printPolicyConfiguration(policy) 235 generator.Strict = false 236 pr, err := fwk.AsKubeAdmin.TektonController.RunPipeline(generator, namespace, pipelineRunTimeout) 237 Expect(err).NotTo(HaveOccurred()) 238 Expect(fwk.AsKubeAdmin.TektonController.WatchPipelineRun(pr.Name, namespace, pipelineRunTimeout)).To(Succeed()) 239 240 // Refresh our copy of the PipelineRun for latest results 241 pr, err = fwk.AsKubeAdmin.TektonController.GetPipelineRun(pr.Name, pr.Namespace) 242 Expect(err).NotTo(HaveOccurred()) 243 244 tr, err := fwk.AsKubeAdmin.TektonController.GetTaskRunStatus(fwk.AsKubeAdmin.CommonController.KubeRest(), pr, "verify-enterprise-contract") 245 Expect(err).NotTo(HaveOccurred()) 246 247 printTaskRunStatus(tr, namespace, *kubeClient.CommonController) 248 GinkgoWriter.Printf("Make sure TaskRun %s of PipelineRun %s succeeded\n", tr.PipelineTaskName, pr.Name) 249 Expect(tekton.DidTaskRunSucceed(tr)).To(BeTrue()) 250 GinkgoWriter.Printf("Make sure result for TaskRun %q succeeded\n", tr.PipelineTaskName) 251 Expect(tr.Status.TaskRunStatusFields.Results).Should(Or( 252 // TODO: delete the first option after https://issues.redhat.com/browse/RHTAP-810 is completed 253 ContainElements(tekton.MatchTaskRunResultWithJSONPathValue(constants.OldTektonTaskTestOutputName, "{$.result}", `["FAILURE"]`)), 254 ContainElements(tekton.MatchTaskRunResultWithJSONPathValue(constants.TektonTaskTestOutputName, "{$.result}", `["FAILURE"]`)), 255 )) 256 }) 257 258 It("fails when tests are not satisfied on strict mode", func() { 259 policy := contract.PolicySpecWithSourceConfig( 260 // The BuildahDemo pipeline used to generate the test data does not 261 // include the required test tasks, so this policy should always fail. 262 defaultECP.Spec, ecp.SourceConfig{Include: []string{"test"}}) 263 Expect(fwk.AsKubeAdmin.TektonController.CreateOrUpdatePolicyConfiguration(namespace, policy)).To(Succeed()) 264 // printPolicyConfiguration(policy) 265 266 generator.Strict = true 267 pr, err := fwk.AsKubeAdmin.TektonController.RunPipeline(generator, namespace, pipelineRunTimeout) 268 Expect(err).NotTo(HaveOccurred()) 269 Expect(fwk.AsKubeAdmin.TektonController.WatchPipelineRun(pr.Name, namespace, pipelineRunTimeout)).To(Succeed()) 270 271 // Refresh our copy of the PipelineRun for latest results 272 pr, err = fwk.AsKubeAdmin.TektonController.GetPipelineRun(pr.Name, pr.Namespace) 273 Expect(err).NotTo(HaveOccurred()) 274 275 tr, err := fwk.AsKubeAdmin.TektonController.GetTaskRunStatus(fwk.AsKubeAdmin.CommonController.KubeRest(), pr, "verify-enterprise-contract") 276 Expect(err).NotTo(HaveOccurred()) 277 278 printTaskRunStatus(tr, namespace, *kubeClient.CommonController) 279 GinkgoWriter.Printf("Make sure TaskRun %s of PipelineRun %s failed\n", tr.PipelineTaskName, pr.Name) 280 Expect(tekton.DidTaskRunSucceed(tr)).To(BeFalse()) 281 // Because the task fails, no results are created 282 }) 283 284 It("fails when unexpected signature is used", func() { 285 secretName := fmt.Sprintf("dummy-public-key-%s", util.GenerateRandomString(10)) 286 publicKey := []byte("-----BEGIN PUBLIC KEY-----\n" + 287 "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAENZxkE/d0fKvJ51dXHQmxXaRMTtVz\n" + 288 "BQWcmJD/7pcMDEmBcmk8O1yUPIiFj5TMZqabjS9CQQN+jKHG+Bfi0BYlHg==\n" + 289 "-----END PUBLIC KEY-----") 290 GinkgoWriter.Println("Create an unexpected public signing key") 291 Expect(fwk.AsKubeAdmin.TektonController.CreateOrUpdateSigningSecret(publicKey, secretName, namespace)).To(Succeed()) 292 generator.PublicKey = fmt.Sprintf("k8s://%s/%s", namespace, secretName) 293 294 pr, err := fwk.AsKubeAdmin.TektonController.RunPipeline(generator, namespace, pipelineRunTimeout) 295 Expect(err).NotTo(HaveOccurred()) 296 Expect(fwk.AsKubeAdmin.TektonController.WatchPipelineRun(pr.Name, namespace, pipelineRunTimeout)).To(Succeed()) 297 298 // Refresh our copy of the PipelineRun for latest results 299 pr, err = fwk.AsKubeAdmin.TektonController.GetPipelineRun(pr.Name, pr.Namespace) 300 Expect(err).NotTo(HaveOccurred()) 301 302 tr, err := fwk.AsKubeAdmin.TektonController.GetTaskRunStatus(fwk.AsKubeAdmin.CommonController.KubeRest(), pr, "verify-enterprise-contract") 303 Expect(err).NotTo(HaveOccurred()) 304 305 printTaskRunStatus(tr, namespace, *kubeClient.CommonController) 306 GinkgoWriter.Printf("Make sure TaskRun %s of PipelineRun %s failed\n", tr.PipelineTaskName, pr.Name) 307 Expect(tekton.DidTaskRunSucceed(tr)).To(BeFalse()) 308 // Because the task fails, no results are created 309 }) 310 311 Context("ec-cli command", func() { 312 It("verifies ec cli has error handling", func() { 313 generator.WithComponentImage("quay.io/redhat-appstudio/ec-golden-image:latest") 314 pr, err := fwk.AsKubeAdmin.TektonController.RunPipeline(generator, namespace, pipelineRunTimeout) 315 Expect(err).NotTo(HaveOccurred()) 316 Expect(fwk.AsKubeAdmin.TektonController.WatchPipelineRun(pr.Name, namespace, pipelineRunTimeout)).To(Succeed()) 317 318 pr, err = fwk.AsKubeAdmin.TektonController.GetPipelineRun(pr.Name, pr.Namespace) 319 Expect(err).NotTo(HaveOccurred()) 320 321 tr, err := fwk.AsKubeAdmin.TektonController.GetTaskRunStatus(fwk.AsKubeAdmin.CommonController.KubeRest(), pr, "verify-enterprise-contract") 322 Expect(err).NotTo(HaveOccurred()) 323 324 Expect(tr.Status.TaskRunStatusFields.Results).Should(Or( 325 // TODO: delete the first option after https://issues.redhat.com/browse/RHTAP-810 is completed 326 ContainElements(tekton.MatchTaskRunResultWithJSONPathValue(constants.OldTektonTaskTestOutputName, "{$.result}", `["FAILURE"]`)), 327 ContainElements(tekton.MatchTaskRunResultWithJSONPathValue(constants.TektonTaskTestOutputName, "{$.result}", `["FAILURE"]`)), 328 )) 329 //Get container step-report log details from pod 330 reportLog, err := utils.GetContainerLogs(fwk.AsKubeAdmin.CommonController.KubeInterface(), tr.Status.PodName, "step-report", namespace) 331 GinkgoWriter.Printf("*** Logs from pod '%s', container '%s':\n----- START -----%s----- END -----\n", tr.Status.PodName, "step-report", reportLog) 332 Expect(err).NotTo(HaveOccurred()) 333 Expect(reportLog).Should(ContainSubstring("No image attestations found matching the given public key")) 334 }) 335 336 It("verifies ec validate accepts a list of image references", func() { 337 secretName := fmt.Sprintf("golden-image-public-key%s", util.GenerateRandomString(10)) 338 GinkgoWriter.Println("Update public key to verify golden images") 339 goldenImagePublicKey := []byte("-----BEGIN PUBLIC KEY-----\n" + 340 "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZP/0htjhVt2y0ohjgtIIgICOtQtA\n" + 341 "naYJRuLprwIv6FDhZ5yFjYUEtsmoNcW7rx2KM6FOXGsCX3BNc7qhHELT+g==\n" + 342 "-----END PUBLIC KEY-----") 343 Expect(fwk.AsKubeAdmin.TektonController.CreateOrUpdateSigningSecret(goldenImagePublicKey, secretName, namespace)).To(Succeed()) 344 generator.PublicKey = fmt.Sprintf("k8s://%s/%s", namespace, secretName) 345 346 policy := contract.PolicySpecWithSourceConfig( 347 defaultECP.Spec, ecp.SourceConfig{Include: []string{"minimal"}}) 348 Expect(fwk.AsKubeAdmin.TektonController.CreateOrUpdatePolicyConfiguration(namespace, policy)).To(Succeed()) 349 350 generator.WithComponentImage("quay.io/redhat-appstudio/ec-golden-image:latest") 351 generator.AppendComponentImage("quay.io/redhat-appstudio/ec-golden-image:e2e-test-unacceptable-task") 352 pr, err := fwk.AsKubeAdmin.TektonController.RunPipeline(generator, namespace, pipelineRunTimeout) 353 Expect(err).NotTo(HaveOccurred()) 354 Expect(fwk.AsKubeAdmin.TektonController.WatchPipelineRun(pr.Name, namespace, pipelineRunTimeout)).To(Succeed()) 355 356 pr, err = fwk.AsKubeAdmin.TektonController.GetPipelineRun(pr.Name, pr.Namespace) 357 Expect(err).NotTo(HaveOccurred()) 358 359 tr, err := fwk.AsKubeAdmin.TektonController.GetTaskRunStatus(fwk.AsKubeAdmin.CommonController.KubeRest(), pr, "verify-enterprise-contract") 360 Expect(err).NotTo(HaveOccurred()) 361 362 Expect(tr.Status.TaskRunStatusFields.Results).Should(Or( 363 // TODO: delete the first option after https://issues.redhat.com/browse/RHTAP-810 is completed 364 ContainElements(tekton.MatchTaskRunResultWithJSONPathValue(constants.OldTektonTaskTestOutputName, "{$.result}", `["SUCCESS"]`)), 365 ContainElements(tekton.MatchTaskRunResultWithJSONPathValue(constants.TektonTaskTestOutputName, "{$.result}", `["SUCCESS"]`)), 366 )) 367 //Get container step-report log details from pod 368 reportLog, err := utils.GetContainerLogs(fwk.AsKubeAdmin.CommonController.KubeInterface(), tr.Status.PodName, "step-report", namespace) 369 GinkgoWriter.Printf("*** Logs from pod '%s', container '%s':\n----- START -----%s----- END -----\n", tr.Status.PodName, "step-report", reportLog) 370 Expect(err).NotTo(HaveOccurred()) 371 }) 372 }) 373 374 Context("Release Policy", func() { 375 It("verifies redhat products pass the redhat policy rule collection before release ", func() { 376 secretName := fmt.Sprintf("golden-image-public-key%s", util.GenerateRandomString(10)) 377 GinkgoWriter.Println("Update public key to verify golden images") 378 goldenImagePublicKey := []byte("-----BEGIN PUBLIC KEY-----\n" + 379 "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZP/0htjhVt2y0ohjgtIIgICOtQtA\n" + 380 "naYJRuLprwIv6FDhZ5yFjYUEtsmoNcW7rx2KM6FOXGsCX3BNc7qhHELT+g==\n" + 381 "-----END PUBLIC KEY-----") 382 Expect(fwk.AsKubeAdmin.TektonController.CreateOrUpdateSigningSecret(goldenImagePublicKey, secretName, namespace)).To(Succeed()) 383 redhatECP, error := fwk.AsKubeAdmin.TektonController.GetEnterpriseContractPolicy("redhat", "enterprise-contract-service") 384 Expect(error).NotTo(HaveOccurred()) 385 generator.PublicKey = fmt.Sprintf("k8s://%s/%s", namespace, secretName) 386 policy := contract.PolicySpecWithSourceConfig( 387 redhatECP.Spec, 388 ecp.SourceConfig{Include: []string{"redhat"}}) 389 Expect(fwk.AsKubeAdmin.TektonController.CreateOrUpdatePolicyConfiguration(namespace, policy)).To(Succeed()) 390 391 generator.WithComponentImage("quay.io/redhat-appstudio/ec-golden-image:latest") 392 pr, err := fwk.AsKubeAdmin.TektonController.RunPipeline(generator, namespace, pipelineRunTimeout) 393 Expect(err).NotTo(HaveOccurred()) 394 Expect(fwk.AsKubeAdmin.TektonController.WatchPipelineRun(pr.Name, namespace, pipelineRunTimeout)).To(Succeed()) 395 396 pr, err = fwk.AsKubeAdmin.TektonController.GetPipelineRun(pr.Name, pr.Namespace) 397 Expect(err).NotTo(HaveOccurred()) 398 399 tr, err := fwk.AsKubeAdmin.TektonController.GetTaskRunStatus(fwk.AsKubeAdmin.CommonController.KubeRest(), pr, "verify-enterprise-contract") 400 Expect(err).NotTo(HaveOccurred()) 401 402 Expect(tr.Status.TaskRunStatusFields.Results).Should(Or( 403 // TODO: delete the first option after https://issues.redhat.com/browse/RHTAP-810 is completed 404 ContainElements(tekton.MatchTaskRunResultWithJSONPathValue(constants.OldTektonTaskTestOutputName, "{$.result}", `["SUCCESS"]`)), 405 ContainElements(tekton.MatchTaskRunResultWithJSONPathValue(constants.TektonTaskTestOutputName, "{$.result}", `["SUCCESS"]`)), 406 )) 407 }) 408 It("verifies the release policy: Task bundles are in acceptable bundle list", func() { 409 secretName := fmt.Sprintf("golden-image-public-key%s", util.GenerateRandomString(10)) 410 GinkgoWriter.Println("Update public key to verify golden images") 411 goldenImagePublicKey := []byte("-----BEGIN PUBLIC KEY-----\n" + 412 "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEZP/0htjhVt2y0ohjgtIIgICOtQtA\n" + 413 "naYJRuLprwIv6FDhZ5yFjYUEtsmoNcW7rx2KM6FOXGsCX3BNc7qhHELT+g==\n" + 414 "-----END PUBLIC KEY-----") 415 Expect(fwk.AsKubeAdmin.TektonController.CreateOrUpdateSigningSecret(goldenImagePublicKey, secretName, namespace)).To(Succeed()) 416 generator.PublicKey = fmt.Sprintf("k8s://%s/%s", namespace, secretName) 417 policy := contract.PolicySpecWithSourceConfig( 418 defaultECP.Spec, 419 ecp.SourceConfig{Include: []string{ 420 // Account for "acceptable" to "trusted" renaming. Eventually remove "acceptable" from this list 421 "attestation_task_bundle.task_ref_bundles_acceptable", 422 "attestation_task_bundle.task_ref_bundles_trusted", 423 }}, 424 ) 425 Expect(fwk.AsKubeAdmin.TektonController.CreateOrUpdatePolicyConfiguration(namespace, policy)).To(Succeed()) 426 427 generator.WithComponentImage("quay.io/redhat-appstudio/ec-golden-image:e2e-test-unacceptable-task") 428 pr, err := fwk.AsKubeAdmin.TektonController.RunPipeline(generator, namespace, pipelineRunTimeout) 429 Expect(err).NotTo(HaveOccurred()) 430 Expect(fwk.AsKubeAdmin.TektonController.WatchPipelineRun(pr.Name, namespace, pipelineRunTimeout)).To(Succeed()) 431 432 pr, err = fwk.AsKubeAdmin.TektonController.GetPipelineRun(pr.Name, pr.Namespace) 433 Expect(err).NotTo(HaveOccurred()) 434 435 tr, err := fwk.AsKubeAdmin.TektonController.GetTaskRunStatus(fwk.AsKubeAdmin.CommonController.KubeRest(), pr, "verify-enterprise-contract") 436 Expect(err).NotTo(HaveOccurred()) 437 438 Expect(tr.Status.TaskRunStatusFields.Results).Should(Or( 439 // TODO: delete the first option after https://issues.redhat.com/browse/RHTAP-810 is completed 440 ContainElements(tekton.MatchTaskRunResultWithJSONPathValue(constants.OldTektonTaskTestOutputName, "{$.result}", `["FAILURE"]`)), 441 ContainElements(tekton.MatchTaskRunResultWithJSONPathValue(constants.TektonTaskTestOutputName, "{$.result}", `["FAILURE"]`)), 442 )) 443 444 //Get container step-report log details from pod 445 reportLog, err := utils.GetContainerLogs(fwk.AsKubeAdmin.CommonController.KubeInterface(), tr.Status.PodName, "step-report", namespace) 446 GinkgoWriter.Printf("*** Logs from pod '%s', container '%s':\n----- START -----%s----- END -----\n", tr.Status.PodName, "step-report", reportLog) 447 Expect(err).NotTo(HaveOccurred()) 448 Expect(reportLog).Should(MatchRegexp(`Pipeline task .* uses an (?:untrusted|unacceptable) task bundle`)) 449 }) 450 451 It("verifies the release policy: Task bundle references pinned to digest", func() { 452 secretName := fmt.Sprintf("unpinned-task-bundle-public-key%s", util.GenerateRandomString(10)) 453 unpinnedTaskPublicKey := []byte("-----BEGIN PUBLIC KEY-----\n" + 454 "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEPfwkY/ru2JRd6FSqIp7lT3gzjaEC\n" + 455 "EAg+paWtlme2KNcostCsmIbwz+bc2aFV+AxCOpRjRpp3vYrbS5KhkmgC1Q==\n" + 456 "-----END PUBLIC KEY-----") 457 GinkgoWriter.Println("Update public <key to verify unpinned task image") 458 Expect(fwk.AsKubeAdmin.TektonController.CreateOrUpdateSigningSecret(unpinnedTaskPublicKey, secretName, namespace)).To(Succeed()) 459 generator.PublicKey = fmt.Sprintf("k8s://%s/%s", namespace, secretName) 460 461 policy := contract.PolicySpecWithSourceConfig( 462 defaultECP.Spec, 463 ecp.SourceConfig{Include: []string{"attestation_task_bundle.task_ref_bundles_pinned"}}, 464 ) 465 Expect(fwk.AsKubeAdmin.TektonController.CreateOrUpdatePolicyConfiguration(namespace, policy)).To(Succeed()) 466 467 generator.WithComponentImage("quay.io/redhat-appstudio-qe/enterprise-contract-tests:e2e-test-unpinned-task-bundle") 468 pr, err := fwk.AsKubeAdmin.TektonController.RunPipeline(generator, namespace, pipelineRunTimeout) 469 Expect(err).NotTo(HaveOccurred()) 470 Expect(fwk.AsKubeAdmin.TektonController.WatchPipelineRun(pr.Name, namespace, pipelineRunTimeout)).To(Succeed()) 471 472 pr, err = fwk.AsKubeAdmin.TektonController.GetPipelineRun(pr.Name, pr.Namespace) 473 Expect(err).NotTo(HaveOccurred()) 474 475 tr, err := fwk.AsKubeAdmin.TektonController.GetTaskRunStatus(fwk.AsKubeAdmin.CommonController.KubeRest(), pr, "verify-enterprise-contract") 476 Expect(err).NotTo(HaveOccurred()) 477 478 Expect(tr.Status.TaskRunStatusFields.Results).Should(Or( 479 // TODO: delete the first option after https://issues.redhat.com/browse/RHTAP-810 is completed 480 ContainElements(tekton.MatchTaskRunResultWithJSONPathValue(constants.OldTektonTaskTestOutputName, "{$.result}", `["WARNING"]`)), 481 ContainElements(tekton.MatchTaskRunResultWithJSONPathValue(constants.TektonTaskTestOutputName, "{$.result}", `["WARNING"]`)), 482 )) 483 484 //Get container step-report log details from pod 485 reportLog, err := utils.GetContainerLogs(fwk.AsKubeAdmin.CommonController.KubeInterface(), tr.Status.PodName, "step-report", namespace) 486 GinkgoWriter.Printf("*** Logs from pod '%s', container '%s':\n----- START -----%s----- END -----\n", tr.Status.PodName, "step-report", reportLog) 487 Expect(err).NotTo(HaveOccurred()) 488 Expect(reportLog).Should(MatchRegexp(`Pipeline task .* uses an unpinned task bundle reference`)) 489 }) 490 }) 491 }) 492 493 }) 494 }) 495 496 func printTaskRunStatus(tr *pipeline.PipelineRunTaskRunStatus, namespace string, sc common.SuiteController) { 497 if tr.Status == nil { 498 GinkgoWriter.Println("*** TaskRun status: nil") 499 return 500 } 501 502 if y, err := yaml.Marshal(tr.Status); err == nil { 503 GinkgoWriter.Printf("*** TaskRun status:\n%s\n", string(y)) 504 } else { 505 GinkgoWriter.Printf("*** Unable to serialize TaskRunStatus to YAML: %#v; error: %s\n", tr.Status, err) 506 } 507 508 for _, s := range tr.Status.TaskRunStatusFields.Steps { 509 if logs, err := utils.GetContainerLogs(sc.KubeInterface(), tr.Status.PodName, s.Container, namespace); err == nil { 510 GinkgoWriter.Printf("*** Logs from pod '%s', container '%s':\n----- START -----%s----- END -----\n", tr.Status.PodName, s.Container, logs) 511 } else { 512 GinkgoWriter.Printf("*** Can't fetch logs from pod '%s', container '%s': %s\n", tr.Status.PodName, s.Container, err) 513 } 514 } 515 }