diff --git a/Makefile b/Makefile index 502940ce7..8c5a4e011 100644 --- a/Makefile +++ b/Makefile @@ -105,16 +105,16 @@ test: manifests generate fmt vet test-unit test-e2e #HELP Run all tests. .PHONY: e2e FOCUS := $(if $(TEST),-v -focus "$(TEST)") E2E_FLAGS ?= "" -e2e: $(GINKGO) #EXHELP Run the e2e tests. - $(GINKGO) --tags $(GO_BUILD_TAGS) $(E2E_FLAGS) -trace -progress $(FOCUS) test/e2e +e2e: $(SETUP_ENVTEST) #EXHELP Run the e2e tests. + eval $$($(SETUP_ENVTEST) use -p env $(ENVTEST_VERSION)) && go test -tags $(GO_BUILD_TAGS) -v ./test/e2e/... export REG_PKG_NAME=registry-operator export PLAIN_PKG_NAME=plain-operator export CATALOG_IMG=${E2E_REGISTRY_NAME}.${E2E_REGISTRY_NAMESPACE}.svc:5000/test-catalog:e2e .PHONY: test-op-dev-e2e -test-op-dev-e2e: $(GINKGO) $(OPERATOR_SDK) $(KUSTOMIZE) $(KIND) #HELP Run operator create, upgrade and delete tests. +test-op-dev-e2e: $(SETUP_ENVTEST) $(OPERATOR_SDK) $(KUSTOMIZE) $(KIND) #HELP Run operator create, upgrade and delete tests. test/operator-framework-e2e/setup.sh $(OPERATOR_SDK) $(CONTAINER_RUNTIME) $(KUSTOMIZE) $(KIND) $(KIND_CLUSTER_NAME) ${E2E_REGISTRY_NAMESPACE} - $(GINKGO) --tags $(GO_BUILD_TAGS) -trace -progress $(FOCUS) test/operator-framework-e2e + eval $$($(SETUP_ENVTEST) use -p env $(ENVTEST_VERSION)) && go test -tags $(GO_BUILD_TAGS) -v ./test/operator-framework-e2e/... .PHONY: test-unit ENVTEST_VERSION = $(shell go list -m k8s.io/client-go | cut -d" " -f2 | sed 's/^v0\.\([[:digit:]]\{1,\}\)\.[[:digit:]]\{1,\}$$/1.\1.x/') diff --git a/go.mod b/go.mod index e0ad672f7..fb6991801 100644 --- a/go.mod +++ b/go.mod @@ -7,8 +7,6 @@ require ( github.com/blang/semver/v4 v4.0.0 github.com/go-logr/logr v1.3.0 github.com/google/go-cmp v0.6.0 - github.com/onsi/ginkgo/v2 v2.13.1 - github.com/onsi/gomega v1.30.0 github.com/operator-framework/catalogd v0.10.0 github.com/operator-framework/deppy v0.1.0 github.com/operator-framework/operator-registry v1.32.0 @@ -64,7 +62,6 @@ require ( github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-migrate/migrate/v4 v4.16.1 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -72,7 +69,6 @@ require ( github.com/google/cel-go v0.15.3 // indirect github.com/google/gnostic v0.6.9 // indirect github.com/google/gofuzz v1.2.0 // indirect - github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gorilla/mux v1.8.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.7.0 // indirect @@ -94,6 +90,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/onsi/gomega v1.30.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.0-rc3 // indirect github.com/operator-framework/api v0.19.0 // indirect diff --git a/go.sum b/go.sum index f1b52fcba..476164f2a 100644 --- a/go.sum +++ b/go.sum @@ -393,7 +393,6 @@ github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/ github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= @@ -483,7 +482,6 @@ github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -528,7 +526,6 @@ github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= @@ -651,8 +648,8 @@ github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+ github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo/v2 v2.13.1 h1:LNGfMbR2OVGBfXjvRZIZ2YCTQdGKtPLvuI1rMCCj3OU= -github.com/onsi/ginkgo/v2 v2.13.1/go.mod h1:XStQ8QcGwLyF4HdfcZB8SFOS/MWCgDuXMSBe6zrvLgM= github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go index 5ba6245de..f4fed4ddc 100644 --- a/test/e2e/e2e_suite_test.go +++ b/test/e2e/e2e_suite_test.go @@ -2,17 +2,15 @@ package e2e import ( "context" + "os" "testing" - "time" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" - "k8s.io/utils/env" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -33,41 +31,23 @@ const ( testCatalogName = "test-catalog" ) -func TestE2E(t *testing.T) { - RegisterFailHandler(Fail) - SetDefaultEventuallyTimeout(1 * time.Minute) - SetDefaultEventuallyPollingInterval(1 * time.Second) - RunSpecs(t, "E2E Suite") -} - -var _ = BeforeSuite(func() { +func TestMain(m *testing.M) { cfg = ctrl.GetConfigOrDie() scheme := runtime.NewScheme() - Expect(operatorv1alpha1.AddToScheme(scheme)).To(Succeed()) - Expect(rukpakv1alpha1.AddToScheme(scheme)).To(Succeed()) - Expect(catalogd.AddToScheme(scheme)).To(Succeed()) + utilruntime.Must(operatorv1alpha1.AddToScheme(scheme)) + utilruntime.Must(rukpakv1alpha1.AddToScheme(scheme)) + utilruntime.Must(catalogd.AddToScheme(scheme)) + utilruntime.Must(appsv1.AddToScheme(scheme)) + utilruntime.Must(corev1.AddToScheme(scheme)) var err error - - err = appsv1.AddToScheme(scheme) - Expect(err).ToNot(HaveOccurred()) - - err = corev1.AddToScheme(scheme) - Expect(err).ToNot(HaveOccurred()) - c, err = client.New(cfg, client.Options{Scheme: scheme}) - Expect(err).To(Not(HaveOccurred())) -}) + utilruntime.Must(err) -var _ = AfterSuite(func() { - ctx := context.Background() - if basePath := env.GetString("ARTIFACT_PATH", ""); basePath != "" { - // get all the artifacts from the test run and save them to the artifact path - getArtifactsOutput(ctx, basePath) - } -}) + os.Exit(m.Run()) +} // createTestCatalog will create a new catalog on the test cluster, provided // the context, catalog name, and the image reference. It returns the created catalog diff --git a/test/e2e/install_test.go b/test/e2e/install_test.go index 31dbfd691..76647b5d5 100644 --- a/test/e2e/install_test.go +++ b/test/e2e/install_test.go @@ -7,12 +7,13 @@ import ( "os" "path/filepath" "strings" + "testing" "time" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "gopkg.in/yaml.v2" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -23,6 +24,7 @@ import ( "k8s.io/apimachinery/pkg/util/rand" kubeclient "k8s.io/client-go/kubernetes" "k8s.io/utils/env" + "sigs.k8s.io/controller-runtime/pkg/client" operatorv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" @@ -32,237 +34,296 @@ const ( artifactName = "operator-controller-e2e" ) -var _ = Describe("Operator Install", func() { - var ( - ctx context.Context - operatorCatalog *catalogd.Catalog - operatorName string - operator *operatorv1alpha1.Operator - ) - When("An operator is installed from an operator catalog", func() { - BeforeEach(func() { - ctx = context.Background() - var err error - operatorCatalog, err = createTestCatalog(ctx, testCatalogName, os.Getenv(testCatalogRefEnvVar)) - Expect(err).ToNot(HaveOccurred()) - - operatorName = fmt.Sprintf("operator-%s", rand.String(8)) - operator = &operatorv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{ - Name: operatorName, - }, - } - }) - AfterEach(func() { - Expect(c.Delete(ctx, operatorCatalog)).To(Succeed()) - Eventually(func(g Gomega) { - err := c.Get(ctx, types.NamespacedName{Name: operatorCatalog.Name}, &catalogd.Catalog{}) - g.Expect(errors.IsNotFound(err)).To(BeTrue()) - }).Should(Succeed()) - }) - When("the operator bundle format is registry+v1", func() { - BeforeEach(func() { - operator.Spec = operatorv1alpha1.OperatorSpec{ - PackageName: "prometheus", - } - }) - It("resolves the specified package with correct bundle path", func() { - By("creating the Operator resource") - Expect(c.Create(ctx, operator)).To(Succeed()) - - By("eventually reporting a successful resolution and bundle path") - Eventually(func(g Gomega) { - g.Expect(c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)).To(Succeed()) - g.Expect(operator.Status.Conditions).To(HaveLen(2)) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) - g.Expect(cond).ToNot(BeNil()) - g.Expect(cond.Status).To(Equal(metav1.ConditionTrue)) - g.Expect(cond.Reason).To(Equal(operatorv1alpha1.ReasonSuccess)) - g.Expect(cond.Message).To(ContainSubstring("resolved to")) - g.Expect(operator.Status.ResolvedBundleResource).To(Equal("localhost/testdata/bundles/registry-v1/prometheus-operator:v2.0.0")) - }).Should(Succeed()) - - By("eventually installing the package successfully") - Eventually(func(g Gomega) { - g.Expect(c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)).To(Succeed()) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeInstalled) - g.Expect(cond).ToNot(BeNil()) - g.Expect(cond.Status).To(Equal(metav1.ConditionTrue)) - g.Expect(cond.Reason).To(Equal(operatorv1alpha1.ReasonSuccess)) - g.Expect(cond.Message).To(ContainSubstring("installed from")) - g.Expect(operator.Status.InstalledBundleResource).ToNot(BeEmpty()) - - bd := rukpakv1alpha1.BundleDeployment{} - g.Expect(c.Get(ctx, types.NamespacedName{Name: operatorName}, &bd)).To(Succeed()) - hasValidBundle := apimeta.FindStatusCondition(bd.Status.Conditions, rukpakv1alpha1.TypeHasValidBundle) - g.Expect(hasValidBundle).ToNot(BeNil()) - g.Expect(hasValidBundle.Reason).To(Equal(rukpakv1alpha1.ReasonUnpackSuccessful)) - installed := apimeta.FindStatusCondition(bd.Status.Conditions, rukpakv1alpha1.TypeInstalled) - g.Expect(installed).ToNot(BeNil()) - g.Expect(installed.Reason).To(Equal(rukpakv1alpha1.ReasonInstallationSucceeded)) - }).Should(Succeed()) - }) - }) - - When("the operator bundle format is plain+v0", func() { - BeforeEach(func() { - operator.Spec = operatorv1alpha1.OperatorSpec{ - PackageName: "plain", - } - }) - It("resolves the specified package with correct bundle path", func() { - By("creating the Operator resource") - Expect(c.Create(ctx, operator)).To(Succeed()) - - By("eventually reporting a successful resolution and bundle path") - Eventually(func(g Gomega) { - g.Expect(c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)).To(Succeed()) - g.Expect(operator.Status.Conditions).To(HaveLen(2)) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) - g.Expect(cond).ToNot(BeNil()) - g.Expect(cond.Status).To(Equal(metav1.ConditionTrue)) - g.Expect(cond.Reason).To(Equal(operatorv1alpha1.ReasonSuccess)) - g.Expect(cond.Message).To(ContainSubstring("resolved to")) - g.Expect(operator.Status.ResolvedBundleResource).ToNot(BeEmpty()) - }).Should(Succeed()) - - By("eventually installing the package successfully") - Eventually(func(g Gomega) { - g.Expect(c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)).To(Succeed()) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeInstalled) - g.Expect(cond).ToNot(BeNil()) - g.Expect(cond.Status).To(Equal(metav1.ConditionTrue)) - g.Expect(cond.Reason).To(Equal(operatorv1alpha1.ReasonSuccess)) - g.Expect(cond.Message).To(ContainSubstring("installed from")) - g.Expect(operator.Status.InstalledBundleResource).ToNot(BeEmpty()) - - bd := rukpakv1alpha1.BundleDeployment{} - g.Expect(c.Get(ctx, types.NamespacedName{Name: operatorName}, &bd)).To(Succeed()) - hasValidBundle := apimeta.FindStatusCondition(bd.Status.Conditions, rukpakv1alpha1.TypeHasValidBundle) - g.Expect(hasValidBundle).ToNot(BeNil()) - g.Expect(hasValidBundle.Reason).To(Equal(rukpakv1alpha1.ReasonUnpackSuccessful)) - installed := apimeta.FindStatusCondition(bd.Status.Conditions, rukpakv1alpha1.TypeInstalled) - g.Expect(installed).ToNot(BeNil()) - g.Expect(installed.Reason).To(Equal(rukpakv1alpha1.ReasonInstallationSucceeded)) - }).Should(Succeed()) - }) - }) - - It("resolves again when a new catalog is available", func() { - pkgName := "prometheus" - operator.Spec = operatorv1alpha1.OperatorSpec{ - PackageName: pkgName, - } +var pollDuration = time.Minute +var pollInterval = time.Second - By("deleting the catalog first") - Expect(c.Delete(ctx, operatorCatalog)).To(Succeed()) - Eventually(func(g Gomega) { - err := c.Get(ctx, types.NamespacedName{Name: operatorCatalog.Name}, &catalogd.Catalog{}) - g.Expect(errors.IsNotFound(err)).To(BeTrue()) - }).Should(Succeed()) - - By("creating the Operator resource") - Expect(c.Create(ctx, operator)).To(Succeed()) - - By("failing to find Operator during resolution") - Eventually(func(g Gomega) { - g.Expect(c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)).To(Succeed()) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) - g.Expect(cond).ToNot(BeNil()) - g.Expect(cond.Status).To(Equal(metav1.ConditionFalse)) - g.Expect(cond.Reason).To(Equal(operatorv1alpha1.ReasonResolutionFailed)) - g.Expect(cond.Message).To(Equal(fmt.Sprintf("no package %q found", pkgName))) - }).Should(Succeed()) - - By("creating an Operator catalog with the desired package") - var err error - operatorCatalog, err = createTestCatalog(ctx, testCatalogName, os.Getenv(testCatalogRefEnvVar)) - Expect(err).ToNot(HaveOccurred()) - Eventually(func(g Gomega) { - g.Expect(c.Get(ctx, types.NamespacedName{Name: operatorCatalog.Name}, operatorCatalog)).To(Succeed()) - cond := apimeta.FindStatusCondition(operatorCatalog.Status.Conditions, catalogd.TypeUnpacked) - g.Expect(cond).ToNot(BeNil()) - g.Expect(cond.Status).To(Equal(metav1.ConditionTrue)) - g.Expect(cond.Reason).To(Equal(catalogd.ReasonUnpackSuccessful)) - }).Should(Succeed()) - - By("eventually resolving the package successfully") - Eventually(func(g Gomega) { - g.Expect(c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)).To(Succeed()) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) - g.Expect(cond).ToNot(BeNil()) - g.Expect(cond.Status).To(Equal(metav1.ConditionTrue)) - g.Expect(cond.Reason).To(Equal(operatorv1alpha1.ReasonSuccess)) - }).Should(Succeed()) - }) - - When("resolving upgrade edges", func() { - BeforeEach(func() { - By("creating an Operator at a specified version") - operator.Spec = operatorv1alpha1.OperatorSpec{ - PackageName: "prometheus", - Version: "1.0.0", - } - Expect(c.Create(ctx, operator)).To(Succeed()) - By("eventually reporting a successful resolution") - Eventually(func(g Gomega) { - g.Expect(c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)).To(Succeed()) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) - g.Expect(cond).ToNot(BeNil()) - g.Expect(cond.Reason).To(Equal(operatorv1alpha1.ReasonSuccess)) - g.Expect(cond.Message).To(ContainSubstring("resolved to")) - g.Expect(operator.Status.ResolvedBundleResource).To(Equal("localhost/testdata/bundles/registry-v1/prometheus-operator:v1.0.0")) - }).Should(Succeed()) - }) - - It("does not allow to upgrade the Operator to a non-successor version", func() { - By("updating the Operator resource to a non-successor version") - // Semver only allows upgrades within major version at the moment. - operator.Spec.Version = "2.0.0" - Expect(c.Update(ctx, operator)).To(Succeed()) - By("eventually reporting an unsatisfiable resolution") - Eventually(func(g Gomega) { - g.Expect(c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)).To(Succeed()) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) - g.Expect(cond).ToNot(BeNil()) - g.Expect(cond.Reason).To(Equal(operatorv1alpha1.ReasonResolutionFailed)) - g.Expect(cond.Message).To(ContainSubstring("constraints not satisfiable")) - g.Expect(cond.Message).To(MatchRegexp("installed package prometheus requires at least one of test-catalog-prometheus-prometheus-operator.1.2.0, test-catalog-prometheus-prometheus-operator.1.0.1, test-catalog-prometheus-prometheus-operator.1.0.0$")) - g.Expect(operator.Status.ResolvedBundleResource).To(BeEmpty()) - }).Should(Succeed()) - }) - - It("does allow to upgrade the Operator to any of the successor versions within non-zero major version", func() { - By("updating the Operator resource by skipping versions") - // Test catalog has versions between the initial version and new version - operator.Spec.Version = "1.2.0" - Expect(c.Update(ctx, operator)).To(Succeed()) - By("eventually reporting a successful resolution and bundle path") - Eventually(func(g Gomega) { - g.Expect(c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)).To(Succeed()) - cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) - g.Expect(cond).ToNot(BeNil()) - g.Expect(cond.Reason).To(Equal(operatorv1alpha1.ReasonSuccess)) - g.Expect(cond.Message).To(ContainSubstring("resolved to")) - g.Expect(operator.Status.ResolvedBundleResource).To(Equal("localhost/testdata/bundles/registry-v1/prometheus-operator:v1.2.0")) - }).Should(Succeed()) - }) - }) - - AfterEach(func() { - if basePath := env.GetString("ARTIFACT_PATH", ""); basePath != "" { - // get all the artifacts from the test run and save them to the artifact path - getArtifactsOutput(ctx, basePath) - } - Expect(c.Delete(ctx, operator)).To(Succeed()) - Eventually(func(g Gomega) { - err := c.Get(ctx, types.NamespacedName{Name: operator.Name}, &operatorv1alpha1.Operator{}) - g.Expect(errors.IsNotFound(err)).To(BeTrue()) - }).Should(Succeed()) - }) +func testInit(t *testing.T) (*operatorv1alpha1.Operator, string, *catalogd.Catalog) { + var err error + operatorCatalog, err := createTestCatalog(context.Background(), testCatalogName, os.Getenv(testCatalogRefEnvVar)) + require.NoError(t, err) + + operatorName := fmt.Sprintf("operator-%s", rand.String(8)) + operator := &operatorv1alpha1.Operator{ + ObjectMeta: metav1.ObjectMeta{ + Name: operatorName, + }, + } + return operator, operatorName, operatorCatalog +} + +func testCleanup(t *testing.T, cat *catalogd.Catalog, operator *operatorv1alpha1.Operator) { + require.NoError(t, c.Delete(context.Background(), cat)) + require.Eventually(t, func() bool { + err := c.Get(context.Background(), types.NamespacedName{Name: cat.Name}, &catalogd.Catalog{}) + return errors.IsNotFound(err) + }, pollDuration, pollInterval) + require.NoError(t, c.Delete(context.Background(), operator)) + require.Eventually(t, func() bool { + err := c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, &operatorv1alpha1.Operator{}) + return errors.IsNotFound(err) + }, pollDuration, pollInterval) +} + +func TestOperatorInstallRegistry(t *testing.T) { + t.Log("When an operator is installed from an operator catalog") + t.Log("When the operator bundle format is registry+v1") + + operator, operatorName, operatorCatalog := testInit(t) + defer testCleanup(t, operatorCatalog, operator) + defer getArtifactsOutput(t) + + operator.Spec = operatorv1alpha1.OperatorSpec{ + PackageName: "prometheus", + } + t.Log("It resolves the specified package with correct bundle path") + t.Log("By creating the Operator resource") + require.NoError(t, c.Create(context.Background(), operator)) + + t.Log("By eventually reporting a successful resolution and bundle path") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) + assert.Len(ct, operator.Status.Conditions, 2) + cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + assert.Contains(ct, cond.Message, "resolved to") + assert.Equal(ct, "localhost/testdata/bundles/registry-v1/prometheus-operator:v2.0.0", operator.Status.ResolvedBundleResource) + }, pollDuration, pollInterval) + + t.Log("By eventually installing the package successfully") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) + cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeInstalled) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + assert.Contains(ct, cond.Message, "installed from") + assert.NotEmpty(ct, operator.Status.InstalledBundleResource) + + bd := rukpakv1alpha1.BundleDeployment{} + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operatorName}, &bd)) + hasValidBundle := apimeta.FindStatusCondition(bd.Status.Conditions, rukpakv1alpha1.TypeHasValidBundle) + if !assert.NotNil(ct, hasValidBundle) { + return + } + assert.Equal(ct, rukpakv1alpha1.ReasonUnpackSuccessful, hasValidBundle.Reason) + installed := apimeta.FindStatusCondition(bd.Status.Conditions, rukpakv1alpha1.TypeInstalled) + if !assert.NotNil(ct, installed) { + return + } + assert.Equal(ct, rukpakv1alpha1.ReasonInstallationSucceeded, installed.Reason) + }, pollDuration, pollInterval) +} + +func TestOperatorInstallPlain(t *testing.T) { + t.Log("When an operator is installed from an operator catalog") + t.Log("When the operator bundle format is plain+v0") + + operator, operatorName, operatorCatalog := testInit(t) + defer testCleanup(t, operatorCatalog, operator) + defer getArtifactsOutput(t) + + operator.Spec = operatorv1alpha1.OperatorSpec{ + PackageName: "plain", + } + t.Log("It resolves the specified package with correct bundle path") + t.Log("By creating the Operator resource") + require.NoError(t, c.Create(context.Background(), operator)) + + t.Log("By eventually reporting a successful resolution and bundle path") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) + assert.Len(ct, operator.Status.Conditions, 2) + cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + assert.Contains(ct, cond.Message, "resolved to") + assert.NotEmpty(ct, operator.Status.ResolvedBundleResource) + }, pollDuration, pollInterval) + + t.Log("By eventually installing the package successfully") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) + cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeInstalled) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + assert.Contains(ct, cond.Message, "installed from") + assert.NotEmpty(ct, operator.Status.InstalledBundleResource) + + bd := rukpakv1alpha1.BundleDeployment{} + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operatorName}, &bd)) + hasValidBundle := apimeta.FindStatusCondition(bd.Status.Conditions, rukpakv1alpha1.TypeHasValidBundle) + if !assert.NotNil(ct, hasValidBundle) { + return + } + assert.Equal(ct, rukpakv1alpha1.ReasonUnpackSuccessful, hasValidBundle.Reason) + installed := apimeta.FindStatusCondition(bd.Status.Conditions, rukpakv1alpha1.TypeInstalled) + if !assert.NotNil(ct, installed) { + return + } + assert.Equal(ct, rukpakv1alpha1.ReasonInstallationSucceeded, installed.Reason) + }, pollDuration, pollInterval) +} + +func TestOperatorInstallReResolvesWhenNewCatalog(t *testing.T) { + t.Log("When an operator is installed from an operator catalog") + t.Log("It resolves again when a new catalog is available") + + operator, _, operatorCatalog := testInit(t) + defer testCleanup(t, operatorCatalog, operator) + defer getArtifactsOutput(t) + + pkgName := "prometheus" + operator.Spec = operatorv1alpha1.OperatorSpec{ + PackageName: pkgName, + } + + t.Log("By deleting the catalog first") + require.NoError(t, c.Delete(context.Background(), operatorCatalog)) + require.EventuallyWithT(t, func(ct *assert.CollectT) { + err := c.Get(context.Background(), types.NamespacedName{Name: operatorCatalog.Name}, &catalogd.Catalog{}) + assert.True(ct, errors.IsNotFound(err)) + }, pollDuration, pollInterval) + + t.Log("By creating the Operator resource") + require.NoError(t, c.Create(context.Background(), operator)) + + t.Log("By failing to find Operator during resolution") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) + cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionFalse, cond.Status) + assert.Equal(ct, operatorv1alpha1.ReasonResolutionFailed, cond.Reason) + assert.Equal(ct, fmt.Sprintf("no package %q found", pkgName), cond.Message) + }, pollDuration, pollInterval) + + t.Log("By creating an Operator catalog with the desired package") + var err error + operatorCatalog, err = createTestCatalog(context.Background(), testCatalogName, os.Getenv(testCatalogRefEnvVar)) + require.NoError(t, err) + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operatorCatalog.Name}, operatorCatalog)) + cond := apimeta.FindStatusCondition(operatorCatalog.Status.Conditions, catalogd.TypeUnpacked) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, catalogd.ReasonUnpackSuccessful, cond.Reason) + }, pollDuration, pollInterval) + + t.Log("By eventually resolving the package successfully") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) + cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + }, pollDuration, pollInterval) +} - }) -}) +func TestOperatorInstallNonSuccessorVersion(t *testing.T) { + t.Log("When an operator is installed from an operator catalog") + t.Log("When resolving upgrade edges") + + operator, _, operatorCatalog := testInit(t) + defer testCleanup(t, operatorCatalog, operator) + defer getArtifactsOutput(t) + + t.Log("By creating an Operator at a specified version") + operator.Spec = operatorv1alpha1.OperatorSpec{ + PackageName: "prometheus", + Version: "1.0.0", + } + require.NoError(t, c.Create(context.Background(), operator)) + t.Log("By eventually reporting a successful resolution") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) + cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + assert.Contains(ct, cond.Message, "resolved to") + assert.Equal(ct, "localhost/testdata/bundles/registry-v1/prometheus-operator:v1.0.0", operator.Status.ResolvedBundleResource) + }, pollDuration, pollInterval) + + t.Log("It does not allow to upgrade the Operator to a non-successor version") + t.Log("By updating the Operator resource to a non-successor version") + // Semver only allows upgrades within major version at the moment. + operator.Spec.Version = "2.0.0" + require.NoError(t, c.Update(context.Background(), operator)) + t.Log("By eventually reporting an unsatisfiable resolution") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) + cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, operatorv1alpha1.ReasonResolutionFailed, cond.Reason) + assert.Contains(ct, cond.Message, "constraints not satisfiable") + assert.Contains(ct, cond.Message, "installed package prometheus requires at least one of test-catalog-prometheus-prometheus-operator.1.2.0, test-catalog-prometheus-prometheus-operator.1.0.1, test-catalog-prometheus-prometheus-operator.1.0.0") + assert.Empty(ct, operator.Status.ResolvedBundleResource) + }, pollDuration, pollInterval) +} + +func TestOperatorInstallSuccessorVersion(t *testing.T) { + t.Log("When an operator is installed from an operator catalog") + t.Log("When resolving upgrade edges") + operator, _, operatorCatalog := testInit(t) + defer testCleanup(t, operatorCatalog, operator) + defer getArtifactsOutput(t) + + t.Log("By creating an Operator at a specified version") + operator.Spec = operatorv1alpha1.OperatorSpec{ + PackageName: "prometheus", + Version: "1.0.0", + } + require.NoError(t, c.Create(context.Background(), operator)) + t.Log("By eventually reporting a successful resolution") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) + cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + assert.Contains(ct, cond.Message, "resolved to") + assert.Equal(ct, "localhost/testdata/bundles/registry-v1/prometheus-operator:v1.0.0", operator.Status.ResolvedBundleResource) + }, pollDuration, pollInterval) + + t.Log("It does allow to upgrade the Operator to any of the successor versions within non-zero major version") + t.Log("By updating the Operator resource by skipping versions") + // Test catalog has versions between the initial version and new version + operator.Spec.Version = "1.2.0" + require.NoError(t, c.Update(context.Background(), operator)) + t.Log("By eventually reporting a successful resolution and bundle path") + require.EventuallyWithT(t, func(ct *assert.CollectT) { + assert.NoError(ct, c.Get(context.Background(), types.NamespacedName{Name: operator.Name}, operator)) + cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + assert.Contains(ct, cond.Message, "resolved to") + assert.Equal(ct, "localhost/testdata/bundles/registry-v1/prometheus-operator:v1.2.0", operator.Status.ResolvedBundleResource) + }, pollDuration, pollInterval) +} // getArtifactsOutput gets all the artifacts from the test run and saves them to the artifact path. // Currently it saves: @@ -272,90 +333,95 @@ var _ = Describe("Operator Install", func() { // - bundle // - bundledeployments // - catalogsources -func getArtifactsOutput(ctx context.Context, basePath string) { +func getArtifactsOutput(t *testing.T) { + basePath := env.GetString("ARTIFACT_PATH", "") + if basePath == "" { + return + } + kubeClient, err := kubeclient.NewForConfig(cfg) - Expect(err).To(Not(HaveOccurred())) + require.NoError(t, err) // sanitize the artifact name for use as a directory name - testName := strings.ReplaceAll(strings.ToLower(CurrentSpecReport().LeafNodeText), " ", "-") + testName := strings.ReplaceAll(strings.ToLower(t.Name()), " ", "-") // Get the test description and sanitize it for use as a directory name artifactPath := filepath.Join(basePath, artifactName, fmt.Sprint(time.Now().UnixNano()), testName) // Create the full artifact path err = os.MkdirAll(artifactPath, 0755) - Expect(err).To(Not(HaveOccurred())) + require.NoError(t, err) // Get all namespaces namespaces := corev1.NamespaceList{} - if err := c.List(ctx, &namespaces); err != nil { - GinkgoWriter.Printf("Failed to list namespaces %w", err) + if err := c.List(context.Background(), &namespaces); err != nil { + fmt.Printf("Failed to list namespaces %v", err) } // get all operators save them to the artifact path. operators := operatorv1alpha1.OperatorList{} - if err := c.List(ctx, &operators, client.InNamespace("")); err != nil { - GinkgoWriter.Printf("Failed to list operators %w", err) + if err := c.List(context.Background(), &operators, client.InNamespace("")); err != nil { + fmt.Printf("Failed to list operators %v", err) } for _, operator := range operators.Items { // Save operator to artifact path operatorYaml, err := yaml.Marshal(operator) if err != nil { - GinkgoWriter.Printf("Failed to marshal operator %w", err) + fmt.Printf("Failed to marshal operator %v", err) continue } if err := os.WriteFile(filepath.Join(artifactPath, operator.Name+"-operator.yaml"), operatorYaml, 0600); err != nil { - GinkgoWriter.Printf("Failed to write operator to file %w", err) + fmt.Printf("Failed to write operator to file %v", err) } } // get all catalogsources save them to the artifact path. catalogsources := catalogd.CatalogList{} - if err := c.List(ctx, &catalogsources, client.InNamespace("")); err != nil { - GinkgoWriter.Printf("Failed to list catalogsources %w", err) + if err := c.List(context.Background(), &catalogsources, client.InNamespace("")); err != nil { + fmt.Printf("Failed to list catalogsources %v", err) } for _, catalogsource := range catalogsources.Items { // Save catalogsource to artifact path catalogsourceYaml, err := yaml.Marshal(catalogsource) if err != nil { - GinkgoWriter.Printf("Failed to marshal catalogsource %w", err) + fmt.Printf("Failed to marshal catalogsource %v", err) continue } if err := os.WriteFile(filepath.Join(artifactPath, catalogsource.Name+"-catalogsource.yaml"), catalogsourceYaml, 0600); err != nil { - GinkgoWriter.Printf("Failed to write catalogsource to file %w", err) + fmt.Printf("Failed to write catalogsource to file %v", err) } } // Get all Bundles in the namespace and save them to the artifact path. bundles := rukpakv1alpha1.BundleList{} - if err := c.List(ctx, &bundles, client.InNamespace("")); err != nil { - GinkgoWriter.Printf("Failed to list bundles %w", err) + if err := c.List(context.Background(), &bundles, client.InNamespace("")); err != nil { + fmt.Printf("Failed to list bundles %v", err) } for _, bundle := range bundles.Items { // Save bundle to artifact path bundleYaml, err := yaml.Marshal(bundle) if err != nil { - GinkgoWriter.Printf("Failed to marshal bundle %w", err) + fmt.Printf("Failed to marshal bundle %v", err) continue } if err := os.WriteFile(filepath.Join(artifactPath, bundle.Name+"-bundle.yaml"), bundleYaml, 0600); err != nil { - GinkgoWriter.Printf("Failed to write bundle to file %w", err) + fmt.Printf("Failed to write bundle to file %v", err) } } // Get all BundleDeployments in the namespace and save them to the artifact path. bundleDeployments := rukpakv1alpha1.BundleDeploymentList{} - if err := c.List(ctx, &bundleDeployments, client.InNamespace("")); err != nil { - GinkgoWriter.Printf("Failed to list bundleDeployments %w", err) + if err := c.List(context.Background(), &bundleDeployments, client.InNamespace("")); err != nil { + fmt.Printf("Failed to list bundleDeployments %v", err) } for _, bundleDeployment := range bundleDeployments.Items { // Save bundleDeployment to artifact path bundleDeploymentYaml, err := yaml.Marshal(bundleDeployment) if err != nil { - GinkgoWriter.Printf("Failed to marshal bundleDeployment %w", err) + fmt.Printf("Failed to marshal bundleDeployment %v", err) continue } if err := os.WriteFile(filepath.Join(artifactPath, bundleDeployment.Name+"-bundleDeployment.yaml"), bundleDeploymentYaml, 0600); err != nil { - GinkgoWriter.Printf("Failed to write bundleDeployment to file %w", err) + fmt.Printf("Failed to write bundleDeployment to file %v", err) } } @@ -367,14 +433,14 @@ func getArtifactsOutput(ctx context.Context, basePath string) { namespacedArtifactPath := filepath.Join(artifactPath, namespace.Name) if err := os.Mkdir(namespacedArtifactPath, 0755); err != nil { - GinkgoWriter.Printf("Failed to create namespaced artifact path %w", err) + fmt.Printf("Failed to create namespaced artifact path %v", err) continue } // get all deployments in the namespace and save them to the artifact path. deployments := appsv1.DeploymentList{} - if err := c.List(ctx, &deployments, client.InNamespace(namespace.Name)); err != nil { - GinkgoWriter.Printf("Failed to list deployments %w in namespace: %q", err, namespace.Name) + if err := c.List(context.Background(), &deployments, client.InNamespace(namespace.Name)); err != nil { + fmt.Printf("Failed to list deployments %v in namespace: %q", err, namespace.Name) continue } @@ -382,40 +448,40 @@ func getArtifactsOutput(ctx context.Context, basePath string) { // Save deployment to artifact path deploymentYaml, err := yaml.Marshal(deployment) if err != nil { - GinkgoWriter.Printf("Failed to marshal deployment %w", err) + fmt.Printf("Failed to marshal deployment %v", err) continue } if err := os.WriteFile(filepath.Join(namespacedArtifactPath, deployment.Name+"-deployment.yaml"), deploymentYaml, 0600); err != nil { - GinkgoWriter.Printf("Failed to write deployment to file %w", err) + fmt.Printf("Failed to write deployment to file %v", err) } } // Get logs from all pods in all namespaces pods := corev1.PodList{} - if err := c.List(ctx, &pods, client.InNamespace(namespace.Name)); err != nil { - GinkgoWriter.Printf("Failed to list pods %w in namespace: %q", err, namespace.Name) + if err := c.List(context.Background(), &pods, client.InNamespace(namespace.Name)); err != nil { + fmt.Printf("Failed to list pods %v in namespace: %q", err, namespace.Name) } for _, pod := range pods.Items { if pod.Status.Phase != corev1.PodRunning && pod.Status.Phase != corev1.PodSucceeded && pod.Status.Phase != corev1.PodFailed { continue } for _, container := range pod.Spec.Containers { - logs, err := kubeClient.CoreV1().Pods(namespace.Name).GetLogs(pod.Name, &corev1.PodLogOptions{Container: container.Name}).Stream(ctx) + logs, err := kubeClient.CoreV1().Pods(namespace.Name).GetLogs(pod.Name, &corev1.PodLogOptions{Container: container.Name}).Stream(context.Background()) if err != nil { - GinkgoWriter.Printf("Failed to get logs for pod %q in namespace %q: %w", pod.Name, namespace.Name, err) + fmt.Printf("Failed to get logs for pod %q in namespace %q: %v", pod.Name, namespace.Name, err) continue } defer logs.Close() outFile, err := os.Create(filepath.Join(namespacedArtifactPath, pod.Name+"-"+container.Name+"-logs.txt")) if err != nil { - GinkgoWriter.Printf("Failed to create file for pod %q in namespace %q: %w", pod.Name, namespace.Name, err) + fmt.Printf("Failed to create file for pod %q in namespace %q: %v", pod.Name, namespace.Name, err) continue } defer outFile.Close() if _, err := io.Copy(outFile, logs); err != nil { - GinkgoWriter.Printf("Failed to copy logs for pod %q in namespace %q: %w", pod.Name, namespace.Name, err) + fmt.Printf("Failed to copy logs for pod %q in namespace %q: %v", pod.Name, namespace.Name, err) continue } } diff --git a/test/operator-framework-e2e/operator_framework_test.go b/test/operator-framework-e2e/operator_framework_test.go index 3316b4b54..fa7112ab1 100644 --- a/test/operator-framework-e2e/operator_framework_test.go +++ b/test/operator-framework-e2e/operator_framework_test.go @@ -6,125 +6,84 @@ import ( "testing" "time" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" - "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/util/rand" - "k8s.io/client-go/rest" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" operatorv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1" ) -var ( - cfg *rest.Config - c client.Client - ctx context.Context -) - func TestOperatorFramework(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Operator Framework E2E Suite") -} + t.Parallel() + cfg := ctrl.GetConfigOrDie() -var _ = BeforeSuite(func() { - cfg = ctrl.GetConfigOrDie() scheme := runtime.NewScheme() - ctx = context.Background() - err := catalogd.AddToScheme(scheme) - Expect(err).ToNot(HaveOccurred()) + require.NoError(t, catalogd.AddToScheme(scheme)) + require.NoError(t, operatorv1alpha1.AddToScheme(scheme)) - err = operatorv1alpha1.AddToScheme(scheme) - Expect(err).ToNot(HaveOccurred()) + c, err := client.New(cfg, client.Options{Scheme: scheme}) + require.NoError(t, err) - c, err = client.New(cfg, client.Options{Scheme: scheme}) - Expect(err).ToNot(HaveOccurred()) -}) - -var _ = Describe("Operator Framework E2E", func() { - var catalog *catalogd.Catalog - BeforeEach(func() { - catalog = &catalogd.Catalog{ + var operators = []*operatorv1alpha1.Operator{ + { ObjectMeta: metav1.ObjectMeta{ - Name: "catalog" + rand.String(10), + Name: "plainv0", }, - Spec: catalogd.CatalogSpec{ - Source: catalogd.CatalogSource{ - Type: catalogd.SourceTypeImage, - Image: &catalogd.ImageSource{ - Ref: os.Getenv("CATALOG_IMG"), - InsecureSkipTLSVerify: true, - }, - }, + Spec: operatorv1alpha1.OperatorSpec{ + PackageName: os.Getenv("PLAIN_PKG_NAME"), }, - } - Expect(c.Create(ctx, catalog)).NotTo(HaveOccurred()) - }) - When("Creating an Operator that references a package with a plain+v0 bundle type", func() { - var operator *operatorv1alpha1.Operator - BeforeEach(func() { - operator = &operatorv1alpha1.Operator{ - ObjectMeta: metav1.ObjectMeta{ - Name: "plainv0", - }, - Spec: operatorv1alpha1.OperatorSpec{ - PackageName: os.Getenv("PLAIN_PKG_NAME"), - }, - } + }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "registryv1", + }, + Spec: operatorv1alpha1.OperatorSpec{ + PackageName: os.Getenv("REG_PKG_NAME"), + }, + }, + } - Expect(c.Create(ctx, operator)).NotTo(HaveOccurred()) - }) - It("should have a status condition type of Installed with a status of True and a reason of Success", func() { - Eventually(func(g Gomega) { - op := &operatorv1alpha1.Operator{} - g.Expect(c.Get(ctx, client.ObjectKeyFromObject(operator), op)).NotTo(HaveOccurred()) - cond := meta.FindStatusCondition(op.Status.Conditions, operatorv1alpha1.TypeInstalled) - g.Expect(cond).NotTo(BeNil()) - g.Expect(cond.Status).Should(Equal(metav1.ConditionTrue)) - g.Expect(cond.Reason).Should(Equal(operatorv1alpha1.ReasonSuccess)) - }).WithTimeout(2 * time.Minute).Should(Succeed()) - }) - AfterEach(func() { - Expect(c.Delete(ctx, operator)).NotTo(HaveOccurred()) - }) - }) - When("Creating an Operator that references a package with a registry+v1 bundle type", func() { - var operator *operatorv1alpha1.Operator - BeforeEach(func() { - operator = &operatorv1alpha1.Operator{ + for _, op := range operators { + operator := op + t.Run(operator.ObjectMeta.Name, func(t *testing.T) { + t.Parallel() + catalog := &catalogd.Catalog{ ObjectMeta: metav1.ObjectMeta{ - Name: "registryv1", + GenerateName: "catalog", }, - Spec: operatorv1alpha1.OperatorSpec{ - PackageName: os.Getenv("REG_PKG_NAME"), + Spec: catalogd.CatalogSpec{ + Source: catalogd.CatalogSource{ + Type: catalogd.SourceTypeImage, + Image: &catalogd.ImageSource{ + Ref: os.Getenv("CATALOG_IMG"), + InsecureSkipTLSVerify: true, + }, + }, }, } - - Expect(c.Create(ctx, operator)).NotTo(HaveOccurred()) - }) - It("should have a status condition type of Installed with a status of True and a reason of Success", func() { - Eventually(func(g Gomega) { + t.Logf("When creating an Operator that references a package with a %q bundle type", operator.ObjectMeta.Name) + require.NoError(t, c.Create(context.Background(), catalog)) + require.NoError(t, c.Create(context.Background(), operator)) + t.Log("It should have a status condition type of Installed with a status of True and a reason of Success") + require.EventuallyWithT(t, func(ct *assert.CollectT) { op := &operatorv1alpha1.Operator{} - g.Expect(c.Get(ctx, client.ObjectKeyFromObject(operator), op)).NotTo(HaveOccurred()) + assert.NoError(ct, c.Get(context.Background(), client.ObjectKeyFromObject(operator), op)) cond := meta.FindStatusCondition(op.Status.Conditions, operatorv1alpha1.TypeInstalled) - g.Expect(cond).NotTo(BeNil()) - g.Expect(cond.Status).Should(Equal(metav1.ConditionTrue)) - g.Expect(cond.Reason).Should(Equal(operatorv1alpha1.ReasonSuccess)) - }).WithTimeout(2 * time.Minute).Should(Succeed()) + if !assert.NotNil(ct, cond) { + return + } + assert.Equal(ct, metav1.ConditionTrue, cond.Status) + assert.Equal(ct, operatorv1alpha1.ReasonSuccess, cond.Reason) + }, 2*time.Minute, time.Second) + require.NoError(t, c.Delete(context.Background(), catalog)) + require.NoError(t, c.Delete(context.Background(), operator)) }) - AfterEach(func() { - Expect(c.Delete(ctx, operator)).NotTo(HaveOccurred()) - }) - }) - - AfterEach(func() { - Expect(c.Delete(ctx, catalog)).NotTo(HaveOccurred()) - }) -}) + } +} diff --git a/test/operator-framework-e2e/setup.sh b/test/operator-framework-e2e/setup.sh index 9097b3b94..c2f42c3e6 100755 --- a/test/operator-framework-e2e/setup.sh +++ b/test/operator-framework-e2e/setup.sh @@ -56,7 +56,7 @@ fi # We're going to do file manipulation, so let's work in a temp dir TMP_ROOT="$(mktemp -d ./tmp.XXXXXX)" # Make sure to delete the temp dir when we exit -trap 'rm -rf ${TMP_ROOT}' EXIT +trap 'chmod -R +w ${TMP_ROOT} && rm -rf ${TMP_ROOT}' EXIT DOMAIN=oc-opdev-e2e.operatorframework.io REG_DIR="${TMP_ROOT}/registry" @@ -262,4 +262,4 @@ kubectl wait --for=condition=Complete -n "${namespace}" jobs/kaniko --timeout=60 # don't have write permissions so they can't be removed unless # we ensure they have the write permissions chmod -R +w "${REG_DIR}/bin" -chmod -R +w "${PLAIN_DIR}/bin" \ No newline at end of file +chmod -R +w "${PLAIN_DIR}/bin"