From f3fce195ccd6b737d2f16abe43fbec6f860d407d Mon Sep 17 00:00:00 2001 From: jubittajohn Date: Tue, 8 Aug 2023 23:22:30 -0400 Subject: [PATCH] Updated to generate plain manifests from an SDK project with kustomize;Restructed code to have a single It block in a describe block Signed-off-by: jubittajohn --- Makefile | 10 +- go.mod | 14 +- test/operator-framework-e2e/README.md | 13 + .../create_fbc_helper.go | 4 +- .../generate_dockerfile.go | 20 +- .../operator_framework_test.go | 618 ++++++++++-------- test/operator-framework-e2e/read_manifests.go | 46 +- .../bundles/plain-v0/plain.v0.1.0/Dockerfile | 2 +- .../plain.v0.1.0/manifests/configmap.yaml | 2 +- .../bundles/plain-v0/plain.v0.1.1/Dockerfile | 2 - .../plain.v0.1.1/manifests/configmap.yaml | 19 - 11 files changed, 417 insertions(+), 333 deletions(-) delete mode 100644 testdata/bundles/plain-v0/plain.v0.1.1/Dockerfile delete mode 100644 testdata/bundles/plain-v0/plain.v0.1.1/manifests/configmap.yaml diff --git a/Makefile b/Makefile index 6f31a8892..467e7ca21 100644 --- a/Makefile +++ b/Makefile @@ -117,7 +117,7 @@ e2e: run kind-load-test-artifacts test-e2e e2e-coverage kind-cluster-cleanup ## .PHONY: operator-developer-e2e operator-developer-e2e: KIND_CLUSTER_NAME=operator-controller-op-dev-e2e ## Run operator-developer e2e on local kind cluster -operator-developer-e2e: run setup-op-dev-e2e deploy-local-registry test-op-dev-e2e stop-local-registry remove-local-registry kind-cluster-cleanup +operator-developer-e2e: run $(OPM) $(OPERATOR_SDK) $(KUSTOMIZE) deploy-local-registry test-op-dev-e2e stop-local-registry remove-local-registry kind-cluster-cleanup .PHONY: e2e-coverage e2e-coverage: @@ -151,7 +151,7 @@ kind-load-test-artifacts: $(KIND) ## Load the e2e testdata container images into .PHONY: deploy-local-registry deploy-local-registry: ## Deploy local docker registry - $(CONTAINER_RUNTIME) run -d -p 5000:5000 --restart=always --name local-registry registry:2 + $(CONTAINER_RUNTIME) run -d -p 5001:5000 --restart=always --name local-registry registry:2 .PHONY: stop-local-registry stop-local-registry: ## Stop local registry @@ -161,15 +161,15 @@ stop-local-registry: ## Stop local registry remove-local-registry: ## Remove local registry $(CONTAINER_RUNTIME) container rm -v local-registry -.PHONY: setup-op-dev-e2e -setup-op-dev-e2e: $(OPM) $(OPERATOR_SDK) - opm: $(OPM) $(OPM) $(OPM_ARGS) operator-sdk: $(OPERATOR_SDK) (cd $(OPERATOR_SDK_PROJECT_PATH) && $(OPERATOR_SDK) $(OPERATOR_SDK_ARGS)) +kustomize: $(KUSTOMIZE) + (cd $(OPERATOR_SDK_PROJECT_PATH) && $(KUSTOMIZE) $(KUSTOMIZE_ARGS)) + ##@ Build export VERSION ?= $(shell git describe --tags --always --dirty) diff --git a/go.mod b/go.mod index d406ae0f3..8cc09d0f3 100644 --- a/go.mod +++ b/go.mod @@ -5,21 +5,22 @@ go 1.20 require ( github.com/blang/semver/v4 v4.0.0 github.com/go-logr/logr v1.2.4 - github.com/onsi/ginkgo/v2 v2.11.0 - github.com/onsi/gomega v1.27.10 + github.com/onsi/ginkgo/v2 v2.9.7 + github.com/onsi/gomega v1.27.7 + github.com/operator-framework/api v0.17.4-0.20230223191600-0131a6301e42 github.com/operator-framework/catalogd v0.4.1 github.com/operator-framework/deppy v0.0.0-20230629133131-bb7b6ae7b266 github.com/operator-framework/operator-registry v1.27.1 github.com/operator-framework/rukpak v0.13.0 github.com/spf13/pflag v1.0.5 - github.com/stretchr/testify v1.8.4 - go.uber.org/zap v1.25.0 + github.com/stretchr/testify v1.8.1 + go.uber.org/zap v1.24.0 + k8s.io/apiextensions-apiserver v0.26.1 k8s.io/apimachinery v0.26.1 k8s.io/client-go v0.26.1 k8s.io/component-base v0.26.1 k8s.io/utils v0.0.0-20221128185143-99ec85e7a448 sigs.k8s.io/controller-runtime v0.14.4 - sigs.k8s.io/yaml v1.3.0 ) require ( @@ -96,7 +97,6 @@ require ( github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc2 // indirect - github.com/operator-framework/api v0.17.4-0.20230223191600-0131a6301e42 // indirect github.com/otiai10/copy v1.2.0 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect @@ -139,7 +139,6 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/api v0.26.1 // indirect - k8s.io/apiextensions-apiserver v0.26.1 // indirect k8s.io/apiserver v0.26.1 // indirect k8s.io/klog/v2 v2.80.1 // indirect k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect @@ -147,4 +146,5 @@ require ( sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.35 // indirect sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/test/operator-framework-e2e/README.md b/test/operator-framework-e2e/README.md index 90aff4e80..de7d9cf0d 100644 --- a/test/operator-framework-e2e/README.md +++ b/test/operator-framework-e2e/README.md @@ -6,6 +6,19 @@ This is a cross-component demo with all OLM v1 repositories. The ginkgo test doe - Uses operator-sdk to build `registry+v1` bundles and create catalogs to include the bundles. - Installs, upgrades and deletes a `registry+v1` operator. +1. start with an empty directory +2. call operator-sdk to initialize and generate an operator +3. generate a bundle directory +4. build/push/kind load bundle images from the bundle directories +5. repeat steps 2-4 as necessary to get bundles for multiple operator versions +6. generate a catalog directory +7. build/push/kind load the catalog +8. create a Catalog CR (with kubectl operator) +9. create an Operator CR (with kubectl operator) +10. trigger Operator upgrades (with kubectl operator) +11. delete the Operator CR (with kubectl operator) +12. delete the Catalog CR (with kubectl operator) +13. repeat steps 2-12 for each bundle format (e.g. registry+v1 and plain+v0) ## Objective - Development on OLM v1 is split across multiple repositories, and the list of relevant repositories may grow over time. While we have demos showing improvements in functionality of components over time, it can be difficult to have a picture of the state of OLM v1 at any given time for someone not following its development closely. Having a single source to look for OLM v1 behavior can provide more clarity about the state of the project. - With the scale of the OLM v1 project, it is useful to have a means to test components in the operator development + lifecycle pipeline together to create a more cohesive experience for all users. diff --git a/test/operator-framework-e2e/create_fbc_helper.go b/test/operator-framework-e2e/create_fbc_helper.go index 25a6e3860..9383535e4 100644 --- a/test/operator-framework-e2e/create_fbc_helper.go +++ b/test/operator-framework-e2e/create_fbc_helper.go @@ -125,8 +125,8 @@ func WriteFBC(fbc declcfg.DeclarativeConfig, fbcFilePath, fbcFileName string) er return err } -// Generates the semver using the bundle images passed -func generateOLMSemverFile(semverFileName string, bundleImages []string) error { +// Forms the semver using the bundle images passed +func formOLMSemverFile(semverFileName string, bundleImages []string) error { images := make([]string, 0, len(bundleImages)) for _, bundleImage := range bundleImages { images = append(images, fmt.Sprintf(" - image: %s", bundleImage)) diff --git a/test/operator-framework-e2e/generate_dockerfile.go b/test/operator-framework-e2e/generate_dockerfile.go index 86eb4795a..b58a35e1d 100644 --- a/test/operator-framework-e2e/generate_dockerfile.go +++ b/test/operator-framework-e2e/generate_dockerfile.go @@ -2,18 +2,16 @@ package operatore2e import ( "os" - "path/filepath" "text/template" ) -// generates Dockerfile and its contents for a given set of yaml files -func generateDockerFile(dockerFilePath, yamlFolderName, dockerFileName string) error { +// GenerateDockerFile generates Dockerfile and its contents for a given set of yaml files +func generateDockerFile(dockerFilePath, yamlFolderName, dockerfileTmpl string) error { t, err := template.New("dockerfile").Parse(dockerfileTmpl) if err != nil { panic(err) } - dockerFilePath = filepath.Join(dockerFilePath, dockerFileName) file, err := os.Create(dockerFilePath) if err != nil { return err @@ -28,4 +26,16 @@ func generateDockerFile(dockerFilePath, yamlFolderName, dockerFileName string) e return err } -const dockerfileTmpl = "ADD {{.YamlDir}} /configs/{{.YamlDir}}\n" +// GenerateCatalogDockerFile generates Dockerfile for the catalog +func generateCatalogDockerFile(dockerFilePath, yamlFolderName string) error { + return generateDockerFile(dockerFilePath, yamlFolderName, catalogDockerfileTmpl) +} + +// GenerateBundleDockerFile generates Dockerfile for the bundle +func generateBundleDockerFile(dockerFilePath, yamlFolderName string) error { + return generateDockerFile(dockerFilePath, yamlFolderName, bundleDockerfileTmpl) +} + +// Dockerfile templates +const catalogDockerfileTmpl = "ADD {{.YamlDir}} /configs/{{.YamlDir}}\n" +const bundleDockerfileTmpl = "ADD manifests /manifests\n" diff --git a/test/operator-framework-e2e/operator_framework_test.go b/test/operator-framework-e2e/operator_framework_test.go index 229c87bd1..5a8bd59ed 100644 --- a/test/operator-framework-e2e/operator_framework_test.go +++ b/test/operator-framework-e2e/operator_framework_test.go @@ -21,7 +21,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" @@ -73,7 +72,7 @@ type SdkProjectInfo struct { } const ( - remoteRegistryRepo = "localhost:5000/" + remoteRegistryRepo = "localhost:5001/" kindServer = "operator-controller-op-dev-e2e" deployedNameSpace = "rukpak-system" operatorControllerHome = "../.." @@ -109,6 +108,7 @@ var _ = BeforeSuite(func() { var _ = Describe("Operator Framework E2E for plain bundles", func() { var ( + sdkInfo *SdkProjectInfo bundleInfo *BundleInfo catalogDInfo *CatalogDInfo operatorAction *OperatorActionInfo @@ -117,15 +117,20 @@ var _ = Describe("Operator Framework E2E for plain bundles", func() { err error ) BeforeEach(func() { + sdkInfo = &SdkProjectInfo{ + projectName: "plain-example", + domainName: "plain.com", + group: "cache", + version: "v1alpha1", + kind: "Memcached1", + } bundleInfo = &BundleInfo{ baseFolderPath: "../../testdata/bundles/plain-v0", bundles: []BundleContent{ { - bInputDir: "plain.v0.1.0", bundleVersion: "0.1.0", }, { - bInputDir: "plain.v0.1.1", bundleVersion: "0.1.1", }, }, @@ -133,7 +138,7 @@ var _ = Describe("Operator Framework E2E for plain bundles", func() { catalogDInfo = &CatalogDInfo{ baseFolderPath: "../../testdata/catalogs", fbcFileName: "catalog.yaml", - operatorName: "plain", + operatorName: "plain-operator", desiredChannelName: "beta", } operatorAction = &OperatorActionInfo{ @@ -141,102 +146,102 @@ var _ = Describe("Operator Framework E2E for plain bundles", func() { upgradeVersion: "0.1.1", } for i, b := range bundleInfo.bundles { + bundleInfo.bundles[i].bInputDir = catalogDInfo.operatorName + ".v" + b.bundleVersion bundleInfo.bundles[i].imageRef = remoteRegistryRepo + catalogDInfo.operatorName + "-bundle:v" + b.bundleVersion } catalogDInfo.catalogDir = catalogDInfo.operatorName + "-catalog" catalogDInfo.imageRef = remoteRegistryRepo + catalogDInfo.catalogDir + ":test" }) - When("Build and load plain+v0 bundle images into the test environment", func() { - It("Build the plain bundle images and load them", func() { - for _, b := range bundleInfo.bundles { - dockerContext := filepath.Join(bundleInfo.baseFolderPath, b.bInputDir) - dockerfilePath := filepath.Join(dockerContext, "Dockerfile") - err = buildPushLoadContainer(b.imageRef, dockerfilePath, dockerContext, kindServer, GinkgoWriter) - Expect(err).ToNot(HaveOccurred()) - } - }) - }) - When("Create the FBC", func() { - It("Create a FBC", func() { - By("Creating FBC for plain bundle using custom routine") - imageRefsBundleVersions := make(map[string]string) - for _, b := range bundleInfo.bundles { - imageRefsBundleVersions[b.imageRef] = b.bundleVersion - } - fbc := CreateFBC(catalogDInfo.operatorName, catalogDInfo.desiredChannelName, imageRefsBundleVersions) - err = WriteFBC(*fbc, filepath.Join(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir), catalogDInfo.fbcFileName) - Expect(err).ToNot(HaveOccurred()) - }) - }) - When("Build and load the FBC image into the test environment", func() { - It("Generate the docker file, build and load FBC image", func() { - By("Calling generate dockerfile function written") - err = generateDockerFile(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir, catalogDInfo.catalogDir+".Dockerfile") - Expect(err).ToNot(HaveOccurred()) - - By("Building the catalog image and loading into the test environment") - dockerContext := catalogDInfo.baseFolderPath - dockerfilePath := filepath.Join(dockerContext, catalogDInfo.catalogDir) + ".Dockerfile" - err = buildPushLoadContainer(catalogDInfo.imageRef, dockerfilePath, dockerContext, kindServer, GinkgoWriter) - Expect(err).ToNot(HaveOccurred()) - }) - }) - When("Create a catalog object and check if the resources are created", func() { - It("Create catalog object for the FBC and check if catalog, packages and bundle metadatas are created", func() { - bundleVersions := make([]string, len(bundleInfo.bundles)) - for i, bundle := range bundleInfo.bundles { - bundleVersions[i] = bundle.bundleVersion - } - operatorCatalog, err = createCatalogCheckResources(operatorCatalog, catalogDInfo, bundleVersions) - Expect(err).ToNot(HaveOccurred()) - }) - }) - When("Install an operator and check if the operator operations succeed", func() { - It("Create an operator object and install it", func() { - By("Creating an operator object") - operator, err = createOperator(ctx, catalogDInfo.operatorName, operatorAction.installVersion) - Expect(err).ToNot(HaveOccurred()) - - By("Checking if the operator operations succeeded") - checkOperatorOperationsSuccess(operator, catalogDInfo.operatorName, operatorAction.installVersion, bundleInfo.baseFolderPath) - }) - }) - When("Upgrade an operator to higher version and check if the operator operations succeed", func() { - It("Upgrade to a higher version of the operator", func() { - By("Upgrading the operator") - operator, err = upgradeOperator(ctx, catalogDInfo.operatorName, operatorAction.upgradeVersion) + It("should succeed", func() { + By("creating a new operator-sdk project") + err = sdkInitialize(sdkInfo) + Expect(err).ToNot(HaveOccurred()) + + By("creating a new api and controller") + err = sdkNewAPIAndController(sdkInfo) + Expect(err).ToNot(HaveOccurred()) + + By("generating CRD manifests") + err = sdkGenerateManifests(sdkInfo) + Expect(err).ToNot(HaveOccurred()) + + By("generating bundle directory using kustomize") + // Creates bundle structure for the specified bundle versions + // Bundle content is same for the bundles now + for _, b := range bundleInfo.bundles { + err = kustomizeGenPlainBundleDirectory(sdkInfo, bundleInfo.baseFolderPath, b) Expect(err).ToNot(HaveOccurred()) + } - By("Checking if the operator operations succeeded") - checkOperatorOperationsSuccess(operator, catalogDInfo.operatorName, operatorAction.upgradeVersion, bundleInfo.baseFolderPath) - }) - }) - When("Delete an operator", func() { - It("Delete an operator", func() { - err = deleteOperator(ctx, catalogDInfo.operatorName) + By("building/pushing/kind loading bundle images from bundle directories") + for _, b := range bundleInfo.bundles { + dockerContext := filepath.Join(bundleInfo.baseFolderPath, b.bInputDir) + dockerfilePath := filepath.Join(dockerContext, "plainbundle.Dockerfile") + err = buildPushLoadContainer(b.imageRef, dockerfilePath, dockerContext, kindServer, GinkgoWriter) Expect(err).ToNot(HaveOccurred()) + } - By("Verifying the operator doesn't exist") - Eventually(func(g Gomega) { - err = validateOperatorDeletion(catalogDInfo.operatorName) - g.Expect(errors.IsNotFound(err)).To(BeTrue()) - }).Should(Succeed()) - }) + By("generating catalog directory by forming FBC and dockerfile using custom function") + imageRefsBundleVersions := make(map[string]string) + for _, b := range bundleInfo.bundles { + imageRefsBundleVersions[b.imageRef] = b.bundleVersion + } + err = genPlainCatalogDirectory(catalogDInfo, imageRefsBundleVersions) + Expect(err).ToNot(HaveOccurred()) + + By("building/pushing/kind loading the catalog images") + dockerContext := catalogDInfo.baseFolderPath + dockerfilePath := filepath.Join(dockerContext, catalogDInfo.catalogDir+".Dockerfile") + err = buildPushLoadContainer(catalogDInfo.imageRef, dockerfilePath, dockerContext, kindServer, GinkgoWriter) + Expect(err).ToNot(HaveOccurred()) + + By("creating a Catalog CR and verifying the creation of respective packages and bundle metadata") + bundleVersions := make([]string, len(bundleInfo.bundles)) + for i, bundle := range bundleInfo.bundles { + bundleVersions[i] = bundle.bundleVersion + } + operatorCatalog, err = createCatalogCheckResources(operatorCatalog, catalogDInfo, bundleVersions) + Expect(err).ToNot(HaveOccurred()) + + By("creating an operator CR and verifying the operator operations") + nameSpace := sdkInfo.projectName + "-system" + operator, err = createOperator(ctx, catalogDInfo.operatorName, operatorAction.installVersion) + Expect(err).ToNot(HaveOccurred()) + checkOperatorOperationsSuccess(operator, catalogDInfo.operatorName, operatorAction.installVersion, bundleInfo.baseFolderPath, nameSpace) + + By("upgrading an operator and verifying the operator operations") + nameSpace = sdkInfo.projectName + "-system" + operator, err = upgradeOperator(ctx, catalogDInfo.operatorName, operatorAction.upgradeVersion) + Expect(err).ToNot(HaveOccurred()) + checkOperatorOperationsSuccess(operator, catalogDInfo.operatorName, operatorAction.upgradeVersion, bundleInfo.baseFolderPath, nameSpace) + + By("deleting the operator CR and verifying the operator doesn't exist after deletion") + err = deleteOperator(ctx, catalogDInfo.operatorName) + Expect(err).ToNot(HaveOccurred()) + Eventually(func(g Gomega) { + err = validateOperatorDeletion(catalogDInfo.operatorName) + g.Expect(errors.IsNotFound(err)).To(BeTrue()) + }).Should(Succeed()) + + By("deleting the catalog CR and verifying the deletion") + err = deleteCatalog(operatorCatalog) + Expect(err).ToNot(HaveOccurred()) + Eventually(func(g Gomega) { + err = validateCatalogDeletion(operatorCatalog) + g.Expect(errors.IsNotFound(err)).To(BeTrue()) + }).Should(Succeed()) }) - When("Clearing up catalog object and other files formed for the test", func() { - It("Clearing up data generated for the test", func() { - //Deleting the catalog object and checking if the deletion was successful - Eventually(func(g Gomega) { - err = deleteAndValidateCatalogDeletion(operatorCatalog) - g.Expect(errors.IsNotFound(err)).To(BeTrue()) - }).Should(Succeed()) - - var toDelete []string - toDelete = append(toDelete, filepath.Join(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir)) // delete the FBC formed - toDelete = append(toDelete, filepath.Join(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir+".Dockerfile")) // delete the catalog Dockerfile generated - err = deleteFolderFile(toDelete) - Expect(err).ToNot(HaveOccurred()) - }) + AfterEach(func() { + // Clearing up data generated for the test + var toDelete []string + for _, b := range bundleInfo.bundles { + toDelete = append(toDelete, bundleInfo.baseFolderPath+"/"+b.bInputDir) // delete the registry+v1 bundles formed + } + toDelete = append(toDelete, sdkInfo.projectName) //delete the sdk project directory + toDelete = append(toDelete, filepath.Join(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir)) // delete the FBC formed + toDelete = append(toDelete, filepath.Join(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir+".Dockerfile")) // delete the catalog Dockerfile generated + err = deleteFolderFile(toDelete) + Expect(err).ToNot(HaveOccurred()) }) }) @@ -253,11 +258,11 @@ var _ = Describe("Operator Framework E2E for registry+v1 bundles", func() { ) BeforeEach(func() { sdkInfo = &SdkProjectInfo{ - projectName: "example-operator", - domainName: "example.com", + projectName: "registry-operator", + domainName: "example2.com", group: "cache", version: "v1alpha1", - kind: "Memcached1", + kind: "Memcached2", } bundleInfo = &BundleInfo{ baseFolderPath: "../../testdata/bundles/registry-v1", @@ -273,7 +278,7 @@ var _ = Describe("Operator Framework E2E for registry+v1 bundles", func() { catalogDInfo = &CatalogDInfo{ baseFolderPath: "../../testdata/catalogs", fbcFileName: "catalog.yaml", - operatorName: "example-operator", + operatorName: "registry-operator", } operatorAction = &OperatorActionInfo{ installVersion: "0.1.0", @@ -288,150 +293,99 @@ var _ = Describe("Operator Framework E2E for registry+v1 bundles", func() { semverFileName = "registry-semver.yaml" }) - - When("Build registry+v1 bundles with operator-sdk", func() { - It("Initialize new operator-sdk project and create new api and controller", func() { - err = sdkInitialize(sdkInfo) - Expect(err).ToNot(HaveOccurred()) - }) - It("Generate manifests and CSV for the operator", func() { - err = sdkGenerateManifestsCSV(sdkInfo) - Expect(err).ToNot(HaveOccurred()) - }) - It("Generate and build registry+v1 bundle", func() { - // Creates bundle structure for the specified bundle versions - // Bundle content is same for the bundles now - for _, b := range bundleInfo.bundles { - err = sdkComplete(sdkInfo, bundleInfo.baseFolderPath, b) - Expect(err).ToNot(HaveOccurred()) - } - }) - }) - When("Create FBC and validate FBC", func() { - It("Create a FBC", func() { - sdkCatalogFile := filepath.Join(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir, catalogDInfo.fbcFileName) - By("Forming the semver yaml file") - bundleImageRefs := make([]string, len(bundleInfo.bundles)) - for i, bundle := range bundleInfo.bundles { - bundleImageRefs[i] = bundle.imageRef - } - err := generateOLMSemverFile(semverFileName, bundleImageRefs) - Expect(err).ToNot(HaveOccurred()) - - By("Forming the FBC using semver") - semverFileAbsPath, err := filepath.Abs(semverFileName) - Expect(err).ToNot(HaveOccurred()) - opmArgs := "OPM_ARGS=alpha render-template semver " + semverFileAbsPath + " -o yaml --use-http" - cmd := exec.Command("make", "-s", "opm", opmArgs) - cmd.Dir = operatorControllerHome - output, err := cmd.Output() - Expect(err).ToNot(HaveOccurred()) - - By("Saving the output under catalogs in testdata") - err = os.MkdirAll(filepath.Dir(sdkCatalogFile), os.ModePerm) - Expect(err).ToNot(HaveOccurred()) - - file, err := os.Create(sdkCatalogFile) - Expect(err).ToNot(HaveOccurred()) - defer file.Close() - _, err = file.Write(output) - Expect(err).ToNot(HaveOccurred()) - }) - It("Validate FBC", func() { - By("By validating the FBC using opm validate") - err = validateFBC(filepath.Join(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir)) - Expect(err).ToNot(HaveOccurred()) - }) - }) - When("Generate docker file and FBC image and load the FBC image into test environment", func() { - It("Create the docker file", func() { - By("By using opm generate tool") - dockerFolderAbsPath, err := filepath.Abs(filepath.Join(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir)) - Expect(err).ToNot(HaveOccurred()) - opmArgs := "OPM_ARGS=generate dockerfile " + dockerFolderAbsPath - cmd := exec.Command("make", "opm", opmArgs) - cmd.Dir = operatorControllerHome - err = cmd.Run() - Expect(err).ToNot(HaveOccurred()) - - By("Building the catalog image and loading into the test environment") - dockerContext := catalogDInfo.baseFolderPath - dockerFilePath := filepath.Join(dockerContext, catalogDInfo.catalogDir+".Dockerfile") - err = buildPushLoadContainer(catalogDInfo.imageRef, dockerFilePath, dockerContext, kindServer, GinkgoWriter) - Expect(err).ToNot(HaveOccurred()) - }) - }) - When("Create a catalog object and check if the resources are created", func() { - It("Create catalog object for the FBC and check if catalog, packages and bundle metadatas are created", func() { - bundleVersions := make([]string, len(bundleInfo.bundles)) - for i, bundle := range bundleInfo.bundles { - bundleVersions[i] = bundle.bundleVersion - } - operatorCatalog, err = createCatalogCheckResources(operatorCatalog, catalogDInfo, bundleVersions) - Expect(err).ToNot(HaveOccurred()) - }) - }) - When("Install an operator and check if the operator operations succeed", func() { - It("Create an operator object and install it", func() { - By("Creating an operator object") - operator, err = createOperator(ctx, catalogDInfo.operatorName, operatorAction.installVersion) - Expect(err).ToNot(HaveOccurred()) - - By("Checking if the operator operations succeeded") - checkOperatorOperationsSuccess(operator, catalogDInfo.operatorName, operatorAction.installVersion, bundleInfo.baseFolderPath) - }) - }) - When("Upgrade an operator to higher version and check if the operator operations succeed", func() { - It("Upgrade to a higher version of the operator", func() { - By("Upgrading the operator") - operator, err = upgradeOperator(ctx, catalogDInfo.operatorName, operatorAction.upgradeVersion) - Expect(err).ToNot(HaveOccurred()) - - By("Checking if the operator operations succeeded") - checkOperatorOperationsSuccess(operator, catalogDInfo.operatorName, operatorAction.upgradeVersion, bundleInfo.baseFolderPath) - }) - }) - When("An operator is deleted", func() { - It("Delete and operator", func() { - err = deleteOperator(ctx, catalogDInfo.operatorName) + It("should succeed", func() { + By("creating a new operator-sdk project") + err = sdkInitialize(sdkInfo) + Expect(err).ToNot(HaveOccurred()) + + By("creating a new api and controller") + err = sdkNewAPIAndController(sdkInfo) + Expect(err).ToNot(HaveOccurred()) + + By("generating CRD manifests") + err = sdkGenerateManifests(sdkInfo) + Expect(err).ToNot(HaveOccurred()) + + By("generating the CSV") + err = sdkGenerateCSV(sdkInfo) + Expect(err).ToNot(HaveOccurred()) + + By("generating bundle directory and building/pushing/kind loading bundle images from bundle directories") + // Creates bundle structure for the specified bundle versions + // Bundle content is same for the bundles now + for _, b := range bundleInfo.bundles { + err = sdkBundleComplete(sdkInfo, bundleInfo.baseFolderPath, b) Expect(err).ToNot(HaveOccurred()) + } - By("Eventually the operator should not exists") - Eventually(func(g Gomega) { - err = validateOperatorDeletion(catalogDInfo.operatorName) - g.Expect(errors.IsNotFound(err)).To(BeTrue()) - }).Should(Succeed()) - }) + By("generating catalog directory by forming FBC and dockerfile using opm tool, and validating the FBC formed") + bundleImageRefs := make([]string, len(bundleInfo.bundles)) + for i, bundle := range bundleInfo.bundles { + bundleImageRefs[i] = bundle.imageRef + } + err = genRegistryCatalogDirectory(catalogDInfo, bundleImageRefs, semverFileName) + Expect(err).ToNot(HaveOccurred()) + + By("building/pushing/kind loading the catalog images") + dockerContext := catalogDInfo.baseFolderPath + dockerFilePath := filepath.Join(dockerContext, catalogDInfo.catalogDir+".Dockerfile") + err = buildPushLoadContainer(catalogDInfo.imageRef, dockerFilePath, dockerContext, kindServer, GinkgoWriter) + Expect(err).ToNot(HaveOccurred()) + + By("creating a Catalog CR and verifying the creation of respective packages and bundle metadata") + bundleVersions := make([]string, len(bundleInfo.bundles)) + for i, bundle := range bundleInfo.bundles { + bundleVersions[i] = bundle.bundleVersion + } + operatorCatalog, err = createCatalogCheckResources(operatorCatalog, catalogDInfo, bundleVersions) + Expect(err).ToNot(HaveOccurred()) + + By("creating an operator CR and verifying the operator operations") + operator, err = createOperator(ctx, catalogDInfo.operatorName, operatorAction.installVersion) + Expect(err).ToNot(HaveOccurred()) + checkOperatorOperationsSuccess(operator, catalogDInfo.operatorName, operatorAction.installVersion, bundleInfo.baseFolderPath, deployedNameSpace) + + By("upgrading an operator and verifying the operator operations") + operator, err = upgradeOperator(ctx, catalogDInfo.operatorName, operatorAction.upgradeVersion) + Expect(err).ToNot(HaveOccurred()) + checkOperatorOperationsSuccess(operator, catalogDInfo.operatorName, operatorAction.upgradeVersion, bundleInfo.baseFolderPath, deployedNameSpace) + + By("deleting the operator CR and verifying the operator doesn't exist") + err = deleteOperator(ctx, catalogDInfo.operatorName) + Expect(err).ToNot(HaveOccurred()) + Eventually(func(g Gomega) { + err = validateOperatorDeletion(catalogDInfo.operatorName) + g.Expect(errors.IsNotFound(err)).To(BeTrue()) + }).Should(Succeed()) + + By("deleting the catalog CR and verifying the deletion") + err = deleteCatalog(operatorCatalog) + Expect(err).ToNot(HaveOccurred()) + Eventually(func(g Gomega) { + err = validateCatalogDeletion(operatorCatalog) + g.Expect(errors.IsNotFound(err)).To(BeTrue()) + }).Should(Succeed()) }) - When("Clearing up catalog object and other files formed for the test", func() { - It("Clearing up data generated for the test", func() { - //Deleting the catalog object and checking if the deletion was successful - Eventually(func(g Gomega) { - err = deleteAndValidateCatalogDeletion(operatorCatalog) - g.Expect(errors.IsNotFound(err)).To(BeTrue()) - }).Should(Succeed()) - - var toDelete []string - for _, b := range bundleInfo.bundles { - toDelete = append(toDelete, bundleInfo.baseFolderPath+"/"+b.bInputDir) // delete the registry+v1 bundles formed - } - toDelete = append(toDelete, sdkInfo.projectName) //delete the sdk project directory - toDelete = append(toDelete, semverFileName) // delete the semver yaml formed - toDelete = append(toDelete, filepath.Join(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir)) // delete the FBC formed - toDelete = append(toDelete, filepath.Join(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir+".Dockerfile")) // delete the catalog Dockerfile generated - err = deleteFolderFile(toDelete) - Expect(err).ToNot(HaveOccurred()) - }) + AfterEach(func() { + var toDelete []string + for _, b := range bundleInfo.bundles { + toDelete = append(toDelete, bundleInfo.baseFolderPath+"/"+b.bInputDir) // delete the registry+v1 bundles formed + } + toDelete = append(toDelete, sdkInfo.projectName) //delete the sdk project directory + toDelete = append(toDelete, semverFileName) // delete the semver yaml formed + toDelete = append(toDelete, filepath.Join(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir)) // delete the FBC formed + toDelete = append(toDelete, filepath.Join(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir+".Dockerfile")) // delete the catalog Dockerfile generated + err = deleteFolderFile(toDelete) + Expect(err).ToNot(HaveOccurred()) }) }) +// Creates new operator-sdk project with the name sdkInfo.projectName func sdkInitialize(sdkInfo *SdkProjectInfo) error { - // Create new project for the operator if err := os.Mkdir(sdkInfo.projectName, 0755); err != nil { return fmt.Errorf("Error creating the sdk project %v:%v", sdkInfo.projectName, err) } - // Initialize the operator-sdk project operatorSdkProjectAbsPath, _ := filepath.Abs(sdkInfo.projectName) operatorSdkProjectPath := "OPERATOR_SDK_PROJECT_PATH=" + operatorSdkProjectAbsPath operatorSdkArgs := "OPERATOR_SDK_ARGS= init --domain=" + sdkInfo.domainName @@ -442,12 +396,17 @@ func sdkInitialize(sdkInfo *SdkProjectInfo) error { return fmt.Errorf("Error initializing the operator-sdk project %v: %v : %v", sdkInfo.projectName, string(output), err) } - // Create new API and controller - operatorSdkProjectPath = "OPERATOR_SDK_PROJECT_PATH=" + operatorSdkProjectAbsPath - operatorSdkArgs = "OPERATOR_SDK_ARGS= create api --group=" + sdkInfo.group + " --version=" + sdkInfo.version + " --kind=" + sdkInfo.kind + " --resource --controller" - cmd = exec.Command("make", "operator-sdk", operatorSdkProjectPath, operatorSdkArgs) + return nil +} + +// Creates new API and controller +func sdkNewAPIAndController(sdkInfo *SdkProjectInfo) error { + operatorSdkProjectAbsPath, _ := filepath.Abs(sdkInfo.projectName) + operatorSdkProjectPath := "OPERATOR_SDK_PROJECT_PATH=" + operatorSdkProjectAbsPath + operatorSdkArgs := "OPERATOR_SDK_ARGS= create api --group=" + sdkInfo.group + " --version=" + sdkInfo.version + " --kind=" + sdkInfo.kind + " --resource --controller" + cmd := exec.Command("make", "operator-sdk", operatorSdkProjectPath, operatorSdkArgs) cmd.Dir = operatorControllerHome - output, err = cmd.CombinedOutput() + output, err := cmd.CombinedOutput() if err != nil { return fmt.Errorf("Error creating new API and controller for the operator-sdk project %v: %v : %v", sdkInfo.projectName, string(output), err) } @@ -463,7 +422,9 @@ func sdkInitialize(sdkInfo *SdkProjectInfo) error { return nil } -func sdkGenerateManifestsCSV(sdkInfo *SdkProjectInfo) error { +// Updates the generated code if the API is changed. +// Generates and updates the CRD manifests +func sdkGenerateManifests(sdkInfo *SdkProjectInfo) error { // Update the generated code for the resources cmd := exec.Command("make", "generate") cmd.Dir = sdkInfo.projectName @@ -482,15 +443,19 @@ func sdkGenerateManifestsCSV(sdkInfo *SdkProjectInfo) error { crdFilePath := filepath.Join(sdkInfo.projectName, "config", "crd", "bases", sdkInfo.group+"."+sdkInfo.domainName+"_"+strings.ToLower(sdkInfo.kind)+"s.yaml") Expect(crdFilePath).To(BeAnExistingFile()) - // Generate CSV for the bundle with default values + return nil +} + +// Generates CSV for the bundle with default values +func sdkGenerateCSV(sdkInfo *SdkProjectInfo) error { operatorSdkProjectAbsPath, _ := filepath.Abs(sdkInfo.projectName) operatorSdkProjectPath := "OPERATOR_SDK_PROJECT_PATH=" + operatorSdkProjectAbsPath operatorSdkArgs := "OPERATOR_SDK_ARGS= generate kustomize manifests --interactive=false" - cmd = exec.Command("make", "operator-sdk", operatorSdkProjectPath, operatorSdkArgs) + cmd := exec.Command("make", "operator-sdk", operatorSdkProjectPath, operatorSdkArgs) cmd.Dir = operatorControllerHome output, err := cmd.CombinedOutput() if err != nil { - return fmt.Errorf("Error generating CSV for the operator-sdk project %v: %v: %v", sdkInfo.projectName, output, err) + return fmt.Errorf("Error generating CSV for the operator-sdk project %v: %v: %v", sdkInfo.projectName, string(output), err) } // Checking if CRD manifests are generated @@ -500,8 +465,39 @@ func sdkGenerateManifestsCSV(sdkInfo *SdkProjectInfo) error { return nil } -func sdkComplete(sdkInfo *SdkProjectInfo, rootBundlePath string, bundleData BundleContent) error { - // Copy CRDs and other supported kinds, generate metadata, and in bundle format +// generates the bundle directory content for plain bundle format. The yaml files are formed using the kustomize tool +// and the bundle dockerfile is generated using a custom routine. +func kustomizeGenPlainBundleDirectory(sdkInfo *SdkProjectInfo, rootBundlePath string, bundleData BundleContent) error { + // Create the bundle directory structure + if err := os.MkdirAll(filepath.Join(rootBundlePath, bundleData.bInputDir, "manifests"), os.ModePerm); err != nil { + return fmt.Errorf("Failed to create directory for bundle structure %s: %v", bundleData.bInputDir, err) + } + + // Create the manifests for the plain+v0 bundle + operatorSdkProjectAbsPath, _ := filepath.Abs(sdkInfo.projectName) + operatorSdkProjectPath := "OPERATOR_SDK_PROJECT_PATH=" + operatorSdkProjectAbsPath + outputPlainBundlePath, _ := filepath.Abs(filepath.Join(rootBundlePath, bundleData.bInputDir, "manifests", "manifest.yaml")) + kustomizeArgs := "KUSTOMIZE_ARGS= build config/default > " + outputPlainBundlePath + cmd := exec.Command("make", "kustomize", operatorSdkProjectPath, kustomizeArgs) + cmd.Dir = operatorControllerHome + output, err := cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("Error generating plain bundle directory %v: %v", string(output), err) + } + + // Create bundle dockerfile + dockerFilePath := filepath.Join(rootBundlePath, bundleData.bInputDir, "plainbundle.Dockerfile") + if err = generateBundleDockerFile(dockerFilePath, bundleData.bInputDir); err != nil { + return fmt.Errorf("Error generating bundle dockerfile for the bundle %v: %v", bundleData.bInputDir, err) + } + return nil +} + +// Copies the CRDs. Generates metadata and manifest in registry+v1 bundle format. +// Build the bundle image and load into cluster. +// Copies the bundle to appropriate bundle format. +func sdkBundleComplete(sdkInfo *SdkProjectInfo, rootBundlePath string, bundleData BundleContent) error { + // Copy CRDs and other supported kinds and generate metadata and manifest in bundle format bundleGenFlags := "BUNDLE_GEN_FLAGS=-q --overwrite=false --version " + bundleData.bundleVersion + " $(BUNDLE_METADATA_OPTS)" cmd := exec.Command("make", "bundle", bundleGenFlags) cmd.Dir = sdkInfo.projectName @@ -565,14 +561,14 @@ func buildPushLoadContainer(tag, dockerfilePath, dockerContext, kindServer strin cmd.Stderr = w cmd.Stdout = w if err := cmd.Run(); err != nil { - return fmt.Errorf("Error building Docker container image %s : %v", tag, err) + return fmt.Errorf("Error building Docker container image %s : %+v", tag, err) } cmd = exec.Command("docker", "push", tag) cmd.Stderr = w cmd.Stdout = w if err := cmd.Run(); err != nil { - return fmt.Errorf("Error pushing Docker container image: %s to the registry: %v", tag, err) + return fmt.Errorf("Error pushing Docker container image: %s to the registry: %+v", tag, err) } err := loadImages(w, kindServer, tag) @@ -591,12 +587,84 @@ func loadImages(w io.Writer, kindServerName string, images ...string) error { cmd.Stderr = w cmd.Stdout = w if err := cmd.Run(); err != nil { - return fmt.Errorf("Error loading the container image %s into the cluster %s : %v", image, kindServerName, err) + return fmt.Errorf("Error loading the container image %s into the cluster %s : %+v", image, kindServerName, err) } } return nil } +// Generates catalog directory contents for the plain bundle format. The FBC(yaml file) and the Dockerfile +// is formed using custom routines +func genPlainCatalogDirectory(catalogDInfo *CatalogDInfo, imageRefsBundleVersions map[string]string) error { + // forming the FBC using custom routine + fbc := CreateFBC(catalogDInfo.operatorName, catalogDInfo.desiredChannelName, imageRefsBundleVersions) + if err := WriteFBC(*fbc, filepath.Join(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir), catalogDInfo.fbcFileName); err != nil { + return fmt.Errorf("Error writing FBC content for the fbc %v : %v", catalogDInfo.fbcFileName, err) + } + + // generating dockerfile for the catalog using custom routine + dockerFilePath := filepath.Join(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir+".Dockerfile") + if err := generateCatalogDockerFile(dockerFilePath, catalogDInfo.catalogDir); err != nil { + return fmt.Errorf("Error generating catalog Dockerfile for the catalog directory %v : %v", catalogDInfo.catalogDir, err) + } + return nil +} + +// Generates catalog contents for the registry bundle format. The FBC(yaml file) and the Dockerfile +// is formed using opm tool. +func genRegistryCatalogDirectory(catalogDInfo *CatalogDInfo, bundleImageRefs []string, semverFileName string) error { + // forming the semver template yaml file + sdkCatalogFile := filepath.Join(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir, catalogDInfo.fbcFileName) + if err := formOLMSemverFile(semverFileName, bundleImageRefs); err != nil { + return fmt.Errorf("Error forming the semver template yaml file %v : %v", semverFileName, err) + } + + // generating the FBC using semver template") + semverFileAbsPath, err := filepath.Abs(semverFileName) + if err != nil { + return fmt.Errorf("Error forming the absolute path of the semver file %v : %v", semverFileName, err) + } + opmArgs := "OPM_ARGS=alpha render-template semver " + semverFileAbsPath + " -o yaml --use-http" + cmd := exec.Command("make", "-s", "opm", opmArgs) + cmd.Dir = operatorControllerHome + output, err := cmd.Output() + if err != nil { + return fmt.Errorf("Error running opm command for FBC generation: %v", err) + } + + // saving the output of semver template under catalogs in testdata + if err = os.MkdirAll(filepath.Dir(sdkCatalogFile), os.ModePerm); err != nil { + return fmt.Errorf("Error forming the catalog directory structure: %v", err) + } + file, err := os.Create(sdkCatalogFile) + if err != nil { + return fmt.Errorf("Error creating the file %v: %v", sdkCatalogFile, err) + } + defer file.Close() + if _, err = file.Write(output); err != nil { + return fmt.Errorf("Error writing to the file %v: %v", sdkCatalogFile, err) + } + + // validating the FBC using opm validate + if err = validateFBC(filepath.Join(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir)); err != nil { + return fmt.Errorf("Error validating the FBC %v: %v", sdkCatalogFile, err) + } + + // generating the dockerfile for catalog using opm generate tool + dockerFolderAbsPath, err := filepath.Abs(filepath.Join(catalogDInfo.baseFolderPath, catalogDInfo.catalogDir)) + if err != nil { + return fmt.Errorf("Error forming the absolute path of the catalog dockerfile %v", err) + } + opmArgs = "OPM_ARGS=generate dockerfile " + dockerFolderAbsPath + cmd = exec.Command("make", "opm", opmArgs) + cmd.Dir = operatorControllerHome + output, err = cmd.CombinedOutput() + if err != nil { + return fmt.Errorf("Error generating catalog dockerfile : %v :%v", string(output), err) + } + return nil +} + // Validates the FBC using opm tool func validateFBC(fbcDirPath string) error { fbcDirAbsPath, err := filepath.Abs(fbcDirPath) @@ -647,6 +715,8 @@ func createOperator(ctx context.Context, opName, version string) (*operatorv1alp err := c.Create(ctx, operator) return operator, err + + // err := checkOperatorOperationsSuccess(opName, catalogDInfo.operatorName, operatorAction.installVersion, bundleInfo.baseFolderPath, nameSpace) } // Upgrades the operator opName for the version @@ -677,6 +747,26 @@ func deleteOperator(ctx context.Context, opName string) error { return err } +// Checks if the operator was successfully deleted and returns error if not. +func validateOperatorDeletion(opName string) error { + err := c.Get(ctx, types.NamespacedName{Name: opName}, &operatorv1alpha1.Operator{}) + return err +} + +// Deletes the catalog instance. +func deleteCatalog(catalog *catalogd.Catalog) error { + if err := c.Delete(ctx, catalog); err != nil { + return fmt.Errorf("Error deleting the catalog instance: %v", err) + } + return nil +} + +// Checks if the catalog was successfully deleted and returns error if not. +func validateCatalogDeletion(catalog *catalogd.Catalog) error { + err := c.Get(ctx, types.NamespacedName{Name: catalog.Name}, &catalogd.Catalog{}) + return err +} + // Checks if the expected condition and actual condition for a resource matches and returns error if not. func checkConditionEquals(actualCond, expectedCond *metav1.Condition) error { if actualCond == nil { @@ -742,7 +832,7 @@ func createCatalogCheckResources(operatorCatalog *catalogd.Catalog, catalogDInfo } // Checks if the operator operator succeeds following operator install or upgrade -func checkOperatorOperationsSuccess(operator *operatorv1alpha1.Operator, pkgName, opVersion, bundlePath string) { +func checkOperatorOperationsSuccess(operator *operatorv1alpha1.Operator, pkgName, opVersion, bundlePath, nameSpace string) { // checking for a successful resolution and bundle path Eventually(func(g Gomega) { err := validateResolutionAndBundlePath(operator) @@ -763,7 +853,7 @@ func checkOperatorOperationsSuccess(operator *operatorv1alpha1.Operator, pkgName // verifying the presence of relevant manifest from the bundle on cluster Eventually(func(g Gomega) { - err := checkManifestPresence(bundlePath, pkgName, opVersion) + err := checkManifestPresence(bundlePath, pkgName, opVersion, nameSpace) g.Expect(err).ToNot(HaveOccurred()) }).Should(Succeed()) } @@ -900,45 +990,35 @@ func validatePackageInstallation(operator *operatorv1alpha1.Operator) error { } // Checks the presence of operator manifests for the operator -func checkManifestPresence(bundlePath, operatorName, version string) error { +func checkManifestPresence(bundlePath, operatorName, version, namespace string) error { resources, err := collectKubernetesObjects(bundlePath, operatorName, version) if err != nil { return err } for _, resource := range resources { - if resource.Kind == "ClusterServiceVersion" { + if resource.GetObjectKind().GroupVersionKind().Kind == "ClusterServiceVersion" { continue } - gvk := schema.GroupVersionKind{ - Group: "", - Version: resource.APIVersion, - Kind: resource.Kind, - } - + gvk := resource.GetObjectKind().GroupVersionKind() obj := &unstructured.Unstructured{} obj.SetGroupVersionKind(gvk) - if err = c.Get(ctx, types.NamespacedName{Name: resource.Metadata.Name, Namespace: deployedNameSpace}, obj); err != nil { - return fmt.Errorf("Error retrieving the resources %v from the namespace %v: %v", resource.Metadata.Name, deployedNameSpace, err) + + objMeta, ok := resource.(metav1.Object) + if !ok { + return fmt.Errorf("Failed to convert resource to metav1.Object") + } + objName := objMeta.GetName() + namespacedName := types.NamespacedName{ + Name: objName, + Namespace: namespace, + } + if err = c.Get(ctx, namespacedName, obj); err != nil { + return fmt.Errorf("Error retrieving the resources %v from the namespace %v: %v", namespacedName.Name, namespace, err) } } return nil } -// Checks if the operator was successfully deleted and returns error if not. -func validateOperatorDeletion(opName string) error { - err := c.Get(ctx, types.NamespacedName{Name: opName}, &operatorv1alpha1.Operator{}) - return err -} - -// Deletes the catalog and checks if the deletion was successful. Returns error if not. -func deleteAndValidateCatalogDeletion(catalog *catalogd.Catalog) error { - if err := c.Delete(ctx, catalog); err != nil { - return fmt.Errorf("Error deleting the catalog instance: %v", err) - } - err := c.Get(ctx, types.NamespacedName{Name: catalog.Name}, &catalogd.Catalog{}) - return err -} - // Moves the content from currentPath to newPath func moveFolderContents(currentPath, newPath string) error { files, err := os.ReadDir(currentPath) diff --git a/test/operator-framework-e2e/read_manifests.go b/test/operator-framework-e2e/read_manifests.go index 7a1d17540..f73735854 100644 --- a/test/operator-framework-e2e/read_manifests.go +++ b/test/operator-framework-e2e/read_manifests.go @@ -4,22 +4,30 @@ import ( "fmt" "os" "path/filepath" - "strings" - "sigs.k8s.io/yaml" + "github.com/operator-framework/api/pkg/operators/v1alpha1" + apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/serializer" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" + clientgoscheme "k8s.io/client-go/kubernetes/scheme" ) -type Object struct { - Kind string `yaml:"kind"` - APIVersion string `yaml:"apiVersion"` - Metadata struct { - Name string `yaml:"name"` - } `yaml:"metadata"` +var ( + scheme = runtime.NewScheme() + + codecs = serializer.NewCodecFactory(scheme) +) + +func init() { + utilruntime.Must(clientgoscheme.AddToScheme(scheme)) + utilruntime.Must(apiextensionsv1.AddToScheme(scheme)) + utilruntime.Must(v1alpha1.AddToScheme(scheme)) } // / collectKubernetesObjects collects the Kubernetes objects present in the bundle manifest folder for a particular package and its version -func collectKubernetesObjects(bundlePath, packageName, version string) ([]Object, error) { - objects := []Object{} +func collectKubernetesObjects(bundlePath, packageName, version string) ([]runtime.Object, error) { + var objects []runtime.Object bundleManifestPath := filepath.Join(bundlePath, packageName+".v"+version, "manifests") err := filepath.Walk(bundleManifestPath, func(filePath string, fileInfo os.FileInfo, err error) error { @@ -31,23 +39,17 @@ func collectKubernetesObjects(bundlePath, packageName, version string) ([]Object return nil } - content, err := os.ReadFile(filePath) + fileContent, err := os.ReadFile(filePath) if err != nil { return fmt.Errorf("error reading file %s: %v", filePath, err) } - documents := strings.Split(string(content), "---") - for _, doc := range documents { - obj := Object{} - if err := yaml.Unmarshal([]byte(doc), &obj); err != nil { - return fmt.Errorf("error parsing YAML in file %s: %v", filePath, err) - } - - if obj.Kind != "" && obj.APIVersion != "" && obj.Metadata.Name != "" { - objects = append(objects, obj) - } + decoder := codecs.UniversalDecoder(scheme.PrioritizedVersionsAllGroups()...) + object, _, err := decoder.Decode(fileContent, nil, nil) + if err != nil { + return fmt.Errorf("failed to decode file %s: %w", filePath, err) } - + objects = append(objects, object) return nil }) diff --git a/testdata/bundles/plain-v0/plain.v0.1.0/Dockerfile b/testdata/bundles/plain-v0/plain.v0.1.0/Dockerfile index ac1b6dda5..bd54f66e6 100644 --- a/testdata/bundles/plain-v0/plain.v0.1.0/Dockerfile +++ b/testdata/bundles/plain-v0/plain.v0.1.0/Dockerfile @@ -1,2 +1,2 @@ FROM scratch -COPY manifests /manifests +COPY manifests /manifests \ No newline at end of file diff --git a/testdata/bundles/plain-v0/plain.v0.1.0/manifests/configmap.yaml b/testdata/bundles/plain-v0/plain.v0.1.0/manifests/configmap.yaml index d7d4f3a1c..f1e623af5 100644 --- a/testdata/bundles/plain-v0/plain.v0.1.0/manifests/configmap.yaml +++ b/testdata/bundles/plain-v0/plain.v0.1.0/manifests/configmap.yaml @@ -16,4 +16,4 @@ data: user-interface.properties: | color.good=purple color.bad=yellow - allow.textmode=true + allow.textmode=true \ No newline at end of file diff --git a/testdata/bundles/plain-v0/plain.v0.1.1/Dockerfile b/testdata/bundles/plain-v0/plain.v0.1.1/Dockerfile deleted file mode 100644 index ac1b6dda5..000000000 --- a/testdata/bundles/plain-v0/plain.v0.1.1/Dockerfile +++ /dev/null @@ -1,2 +0,0 @@ -FROM scratch -COPY manifests /manifests diff --git a/testdata/bundles/plain-v0/plain.v0.1.1/manifests/configmap.yaml b/testdata/bundles/plain-v0/plain.v0.1.1/manifests/configmap.yaml deleted file mode 100644 index 2376a6054..000000000 --- a/testdata/bundles/plain-v0/plain.v0.1.1/manifests/configmap.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# This configmap was pulled straight from the Kubernetes docs -# and can be found at https://kubernetes.io/docs/concepts/configuration/configmap/ -apiVersion: v1 -kind: ConfigMap -metadata: - name: game-demo -data: - # property-like keys; each key maps to a simple value - player_initial_lives: "5" - ui_properties_file_name: "user-interface.properties" - - # file-like keys - game.properties: | - enemy.types=aliens,monsters - player.maximum-lives=8 - user-interface.properties: | - color.good=purple - color.bad=yellow - allow.textmode=true