github.com/redhat-appstudio/e2e-tests@v0.0.0-20240520140907-9709f6f59323/tests/release/pipelines/rh_push_to_external_registry.go (about)

     1  package pipelines
     2  
     3  import (
     4  	"encoding/base64"
     5  	"encoding/json"
     6  	"fmt"
     7  	"os"
     8  	"time"
     9  
    10  	"github.com/devfile/library/v2/pkg/util"
    11  	. "github.com/onsi/ginkgo/v2"
    12  	. "github.com/onsi/gomega"
    13  	appservice "github.com/redhat-appstudio/application-api/api/v1alpha1"
    14  	"github.com/redhat-appstudio/e2e-tests/pkg/clients/has"
    15  	"github.com/redhat-appstudio/e2e-tests/pkg/clients/release"
    16  	"github.com/redhat-appstudio/e2e-tests/pkg/constants"
    17  	"github.com/redhat-appstudio/e2e-tests/pkg/framework"
    18  	"github.com/redhat-appstudio/e2e-tests/pkg/utils"
    19  	"github.com/redhat-appstudio/e2e-tests/pkg/utils/contract"
    20  	"github.com/redhat-appstudio/e2e-tests/pkg/utils/tekton"
    21  	releasecommon "github.com/redhat-appstudio/e2e-tests/tests/release"
    22  	releaseApi "github.com/redhat-appstudio/release-service/api/v1alpha1"
    23  	tektonutils "github.com/redhat-appstudio/release-service/tekton/utils"
    24  	pipeline "github.com/tektoncd/pipeline/pkg/apis/pipeline/v1"
    25  	"k8s.io/apimachinery/pkg/runtime"
    26  
    27  	ecp "github.com/enterprise-contract/enterprise-contract-controller/api/v1alpha1"
    28  	corev1 "k8s.io/api/core/v1"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  )
    31  
    32  var _ = framework.ReleasePipelinesSuiteDescribe("[HACBS-1571]test-release-e2e-push-image-to-pyxis", Label("release-pipelines", "pushPyxis", "HACBS"), func() {
    33  	defer GinkgoRecover()
    34  	// Initialize the tests controllers
    35  	var fw *framework.Framework
    36  	AfterEach(framework.ReportFailure(&fw))
    37  	var err error
    38  	var devNamespace, managedNamespace, compName, additionalCompName string
    39  	var avgControllerQueryTimeout = 5 * time.Minute
    40  
    41  	var imageIDs []string
    42  	var pyxisKeyDecoded, pyxisCertDecoded []byte
    43  	var releasePR1, releasePR2 *pipeline.PipelineRun
    44  	scGitRevision := fmt.Sprintf("test-pyxis-%s", util.GenerateRandomString(4))
    45  
    46  	var component1, component2 *appservice.Component
    47  	var snapshot1, snapshot2 *appservice.Snapshot
    48  	var releaseCR1, releaseCR2 *releaseApi.Release
    49  
    50  	var componentDetected, additionalComponentDetected appservice.ComponentDetectionDescription
    51  
    52  	BeforeAll(func() {
    53  		fw, err = framework.NewFramework(utils.GetGeneratedNamespace("push-pyxis"))
    54  		Expect(err).NotTo(HaveOccurred())
    55  		devNamespace = fw.UserNamespace
    56  		managedNamespace = utils.GetGeneratedNamespace("push-pyxis-managed")
    57  
    58  		_, err = fw.AsKubeAdmin.CommonController.CreateTestNamespace(managedNamespace)
    59  		Expect(err).NotTo(HaveOccurred(), "Error when creating managedNamespace")
    60  
    61  		sourceAuthJson := utils.GetEnv("QUAY_TOKEN", "")
    62  		Expect(sourceAuthJson).ToNot(BeEmpty())
    63  
    64  		keyPyxisStage := os.Getenv(constants.PYXIS_STAGE_KEY_ENV)
    65  		Expect(keyPyxisStage).ToNot(BeEmpty())
    66  
    67  		certPyxisStage := os.Getenv(constants.PYXIS_STAGE_CERT_ENV)
    68  		Expect(certPyxisStage).ToNot(BeEmpty())
    69  
    70  		// Create secret for the release registry repo "hacbs-release-tests".
    71  		_, err = fw.AsKubeAdmin.CommonController.CreateRegistryAuthSecret(releasecommon.RedhatAppstudioUserSecret, managedNamespace, sourceAuthJson)
    72  		Expect(err).ToNot(HaveOccurred())
    73  
    74  		// Linking the build secret to the pipeline service account in dev namespace.
    75  		err = fw.AsKubeAdmin.CommonController.LinkSecretToServiceAccount(devNamespace, releasecommon.HacbsReleaseTestsTokenSecret, constants.DefaultPipelineServiceAccount, true)
    76  		Expect(err).ToNot(HaveOccurred())
    77  
    78  		publicKey, err := fw.AsKubeAdmin.TektonController.GetTektonChainsPublicKey()
    79  		Expect(err).ToNot(HaveOccurred())
    80  
    81  		// Creating k8s secret to access Pyxis stage based on base64 decoded of key and cert
    82  		pyxisKeyDecoded, err = base64.StdEncoding.DecodeString(string(keyPyxisStage))
    83  		Expect(err).ToNot(HaveOccurred())
    84  
    85  		pyxisCertDecoded, err = base64.StdEncoding.DecodeString(string(certPyxisStage))
    86  		Expect(err).ToNot(HaveOccurred())
    87  
    88  		secret := &corev1.Secret{
    89  			ObjectMeta: metav1.ObjectMeta{
    90  				Name:      "pyxis",
    91  				Namespace: managedNamespace,
    92  			},
    93  			Type: corev1.SecretTypeOpaque,
    94  			Data: map[string][]byte{
    95  				"cert": pyxisCertDecoded,
    96  				"key":  pyxisKeyDecoded,
    97  			},
    98  		}
    99  
   100  		_, err = fw.AsKubeAdmin.CommonController.CreateSecret(managedNamespace, secret)
   101  		Expect(err).ToNot(HaveOccurred())
   102  
   103  		Expect(fw.AsKubeAdmin.TektonController.CreateOrUpdateSigningSecret(
   104  			publicKey, releasecommon.PublicSecretNameAuth, managedNamespace)).To(Succeed())
   105  
   106  		defaultECP, err := fw.AsKubeAdmin.TektonController.GetEnterpriseContractPolicy("default", "enterprise-contract-service")
   107  		Expect(err).NotTo(HaveOccurred())
   108  		policy := contract.PolicySpecWithSourceConfig(defaultECP.Spec, ecp.SourceConfig{Include: []string{"@minimal"}, Exclude: []string{"cve"}})
   109  
   110  		managedServiceAccount, err := fw.AsKubeAdmin.CommonController.CreateServiceAccount(releasecommon.ReleasePipelineServiceAccountDefault, managedNamespace, releasecommon.ManagednamespaceSecret, nil)
   111  		Expect(err).NotTo(HaveOccurred())
   112  
   113  		_, err = fw.AsKubeAdmin.ReleaseController.CreateReleasePipelineRoleBindingForServiceAccount(managedNamespace, managedServiceAccount)
   114  		Expect(err).NotTo(HaveOccurred())
   115  
   116  		err = fw.AsKubeAdmin.CommonController.LinkSecretToServiceAccount(managedNamespace, releasecommon.RedhatAppstudioUserSecret, releasecommon.ReleasePipelineServiceAccountDefault, true)
   117  		Expect(err).ToNot(HaveOccurred())
   118  
   119  		// using cdq since git ref is not known
   120  		cdq, err := fw.AsKubeAdmin.HasController.CreateComponentDetectionQuery(releasecommon.ComponentName, devNamespace, releasecommon.GitSourceComponentUrl, "", "", "", false)
   121  		Expect(err).NotTo(HaveOccurred())
   122  		Expect(cdq.Status.ComponentDetected).To(HaveLen(1), "Expected length of the detected Components was not 1")
   123  
   124  		for _, compDetected := range cdq.Status.ComponentDetected {
   125  			compName = compDetected.ComponentStub.ComponentName
   126  			componentDetected = compDetected
   127  		}
   128  
   129  		// using cdq since git ref is not known
   130  		additionalCompName = releasecommon.AdditionalComponentName
   131  		cdq, err = fw.AsKubeAdmin.HasController.CreateComponentDetectionQuery(additionalCompName, devNamespace, releasecommon.AdditionalGitSourceComponentUrl, "", "", "", false)
   132  		Expect(err).NotTo(HaveOccurred())
   133  		Expect(cdq.Status.ComponentDetected).To(HaveLen(1), "Expected length of the detected Components was not 1")
   134  
   135  		for _, compDetected := range cdq.Status.ComponentDetected {
   136  			additionalCompName = compDetected.ComponentStub.ComponentName
   137  			additionalComponentDetected = compDetected
   138  		}
   139  
   140  		_, err = fw.AsKubeAdmin.ReleaseController.CreateReleasePlan(releasecommon.SourceReleasePlanName, devNamespace, releasecommon.ApplicationNameDefault, managedNamespace, "true", nil)
   141  		Expect(err).NotTo(HaveOccurred())
   142  
   143  		data, err := json.Marshal(map[string]interface{}{
   144  			"mapping": map[string]interface{}{
   145  				"components": []map[string]interface{}{
   146  					{
   147  						"name":       compName,
   148  						"repository": "quay.io/" + utils.GetQuayIOOrganization() + "/dcmetromap",
   149  					},
   150  					{
   151  						"name":       additionalCompName,
   152  						"repository": "quay.io/" + utils.GetQuayIOOrganization() + "/simplepython",
   153  					},
   154  				},
   155  			},
   156  			"pyxis": map[string]interface{}{
   157  				"server": "stage",
   158  				"secret": "pyxis",
   159  			},
   160  		})
   161  		Expect(err).NotTo(HaveOccurred())
   162  
   163  		_, err = fw.AsKubeAdmin.ReleaseController.CreateReleasePlanAdmission(releasecommon.TargetReleasePlanAdmissionName, managedNamespace, "", devNamespace, releasecommon.ReleaseStrategyPolicyDefault, releasecommon.ReleasePipelineServiceAccountDefault, []string{releasecommon.ApplicationNameDefault}, true, &tektonutils.PipelineRef{
   164  			Resolver: "git",
   165  			Params: []tektonutils.Param{
   166  				{Name: "url", Value: releasecommon.RelSvcCatalogURL},
   167  				{Name: "revision", Value: releasecommon.RelSvcCatalogRevision},
   168  				{Name: "pathInRepo", Value: "pipelines/rh-push-to-external-registry/rh-push-to-external-registry.yaml"},
   169  			},
   170  		}, &runtime.RawExtension{
   171  			Raw: data,
   172  		})
   173  		Expect(err).NotTo(HaveOccurred())
   174  
   175  		_, err = fw.AsKubeAdmin.TektonController.CreateEnterpriseContractPolicy(releasecommon.ReleaseStrategyPolicyDefault, managedNamespace, policy)
   176  		Expect(err).NotTo(HaveOccurred())
   177  
   178  		_, err = fw.AsKubeAdmin.TektonController.CreatePVCInAccessMode(releasecommon.ReleasePvcName, managedNamespace, corev1.ReadWriteOnce)
   179  		Expect(err).NotTo(HaveOccurred())
   180  
   181  		_, err = fw.AsKubeAdmin.CommonController.CreateRole("role-release-service-account", managedNamespace, map[string][]string{
   182  			"apiGroupsList": {""},
   183  			"roleResources": {"secrets"},
   184  			"roleVerbs":     {"get", "list", "watch"},
   185  		})
   186  		Expect(err).NotTo(HaveOccurred())
   187  
   188  		_, err = fw.AsKubeAdmin.CommonController.CreateRoleBinding("role-release-service-account-binding", managedNamespace, "ServiceAccount", releasecommon.ReleasePipelineServiceAccountDefault, managedNamespace, "Role", "role-release-service-account", "rbac.authorization.k8s.io")
   189  		Expect(err).NotTo(HaveOccurred())
   190  
   191  		_, err = fw.AsKubeAdmin.HasController.CreateApplication(releasecommon.ApplicationNameDefault, devNamespace)
   192  		Expect(err).NotTo(HaveOccurred())
   193  	})
   194  
   195  	AfterAll(func() {
   196  		err = fw.AsKubeAdmin.CommonController.Github.DeleteRef(constants.StrategyConfigsRepo, scGitRevision)
   197  		if err != nil {
   198  			Expect(err.Error()).To(ContainSubstring("Reference does not exist"))
   199  		}
   200  		if !CurrentSpecReport().Failed() {
   201  			Expect(fw.AsKubeAdmin.CommonController.DeleteNamespace(managedNamespace)).NotTo(HaveOccurred())
   202  			Expect(fw.SandboxController.DeleteUserSignup(fw.UserName)).To(BeTrue())
   203  		}
   204  	})
   205  
   206  	var _ = Describe("Post-release verification", func() {
   207  
   208  		It("verifies that Component 1 can be created and build PipelineRun is created for it in dev namespace and succeeds", func() {
   209  			component1, err = fw.AsKubeAdmin.HasController.CreateComponent(componentDetected.ComponentStub, devNamespace, "", "", releasecommon.ApplicationNameDefault, true, map[string]string{})
   210  			Expect(err).NotTo(HaveOccurred())
   211  			Expect(fw.AsKubeAdmin.HasController.WaitForComponentPipelineToBeFinished(component1, "",
   212  				fw.AsKubeAdmin.TektonController, &has.RetryOptions{Retries: 2, Always: true}, nil)).To(Succeed())
   213  		})
   214  
   215  		It("verifies that Component 2 can be created and build PipelineRun is created for it in dev namespace and succeeds", func() {
   216  			component2, err = fw.AsKubeAdmin.HasController.CreateComponent(additionalComponentDetected.ComponentStub, devNamespace, "", "", releasecommon.ApplicationNameDefault, true, map[string]string{})
   217  			Expect(err).NotTo(HaveOccurred())
   218  			Expect(fw.AsKubeAdmin.HasController.WaitForComponentPipelineToBeFinished(component2, "",
   219  				fw.AsKubeAdmin.TektonController, &has.RetryOptions{Retries: 2, Always: true}, nil)).To(Succeed())
   220  		})
   221  
   222  		It("tests that Snapshot is created for each Component", func() {
   223  			Eventually(func() error {
   224  				snapshot1, err = fw.AsKubeAdmin.IntegrationController.GetSnapshot("", "", component1.GetName(), devNamespace)
   225  				if err != nil {
   226  					GinkgoWriter.Printf("cannot get the Snapshot for component %s/%s: %v\n", component1.GetNamespace(), component1.GetName(), err)
   227  					return err
   228  				}
   229  				snapshot2, err = fw.AsKubeAdmin.IntegrationController.GetSnapshot("", "", component2.GetName(), devNamespace)
   230  				if err != nil {
   231  					GinkgoWriter.Printf("cannot get the Snapshot for component %s/%s: %v\n", component2.GetNamespace(), component2.GetName(), err)
   232  					return err
   233  				}
   234  				return nil
   235  			}, 5*time.Minute, releasecommon.DefaultInterval).Should(Succeed(), "timed out waiting for Snapshots to be created in %s namespace", devNamespace)
   236  		})
   237  
   238  		It("tests that associated Release CR is created for each Component's Snapshot", func() {
   239  			Eventually(func() error {
   240  				releaseCR1, err = fw.AsKubeAdmin.ReleaseController.GetRelease("", snapshot1.GetName(), devNamespace)
   241  				if err != nil {
   242  					GinkgoWriter.Printf("cannot get the Release CR for snapshot %s/%s: %v\n", snapshot1.GetNamespace(), component1.GetName(), err)
   243  					return err
   244  				}
   245  				releaseCR2, err = fw.AsKubeAdmin.ReleaseController.GetRelease("", snapshot2.GetName(), devNamespace)
   246  				if err != nil {
   247  					GinkgoWriter.Printf("cannot get the Release CR for snapshot %s/%s: %v\n", snapshot2.GetNamespace(), component1.GetName(), err)
   248  					return err
   249  				}
   250  				return nil
   251  			}, releasecommon.ReleaseCreationTimeout, releasecommon.DefaultInterval).Should(Succeed(), "timed out waiting for Release CRs to be created in %s namespace", devNamespace)
   252  		})
   253  
   254  		It("verifies that Release PipelineRun is triggered for each Release CR", func() {
   255  			Eventually(func() error {
   256  				releasePR1, err = fw.AsKubeAdmin.ReleaseController.GetPipelineRunInNamespace(managedNamespace, releaseCR1.GetName(), releaseCR1.GetNamespace())
   257  				if err != nil {
   258  					GinkgoWriter.Printf("release pipelineRun for Release %s/%s not created yet: %+v\n", releaseCR1.GetNamespace(), releaseCR1.GetName(), err)
   259  					return err
   260  				}
   261  				releasePR2, err = fw.AsKubeAdmin.ReleaseController.GetPipelineRunInNamespace(managedNamespace, releaseCR2.GetName(), releaseCR2.GetNamespace())
   262  				if err != nil {
   263  					GinkgoWriter.Printf("release pipelineRun for Release %s/%s not created yet: %+v\n", releaseCR2.GetNamespace(), releaseCR2.GetName(), err)
   264  					return err
   265  				}
   266  				var errMsg string
   267  				for _, pr := range []*pipeline.PipelineRun{releasePR1, releasePR2} {
   268  					Expect(tekton.HasPipelineRunFailed(pr)).ToNot(BeTrue(), fmt.Sprintf("Release PipelineRun %s/%s failed", pr.GetNamespace(), pr.GetName()))
   269  					if !pr.HasStarted() {
   270  						errMsg += fmt.Sprintf("Release PipelineRun %s/%s did not started yet\n", pr.GetNamespace(), pr.GetName())
   271  					}
   272  				}
   273  				if len(errMsg) > 1 {
   274  					return fmt.Errorf(errMsg)
   275  				}
   276  				return nil
   277  			}, releasecommon.ReleasePipelineRunCreationTimeout, constants.PipelineRunPollingInterval).Should(Succeed(), fmt.Sprintf("timed out waiting for a PipelineRun to start for each Release CR in %s namespace", managedNamespace))
   278  		})
   279  
   280  		It("verifies a release PipelineRun for each component succeeded in managed namespace", func() {
   281  			Eventually(func() error {
   282  				var errMsg string
   283  				for _, pr := range []*pipeline.PipelineRun{releasePR1, releasePR2} {
   284  					pr, err = fw.AsKubeAdmin.TektonController.GetPipelineRun(pr.GetName(), pr.GetNamespace())
   285  					if err != nil {
   286  						return err
   287  					}
   288  					Expect(tekton.HasPipelineRunFailed(pr)).ToNot(BeTrue(), fmt.Sprintf("Release PipelineRun %s/%s failed", pr.GetNamespace(), pr.GetName()))
   289  					if pr.IsDone() {
   290  						Expect(tekton.HasPipelineRunSucceeded(pr)).To(BeTrue(), fmt.Sprintf("Release PipelineRun %s/%s did not succceed", pr.GetNamespace(), pr.GetName()))
   291  					} else {
   292  						errMsg += fmt.Sprintf("Release PipelineRun %s/%s did not finish yet\n", pr.GetNamespace(), pr.GetName())
   293  					}
   294  				}
   295  				if len(errMsg) > 1 {
   296  					return fmt.Errorf(errMsg)
   297  				}
   298  				return nil
   299  			}, releasecommon.ReleasePipelineRunCompletionTimeout, constants.PipelineRunPollingInterval).Should(Succeed())
   300  		})
   301  
   302  		It("validate the result of task create-pyxis-image contains image ids", func() {
   303  			Eventually(func() []string {
   304  				releasePR1, err = fw.AsKubeAdmin.TektonController.GetPipelineRun(releasePR1.GetName(), releasePR1.GetNamespace())
   305  				Expect(err).NotTo(HaveOccurred())
   306  				releasePR2, err = fw.AsKubeAdmin.TektonController.GetPipelineRun(releasePR2.GetName(), releasePR2.GetNamespace())
   307  				Expect(err).NotTo(HaveOccurred())
   308  
   309  				trReleaseLogs, err := fw.AsKubeAdmin.TektonController.GetTaskRunLogs(releasePR1.GetName(), "create-pyxis-image", releasePR1.GetNamespace())
   310  				Expect(err).NotTo(HaveOccurred())
   311  
   312  				trAdditionalReleaseLogs, err := fw.AsKubeAdmin.TektonController.GetTaskRunLogs(releasePR2.GetName(), "create-pyxis-image", releasePR2.GetNamespace())
   313  				Expect(err).NotTo(HaveOccurred())
   314  
   315  				trReleaseImageIDs, err := fw.AsKubeAdmin.ReleaseController.GetPyxisImageIDsFromCreatePyxisImageTaskLogs(trReleaseLogs)
   316  				Expect(err).NotTo(HaveOccurred())
   317  				trAdditionalReleaseIDs, err := fw.AsKubeAdmin.ReleaseController.GetPyxisImageIDsFromCreatePyxisImageTaskLogs(trAdditionalReleaseLogs)
   318  				Expect(err).NotTo(HaveOccurred())
   319  
   320  				Expect(trReleaseImageIDs).NotTo(BeEmpty(), fmt.Sprintf("Invalid ImageID in results of task create-pyxis-image. TaskRun log: %+s", trReleaseLogs))
   321  				Expect(trAdditionalReleaseIDs).ToNot(BeEmpty(), fmt.Sprintf("Invalid ImageID in results of task create-pyxis-image. TaskRun log: %+s", trAdditionalReleaseLogs))
   322  
   323  				Expect(trReleaseImageIDs).ToNot(HaveLen(len(trAdditionalReleaseIDs)), "the number of image IDs should not be the same in both taskrun results. (%+v vs. %+v)", trReleaseImageIDs, trAdditionalReleaseIDs)
   324  
   325  				if len(trReleaseImageIDs) > len(trAdditionalReleaseIDs) {
   326  					imageIDs = trReleaseImageIDs
   327  				} else {
   328  					imageIDs = trAdditionalReleaseIDs
   329  				}
   330  
   331  				return imageIDs
   332  			}, avgControllerQueryTimeout, releasecommon.DefaultInterval).Should(HaveLen(2))
   333  		})
   334  
   335  		It("tests that associated Release CR has completed for each Component's Snapshot", func() {
   336  			Eventually(func() error {
   337  				var errMsg string
   338  				for _, cr := range []*releaseApi.Release{releaseCR1, releaseCR2} {
   339  					cr, err = fw.AsKubeAdmin.ReleaseController.GetRelease("", cr.Spec.Snapshot, devNamespace)
   340  					Expect(err).ShouldNot(HaveOccurred())
   341  					if !cr.IsReleased() {
   342  						errMsg += fmt.Sprintf("release %s/%s is not marked as finished yet", cr.GetNamespace(), cr.GetName())
   343  					}
   344  				}
   345  				if len(errMsg) > 1 {
   346  					return fmt.Errorf(errMsg)
   347  				}
   348  				return nil
   349  			}, releasecommon.ReleaseCreationTimeout, releasecommon.DefaultInterval).Should(Succeed(), "timed out waiting for Release CRs to be created in %s namespace", devNamespace)
   350  		})
   351  
   352  		It("validates that imageIds from task create-pyxis-image exist in Pyxis.", func() {
   353  			for _, imageID := range imageIDs {
   354  				Eventually(func() error {
   355  					body, err := fw.AsKubeAdmin.ReleaseController.GetPyxisImageByImageID(releasecommon.PyxisStageImagesApiEndpoint, imageID,
   356  						[]byte(pyxisCertDecoded), []byte(pyxisKeyDecoded))
   357  					Expect(err).NotTo(HaveOccurred(), "failed to get response body")
   358  
   359  					sbomImage := release.Image{}
   360  					Expect(json.Unmarshal(body, &sbomImage)).To(Succeed(), "failed to unmarshal body content: %s", string(body))
   361  
   362  					return nil
   363  				}, releasecommon.ReleaseCreationTimeout, releasecommon.DefaultInterval).Should(Succeed())
   364  			}
   365  		})
   366  	})
   367  })