Skip to content

Commit

Permalink
rebase on an early commit of operator-framework#250 and implement e2e…
Browse files Browse the repository at this point in the history
… tests

Signed-off-by: Bryce Palmer <bpalmer@redhat.com>
  • Loading branch information
everettraven committed Jun 5, 2023
1 parent 97c8227 commit fc7729e
Show file tree
Hide file tree
Showing 5 changed files with 294 additions and 123 deletions.
4 changes: 3 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -99,8 +99,10 @@ kind-cluster-cleanup: kind ## Delete the kind cluster

kind-load-test-artifacts: kind ## Load the e2e testdata container images into a kind cluster
$(CONTAINER_RUNTIME) build $(TESTDATA_DIR)/bundles/registry-v1/prometheus-operator.v0.47.0 -t localhost/testdata/bundles/registry-v1/prometheus-operator:v0.47.0
$(CONTAINER_RUNTIME) build $(TESTDATA_DIR)/bundles/plain-v0/plain.v0.1.0 -t localhost/testdata/bundles/plain-v0/plain:v0.1.0
$(CONTAINER_RUNTIME) build $(TESTDATA_DIR)/catalogs -f $(TESTDATA_DIR)/catalogs/test-catalog.Dockerfile -t localhost/testdata/catalogs/test-catalog:e2e
$(KIND) load docker-image localhost/testdata/bundles/registry-v1/prometheus-operator:v0.47.0 --name $(KIND_CLUSTER_NAME)
$(KIND) load docker-image localhost/testdata/bundles/plain-v0/plain:v0.1.0 --name $(KIND_CLUSTER_NAME)
$(KIND) load docker-image localhost/testdata/catalogs/test-catalog:e2e --name $(KIND_CLUSTER_NAME)

##@ Build
Expand Down Expand Up @@ -195,7 +197,7 @@ CONTROLLER_TOOLS_VERSION ?= v0.10.0
.PHONY: kind
kind: $(KIND) ## Download kind locally if necessary.
$(KIND): $(LOCALBIN)
test -s $(LOCALBIN)/kind || GOBIN=$(LOCALBIN) go install sigs.k8s.io/kind@v0.15.0
test -s $(LOCALBIN)/kind || GOBIN=$(LOCALBIN) go install sigs.k8s.io/kind@v0.19.0

.PHONY: ginkgo
ginkgo: $(GINKGO) ## Download ginkgo locally if necessary.
Expand Down
116 changes: 116 additions & 0 deletions internal/controllers/operator_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,108 @@ var _ = Describe("Operator Controller Test", func() {
err := cl.Delete(ctx, operator)
Expect(err).To(Not(HaveOccurred()))
})
When("the operator specifies a package with a plain+v0 bundle", func() {
var pkgName string
var pkgVer string
var pkgChan string
BeforeEach(func() {
By("initializing cluster state")
pkgName = "plain"
pkgVer = "0.1.0"
pkgChan = "beta"
operator = &operatorsv1alpha1.Operator{
ObjectMeta: metav1.ObjectMeta{Name: opKey.Name},
Spec: operatorsv1alpha1.OperatorSpec{
PackageName: pkgName,
Version: pkgVer,
Channel: pkgChan,
},
}
err := cl.Create(ctx, operator)
Expect(err).NotTo(HaveOccurred())
})
It("sets resolution success status", func() {
By("running reconcile")
res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey})
Expect(res).To(Equal(ctrl.Result{}))
Expect(err).NotTo(HaveOccurred())

By("fetching updated operator after reconcile")
Expect(cl.Get(ctx, opKey, operator)).NotTo(HaveOccurred())

By("Checking the status fields")
Expect(operator.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhub/plain@sha256:plain"))
Expect(operator.Status.InstalledBundleResource).To(Equal(""))

By("checking the expected conditions")
cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved)
Expect(cond).NotTo(BeNil())
Expect(cond.Status).To(Equal(metav1.ConditionTrue))
Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonSuccess))
Expect(cond.Message).To(Equal("resolved to \"quay.io/operatorhub/plain@sha256:plain\""))
cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled)
Expect(cond).NotTo(BeNil())
Expect(cond.Status).To(Equal(metav1.ConditionUnknown))
Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown))
Expect(cond.Message).To(Equal("bundledeployment status is unknown"))

By("fetching the bundled deployment")
bd := &rukpakv1alpha1.BundleDeployment{}
Expect(cl.Get(ctx, types.NamespacedName{Name: opKey.Name}, bd)).NotTo(HaveOccurred())
Expect(bd.Spec.ProvisionerClassName).To(Equal("core-rukpak-io-plain"))
Expect(bd.Spec.Template.Spec.ProvisionerClassName).To(Equal("core-rukpak-io-plain"))
Expect(bd.Spec.Template.Spec.Source.Type).To(Equal(rukpakv1alpha1.SourceTypeImage))
Expect(bd.Spec.Template.Spec.Source.Image).NotTo(BeNil())
Expect(bd.Spec.Template.Spec.Source.Image.Ref).To(Equal("quay.io/operatorhub/plain@sha256:plain"))
})
})
When("the operator specifies a package with a bade bundle mediatype", func() {
var pkgName string
var pkgVer string
var pkgChan string
BeforeEach(func() {
By("initializing cluster state")
pkgName = "badmedia"
pkgVer = "0.1.0"
pkgChan = "beta"
operator = &operatorsv1alpha1.Operator{
ObjectMeta: metav1.ObjectMeta{Name: opKey.Name},
Spec: operatorsv1alpha1.OperatorSpec{
PackageName: pkgName,
Version: pkgVer,
Channel: pkgChan,
},
}
err := cl.Create(ctx, operator)
Expect(err).NotTo(HaveOccurred())
})
It("sets resolution success status", func() {
By("running reconcile")
res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey})
Expect(res).To(Equal(ctrl.Result{}))
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(Equal("unknown bundle mediatype: badmedia+v1"))

By("fetching updated operator after reconcile")
Expect(cl.Get(ctx, opKey, operator)).NotTo(HaveOccurred())

By("Checking the status fields")
Expect(operator.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhub/badmedia@sha256:badmedia"))
Expect(operator.Status.InstalledBundleResource).To(Equal(""))

By("checking the expected conditions")
cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved)
Expect(cond).NotTo(BeNil())
Expect(cond.Status).To(Equal(metav1.ConditionTrue))
Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonSuccess))
Expect(cond.Message).To(Equal("resolved to \"quay.io/operatorhub/badmedia@sha256:badmedia\""))
cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled)
Expect(cond).NotTo(BeNil())
Expect(cond.Status).To(Equal(metav1.ConditionFalse))
Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationFailed))
Expect(cond.Message).To(Equal("unknown bundle mediatype: badmedia+v1"))
})
})
})
When("an invalid semver is provided that bypasses the regex validation", func() {
var (
Expand Down Expand Up @@ -1061,4 +1163,18 @@ var testEntitySource = input.NewCacheQuerier(map[deppy.Identifier]input.Entity{
"olm.package": `{"packageName":"badimage","version":"0.1.0"}`,
"olm.gvk": `[]`,
}),
"operatorhub/plain/0.1.0": *input.NewEntity("operatorhub/plain/0.1.0", map[string]string{
"olm.bundle.path": `"quay.io/operatorhub/plain@sha256:plain"`,
"olm.channel": `{"channelName":"beta","priority":0}`,
"olm.package": `{"packageName":"plain","version":"0.1.0"}`,
"olm.gvk": `[]`,
"olm.bundle.mediatype": `"plain+v0"`,
}),
"operatorhub/badmedia/0.1.0": *input.NewEntity("operatorhub/badmedia/0.1.0", map[string]string{
"olm.bundle.path": `"quay.io/operatorhub/badmedia@sha256:badmedia"`,
"olm.channel": `{"channelName":"beta","priority":0}`,
"olm.package": `{"packageName":"badmedia","version":"0.1.0"}`,
"olm.gvk": `[]`,
"olm.bundle.mediatype": `"badmedia+v1"`,
}),
})
3 changes: 3 additions & 0 deletions internal/resolution/entitysources/catalogdsource.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (

"github.com/operator-framework/deppy/pkg/deppy"
"github.com/operator-framework/deppy/pkg/deppy/input"
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/entity"
"github.com/operator-framework/operator-registry/alpha/property"
"sigs.k8s.io/controller-runtime/pkg/client"

Expand Down Expand Up @@ -86,6 +87,8 @@ func getEntities(ctx context.Context, client client.Client) (input.EntityList, e
// this is already a json marshalled object, so it doesn't need to be marshalled
// like the other ones
props[property.TypePackage] = string(prop.Value)
case entity.PropertyBundleMediaType:
props[entity.PropertyBundleMediaType] = string(prop.Value)
}
}

Expand Down
142 changes: 124 additions & 18 deletions test/e2e/install_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,25 +29,17 @@ var _ = Describe("Operator Install", func() {
ctx context.Context
pkgName string
operatorName string
catalogName string
operator *operatorv1alpha1.Operator
operatorCatalog *catalogd.Catalog
)
When("An operator is installed from an operator catalog", func() {
BeforeEach(func() {
ctx = context.Background()
pkgName = "prometheus"
operatorName = fmt.Sprintf("operator-%s", rand.String(8))
operator = &operatorv1alpha1.Operator{
ObjectMeta: metav1.ObjectMeta{
Name: operatorName,
},
Spec: operatorv1alpha1.OperatorSpec{
PackageName: pkgName,
},
}
catalogName = fmt.Sprintf("catalog-%s", rand.String(8))
operatorCatalog = &catalogd.Catalog{
ObjectMeta: metav1.ObjectMeta{
Name: testCatalogName,
Name: catalogName,
},
Spec: catalogd.CatalogSpec{
Source: catalogd.CatalogSource{
Expand Down Expand Up @@ -85,14 +77,14 @@ var _ = Describe("Operator Install", func() {
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())
}).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed())
g.Expect(cond.Message).To(ContainSubstring("successfully unpacked the catalog image"))

By("eventually installing the package successfully")
Eventually(func(g Gomega) {
err = c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)
// For some reason the above condition check is returning true and the
// Operators end up being created before the packages exist. Adding this check
// to ensure that there are some packages that exist before actually returning from this
// check.
pkgList := &catalogd.PackageList{}
err = c.List(ctx, pkgList)
g.Expect(err).ToNot(HaveOccurred())
cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeInstalled)
g.Expect(cond).ToNot(BeNil())
Expand Down Expand Up @@ -159,6 +151,7 @@ var _ = Describe("Operator Install", func() {
g.Expect(cond.Reason).To(Equal(operatorv1alpha1.ReasonSuccess))
}).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed())
})

AfterEach(func() {
err := c.Delete(ctx, operator)
Expect(err).ToNot(HaveOccurred())
Expand Down Expand Up @@ -196,5 +189,118 @@ var _ = Describe("Operator Install", func() {
Expect(errors.IsNotFound(err)).To(BeTrue())
}).WithTimeout(5 * time.Minute).WithPolling(defaultPoll).Should(Succeed())
})

When("the operator bundle format is registry+v1", func() {
BeforeEach(func() {
pkgName = "prometheus"
operatorName = fmt.Sprintf("operator-%s", rand.String(8))
operator = &operatorv1alpha1.Operator{
ObjectMeta: metav1.ObjectMeta{
Name: operatorName,
},
Spec: operatorv1alpha1.OperatorSpec{
PackageName: pkgName,
},
}
})
It("resolves the specified package with correct bundle path", func() {
By("creating the Operator resource")
err := c.Create(ctx, operator)
Expect(err).ToNot(HaveOccurred())

By("eventually reporting a successful resolution and bundle path")
Eventually(func(g Gomega) {
err = c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(len(operator.Status.Conditions)).To(Equal(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())
}).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed())

By("eventually installing the package successfully")
Eventually(func(g Gomega) {
err = c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)
g.Expect(err).ToNot(HaveOccurred())
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{}
err = c.Get(ctx, types.NamespacedName{Name: operatorName}, &bd)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(len(bd.Status.Conditions)).To(Equal(2))
g.Expect(bd.Status.Conditions[0].Reason).To(Equal("UnpackSuccessful"))
g.Expect(bd.Status.Conditions[1].Reason).To(Equal("InstallationSucceeded"))
}).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed())

})
AfterEach(func() {
err := c.Delete(ctx, operator)
Expect(err).ToNot(HaveOccurred())
})
})

When("the operator bundle format is plain+v0", func() {
BeforeEach(func() {
pkgName = "plain"
operatorName = fmt.Sprintf("operator-%s", rand.String(8))
operator = &operatorv1alpha1.Operator{
ObjectMeta: metav1.ObjectMeta{
Name: operatorName,
},
Spec: operatorv1alpha1.OperatorSpec{
PackageName: pkgName,
},
}
})
It("resolves the specified package with correct bundle path", func() {
By("creating the Operator resource")
err := c.Create(ctx, operator)
Expect(err).ToNot(HaveOccurred())

By("eventually reporting a successful resolution and bundle path")
Eventually(func(g Gomega) {
err = c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(len(operator.Status.Conditions)).To(Equal(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())
}).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed())

By("eventually installing the package successfully")
Eventually(func(g Gomega) {
err = c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)
g.Expect(err).ToNot(HaveOccurred())
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{}
err = c.Get(ctx, types.NamespacedName{Name: operatorName}, &bd)
g.Expect(err).ToNot(HaveOccurred())
g.Expect(len(bd.Status.Conditions)).To(Equal(2))
g.Expect(bd.Status.Conditions[0].Reason).To(Equal("UnpackSuccessful"))
g.Expect(bd.Status.Conditions[1].Reason).To(Equal("InstallationSucceeded"))
}).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed())

})
AfterEach(func() {
err := c.Delete(ctx, operator)
Expect(err).ToNot(HaveOccurred())
})
})

})
})
Loading

0 comments on commit fc7729e

Please sign in to comment.