From 7707c34a6d1d2dddff408e50a5f1ee976e67c94f Mon Sep 17 00:00:00 2001 From: Jing Qi Date: Thu, 7 Mar 2024 18:47:40 +0800 Subject: [PATCH] feat(RHTAPREL-849): add e2e test for release_to_github --- magefiles/magefile.go | 2 +- pkg/clients/github/repositories.go | 30 +++ tests/release/const.go | 8 + tests/release/pipelines/fbc_release.go | 8 +- tests/release/pipelines/release_to_github.go | 246 +++++++++++++++++++ 5 files changed, 287 insertions(+), 7 deletions(-) create mode 100644 tests/release/pipelines/release_to_github.go diff --git a/magefiles/magefile.go b/magefiles/magefile.go index 974fb92fb8..7699431001 100644 --- a/magefiles/magefile.go +++ b/magefiles/magefile.go @@ -308,7 +308,7 @@ func (ci CI) TestE2E() error { } func RunE2ETests() error { - labelFilter := utils.GetEnv("E2E_TEST_SUITE_LABEL", "!upgrade-create && !upgrade-verify && !upgrade-cleanup && !release-pipelines") + labelFilter := utils.GetEnv("E2E_TEST_SUITE_LABEL", "!upgrade-create && !upgrade-verify && !upgrade-cleanup") return runTests(labelFilter, "e2e-report.xml") } diff --git a/pkg/clients/github/repositories.go b/pkg/clients/github/repositories.go index a7a6462700..a4689cee55 100644 --- a/pkg/clients/github/repositories.go +++ b/pkg/clients/github/repositories.go @@ -3,11 +3,41 @@ package github import ( "context" "fmt" + "strings" "github.com/google/go-github/v44/github" . "github.com/onsi/ginkgo/v2" ) +func (g *Github) CheckIfReleaseExist(owner, repositoryName, releaseURL string) bool { + urlParts := strings.Split(releaseURL, "/") + tagName := urlParts[len(urlParts)-1] + _, _, err := g.client.Repositories.GetReleaseByTag(context.Background(), owner, repositoryName, tagName) + if err != nil { + GinkgoWriter.Printf("GetReleaseByTag %s returned error in repo %s : %v\n", tagName, repositoryName, err) + return false + } + GinkgoWriter.Printf("Release tag %s is found in repository %s \n", tagName, repositoryName) + return true +} + +func (g *Github) DeleteRelease(owner, repositoryName, releaseURL string) bool { + urlParts := strings.Split(releaseURL, "/") + tagName := urlParts[len(urlParts)-1] + release, _, err := g.client.Repositories.GetReleaseByTag(context.Background(), owner, repositoryName, tagName) + if err != nil { + GinkgoWriter.Printf("GetReleaseByTag returned error in repo %s : %v\n", repositoryName, err) + return false + } + + _, err = g.client.Repositories.DeleteRelease(context.Background(), owner, repositoryName, *release.ID) + if err != nil { + GinkgoWriter.Printf("DeleteRelease returned error: %v", err) + } + GinkgoWriter.Printf("Release tag %s is deleted in repository %s \n", tagName, repositoryName) + return true +} + func (g *Github) CheckIfRepositoryExist(repository string) bool { _, resp, err := g.client.Repositories.Get(context.Background(), g.organization, repository) if err != nil { diff --git a/tests/release/const.go b/tests/release/const.go index 7ca5656c3b..0408755ead 100644 --- a/tests/release/const.go +++ b/tests/release/const.go @@ -25,6 +25,8 @@ const ( ReleaseCreationTimeout = 5 * time.Minute ReleasePipelineRunCreationTimeout = 10 * time.Minute ReleasePipelineRunCompletionTimeout = 60 * time.Minute + BuildPipelineRunCompletionTimeout = 60 * time.Minute + BuildPipelineRunCreationTimeout = 10 * time.Minute ReleasePlanStatusUpdateTimeout = 1 * time.Minute DefaultInterval = 100 * time.Millisecond @@ -37,6 +39,12 @@ const ( AdditionalReleasedImagePushRepo string = "quay.io/redhat-appstudio-qe/simplepython" PyxisStageImagesApiEndpoint string = "https://pyxis.preprod.api.redhat.com/v1/images/id/" + // EC constants + EcPolicyLibPath = "github.com/enterprise-contract/ec-policies//policy/lib" + EcPolicyReleasePath = "github.com/enterprise-contract/ec-policies//policy/release" + EcPolicyDataBundle = "oci::quay.io/redhat-appstudio-tekton-catalog/data-acceptable-bundles:latest" + EcPolicyDataPath = "github.com/release-engineering/rhtap-ec-policy//data" + // Service constants ApplicationName string = "application" ) diff --git a/tests/release/pipelines/fbc_release.go b/tests/release/pipelines/fbc_release.go index 471d1e1918..f28629e6fa 100644 --- a/tests/release/pipelines/fbc_release.go +++ b/tests/release/pipelines/fbc_release.go @@ -26,10 +26,6 @@ const ( fbcSourceGitURL = "https://github.com/redhat-appstudio-qe/fbc-sample-repo" targetPort = 50051 relSvcCatalogPathInRepo = "pipelines/fbc-release/fbc-release.yaml" - ecPolicyLibPath = "github.com/enterprise-contract/ec-policies//policy/lib" - ecPolicyReleasePath = "github.com/enterprise-contract/ec-policies//policy/release" - ecPolicyDataBundle = "oci::quay.io/redhat-appstudio-tekton-catalog/data-acceptable-bundles:latest" - ecPolicyDataPath = "github.com/release-engineering/rhtap-ec-policy//data" ) var _ = framework.ReleasePipelinesSuiteDescribe("FBC e2e-tests", Label("release-pipelines", "fbc-tests"), func() { @@ -294,8 +290,8 @@ func createFBCEnterpriseContractPolicy(fbcECPName string, managedFw framework.Fr PublicKey: "k8s://openshift-pipelines/public-key", Sources: []ecp.Source{{ Name: "Default", - Policy: []string{ecPolicyLibPath, ecPolicyReleasePath}, - Data: []string{ecPolicyDataBundle, ecPolicyDataPath}, + Policy: []string{releasecommon.EcPolicyLibPath, releasecommon.EcPolicyReleasePath}, + Data: []string{releasecommon.EcPolicyDataBundle, releasecommon.EcPolicyDataPath}, }}, Configuration: &ecp.EnterpriseContractPolicyConfiguration{ Exclude: []string{"cve", "step_image_registries", "tasks.required_tasks_found:prefetch-dependencies"}, diff --git a/tests/release/pipelines/release_to_github.go b/tests/release/pipelines/release_to_github.go new file mode 100644 index 0000000000..2b3a35cd6b --- /dev/null +++ b/tests/release/pipelines/release_to_github.go @@ -0,0 +1,246 @@ +package pipelines + +import ( + "encoding/json" + "fmt" + "os" + "time" + + "github.com/devfile/library/v2/pkg/util" + ecp "github.com/enterprise-contract/enterprise-contract-controller/api/v1alpha1" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + appservice "github.com/redhat-appstudio/application-api/api/v1alpha1" + "github.com/redhat-appstudio/e2e-tests/pkg/clients/github" + //"github.com/redhat-appstudio/e2e-tests/pkg/clients/has" + "github.com/redhat-appstudio/e2e-tests/pkg/constants" + "github.com/redhat-appstudio/e2e-tests/pkg/framework" + "github.com/redhat-appstudio/e2e-tests/pkg/utils" + "github.com/redhat-appstudio/e2e-tests/pkg/utils/tekton" + tektonv1"github.com/tektoncd/pipeline/pkg/apis/pipeline/v1" + releaseapi "github.com/redhat-appstudio/release-service/api/v1alpha1" + releasecommon "github.com/redhat-appstudio/e2e-tests/tests/release" + tektonutils "github.com/redhat-appstudio/release-service/tekton/utils" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + rhcsServiceAccountName = "release-service-account" + rhcsSourceGitURL = "https://github.com/redhat-appstudio-qe/terraform-provider-rhcs" + rhcsReleaseURL = "https://github.com/redhat-appstudio-qe/terraform-provider-rhcs/releases/tag/v2.1" + rhcsRepoOwner = "redhat-appstudio-qe" + rhcsCatalogPathInRepo = "pipelines/release-to-github/release-to-github.yaml" +) + +var _ = framework.ReleasePipelinesSuiteDescribe("e2e tests for release-to-github", Label("release-pipelines", "release-to-github"), func() { + defer GinkgoRecover() + + var devWorkspace = utils.GetEnv(constants.RELEASE_DEV_WORKSPACE_ENV, constants.DevReleaseTeam) + var managedWorkspace = utils.GetEnv(constants.RELEASE_MANAGED_WORKSPACE_ENV, constants.ManagedReleaseTeam) + + var devNamespace = devWorkspace + "-tenant" + var managedNamespace = managedWorkspace + "-tenant" + + var err error + var devFw *framework.Framework + var managedFw *framework.Framework + var rhcsApplicationName = "rhcs-app-" + util.GenerateRandomString(4) + var rhcsComponentName = "rhcs-comp-" + util.GenerateRandomString(4) + var rhcsReleasePlanName = "rhcs-rp-" + util.GenerateRandomString(4) + var rhcsReleasePlanAdmissionName = "rhcs-rpa-" + util.GenerateRandomString(4) + var rhcsEnterpriseContractPolicyName = "rhcs-policy-" + util.GenerateRandomString(4) + + var snapshot *appservice.Snapshot + var releaseCR *releaseapi.Release + var releasePR, buildPR *tektonv1.PipelineRun + var gh *github.Github + + AfterEach(framework.ReportFailure(&devFw)) + + stageOptions := utils.Options{ + ToolchainApiUrl: os.Getenv(constants.TOOLCHAIN_API_URL_ENV), + KeycloakUrl: os.Getenv(constants.KEYLOAK_URL_ENV), + OfflineToken: os.Getenv(constants.OFFLINE_TOKEN_ENV), + } + + Describe("with RHCS happy path", Label("rhcsHappyPath"), func() { + var component *appservice.Component + BeforeAll(func() { + + devFw, err = framework.NewFrameworkWithTimeout( + devWorkspace, + time.Minute*60, + stageOptions, + ) + Expect(err).NotTo(HaveOccurred()) + + managedFw, err = framework.NewFrameworkWithTimeout( + managedWorkspace, + time.Minute*60, + stageOptions, + ) + Expect(err).NotTo(HaveOccurred()) + managedNamespace = managedFw.UserNamespace + + // Linking the build secret to the pipeline service account in dev namespace. + err = devFw.AsKubeAdmin.CommonController.LinkSecretToServiceAccount(devNamespace, releasecommon.HacbsReleaseTestsTokenSecret, constants.DefaultPipelineServiceAccount, true) + Expect(err).ToNot(HaveOccurred()) + + githubUser := utils.GetEnv("GITHUB_USER","") + githubToken := utils.GetEnv(constants.GITHUB_TOKEN_ENV, "") + gh, err = github.NewGithubClient(githubToken, githubUser) + Expect(githubToken).ToNot(BeEmpty()) + if gh.CheckIfReleaseExist(rhcsRepoOwner, "terraform-provider-rhcs", rhcsReleaseURL){ + // Remove the release if the release exists + gh.DeleteRelease(rhcsRepoOwner, "terraform-provider-rhcs", rhcsReleaseURL) + } + + _, err = managedFw.AsKubeAdmin.CommonController.GetSecret(managedNamespace, releasecommon.RedhatAppstudioUserSecret) + if errors.IsNotFound(err) { + githubSecret := &corev1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: releasecommon.RedhatAppstudioUserSecret, + Namespace: managedNamespace, + }, + Type: corev1.SecretTypeOpaque, + Data: map[string][]byte{ + "token": []byte(githubToken), + }, + } + + _, err = managedFw.AsKubeAdmin.CommonController.CreateSecret(managedNamespace, githubSecret) + Expect(err).ToNot(HaveOccurred()) + } + Expect(err).ToNot(HaveOccurred()) + + err = managedFw.AsKubeAdmin.CommonController.LinkSecretToServiceAccount(managedNamespace, releasecommon.RedhatAppstudioUserSecret, constants.DefaultPipelineServiceAccount, true) + Expect(err).ToNot(HaveOccurred()) + + _, err = devFw.AsKubeDeveloper.HasController.CreateApplication(rhcsApplicationName, devNamespace) + Expect(err).NotTo(HaveOccurred()) + + _, err = devFw.AsKubeDeveloper.ReleaseController.CreateReleasePlan(rhcsReleasePlanName, devNamespace, rhcsApplicationName, managedNamespace, "true") + Expect(err).NotTo(HaveOccurred()) + + createRHCSReleasePlanAdmission(rhcsReleasePlanAdmissionName, *managedFw, devNamespace, managedNamespace, rhcsApplicationName, rhcsEnterpriseContractPolicyName, rhcsCatalogPathInRepo, "false", "", "", "", "") + component = releasecommon.CreateComponentByCDQ(*devFw, devNamespace, managedNamespace, rhcsApplicationName, rhcsComponentName, rhcsSourceGitURL) + createRHCSEnterpriseContractPolicy(rhcsEnterpriseContractPolicyName, *managedFw, devNamespace, managedNamespace) + + }) + + AfterAll(func() { + Expect(devFw.AsKubeDeveloper.HasController.DeleteApplication(rhcsApplicationName, devNamespace, false)).NotTo(HaveOccurred()) + Expect(managedFw.AsKubeDeveloper.TektonController.DeleteEnterpriseContractPolicy(rhcsEnterpriseContractPolicyName, managedNamespace, false)).NotTo(HaveOccurred()) + Expect(managedFw.AsKubeDeveloper.ReleaseController.DeleteReleasePlanAdmission(rhcsReleasePlanAdmissionName, managedNamespace, false)).NotTo(HaveOccurred()) + }) + + var _ = Describe("Post-release verification", func() { + It("verifies that a build PipelineRun is created in dev namespace and succeeds", func() { + Eventually(func() error { + buildPR, err = devFw.AsKubeDeveloper.HasController.GetComponentPipelineRun(component.Name, rhcsApplicationName, devNamespace, "") + if err != nil { + return err + } + if !buildPR.IsDone() { + return fmt.Errorf("build pipelinerun %s in namespace %s did not finish yet", buildPR.Name, buildPR.Namespace) + } + Expect(tekton.HasPipelineRunSucceeded(buildPR)).To(BeTrue(), fmt.Sprintf("build pipelinerun %s/%s did not succeed", buildPR.GetNamespace(), buildPR.GetName())) + snapshot, err = devFw.AsKubeDeveloper.IntegrationController.GetSnapshot("", buildPR.Name, "", devNamespace) + Expect(err).ShouldNot(HaveOccurred()) + return nil + }, releasecommon.BuildPipelineRunCompletionTimeout, releasecommon.DefaultInterval).Should(Succeed(), "timed out when waiting for build pipelinerun to be created") + //Expect(devFw.AsKubeDeveloper.HasController.WaitForComponentPipelineToBeFinished(component, "", devFw.AsKubeDeveloper.TektonController, &has.RetryOptions{Retries: 3, Always: true})).To(Succeed()) + }) + It("verifies the rhcs release pipelinerun is running and succeeds", func() { + releaseCR, err = devFw.AsKubeDeveloper.ReleaseController.GetRelease("", snapshot.Name, devNamespace) + Expect(err).ShouldNot(HaveOccurred()) + Eventually(func() error { + releasePR, err = managedFw.AsKubeAdmin.ReleaseController.GetPipelineRunInNamespace(managedFw.UserNamespace, releaseCR.GetName(), releaseCR.GetNamespace()) + if err != nil { + return err + } + Expect(err).ShouldNot(HaveOccurred()) + + if !releasePR.IsDone() { + return fmt.Errorf("release pipelinerun %s in namespace %s did not finish yet", releasePR.Name, releasePR.Namespace) + } + GinkgoWriter.Println("Release PR: ", releasePR.Name) + Expect(tekton.HasPipelineRunSucceeded(releasePR)).To(BeTrue(), fmt.Sprintf("release pipelinerun %s/%s did not succeed", releasePR.GetNamespace(), releasePR.GetName())) + return nil + }, releasecommon.ReleasePipelineRunCompletionTimeout, releasecommon.DefaultInterval).Should(Succeed(), "timed out when waiting for release pipelinerun to succeed") + }) + + It("verifies release CR completed and set succeeded.", func() { + Eventually(func() error { + releaseCR, err = devFw.AsKubeDeveloper.ReleaseController.GetRelease("", snapshot.Name, devNamespace) + if err != nil { + return err + } + GinkgoWriter.Println("Release CR: ", releaseCR.Name) + if !releaseCR.IsReleased() { + return fmt.Errorf("release %s/%s is not marked as finished yet", releaseCR.GetNamespace(), releaseCR.GetName()) + } + return nil + }, releasecommon.ReleaseCreationTimeout, releasecommon.DefaultInterval).Should(Succeed()) + }) + + It("verifies if the Release exists in github repo", func() { + trReleasePr, err := managedFw.AsKubeAdmin.TektonController.GetTaskRunStatus(managedFw.AsKubeAdmin.CommonController.KubeRest(), releasePR, "create-github-release") + Expect(err).NotTo(HaveOccurred()) + trReleaseURL := trReleasePr.Status.TaskRunStatusFields.Results[0].Value.StringVal + Expect(trReleaseURL).To(Equal(rhcsReleaseURL)) + Expect(gh.CheckIfReleaseExist(rhcsRepoOwner, "terraform-provider-rhcs", trReleaseURL)).To(BeTrue(), fmt.Sprintf("release %s doesn't exist", trReleaseURL)) + }) + }) + }) +}) + + +func createRHCSEnterpriseContractPolicy(rhcsECPName string, managedFw framework.Framework, devNamespace, managedNamespace string) { + defaultEcPolicySpec := ecp.EnterpriseContractPolicySpec{ + Description: "Red Hat's enterprise requirements", + PublicKey: "k8s://openshift-pipelines/public-key", + Sources: []ecp.Source{{ + Name: "Default", + Policy: []string{releasecommon.EcPolicyLibPath, releasecommon.EcPolicyReleasePath}, + Data: []string{releasecommon.EcPolicyDataBundle, releasecommon.EcPolicyDataPath}, + }}, + Configuration: &ecp.EnterpriseContractPolicyConfiguration{ + Exclude: []string{"cve", "step_image_registries", "tasks.required_tasks_found:prefetch-dependencies"}, + Include: []string{"minimal"}, + }, + } + + _, err := managedFw.AsKubeDeveloper.TektonController.CreateEnterpriseContractPolicy(rhcsECPName, managedNamespace, defaultEcPolicySpec) + Expect(err).NotTo(HaveOccurred()) + +} + +func createRHCSReleasePlanAdmission(rhcsRPAName string, managedFw framework.Framework, devNamespace, managedNamespace, rhcsAppName, rhcsECPName, pathInRepoValue, hotfix, issueId, preGA, productName, productVersion string) { + var err error + + data, err := json.Marshal(map[string]interface{}{ + "github": map[string]interface{}{ + "githubSecret": releasecommon.RedhatAppstudioUserSecret, + }, + "sign": map[string]interface{}{ + "configMapName": "hacbs-signing-pipeline-config-redhatbeta2", + }, + }) + Expect(err).NotTo(HaveOccurred()) + + _, err = managedFw.AsKubeAdmin.ReleaseController.CreateReleasePlanAdmission(rhcsRPAName, managedNamespace, "", devNamespace, rhcsECPName, rhcsServiceAccountName, []string{rhcsAppName}, true, &tektonutils.PipelineRef{ + Resolver: "git", + Params: []tektonutils.Param{ + {Name: "url", Value: releasecommon.RelSvcCatalogURL}, + {Name: "revision", Value: releasecommon.RelSvcCatalogRevision}, + {Name: "pathInRepo", Value: pathInRepoValue}, + }, + }, &runtime.RawExtension{ + Raw: data, + }) + Expect(err).NotTo(HaveOccurred()) +}