Skip to content

Commit

Permalink
operator_controller_test: ensure we cleanup after each spec
Browse files Browse the repository at this point in the history
Signed-off-by: Joe Lanford <joe.lanford@gmail.com>
  • Loading branch information
joelanford committed Jun 8, 2023
1 parent 3d30ad0 commit 623d98b
Show file tree
Hide file tree
Showing 3 changed files with 225 additions and 125 deletions.
188 changes: 63 additions & 125 deletions internal/controllers/operator_controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,11 @@ var _ = Describe("Operator Controller Test", func() {
BeforeEach(func() {
opKey = types.NamespacedName{Name: fmt.Sprintf("operator-test-%s", rand.String(8))}
})
AfterEach(func() {
verifyInvariants(ctx, reconciler.Client, operator)
Expect(cl.DeleteAllOf(ctx, &operatorsv1alpha1.Operator{})).To(Succeed())
Expect(cl.DeleteAllOf(ctx, &rukpakv1alpha1.BundleDeployment{})).To(Succeed())
})
When("the operator specifies a non-existent package", func() {
var pkgName string
BeforeEach(func() {
Expand Down Expand Up @@ -137,7 +142,6 @@ var _ = Describe("Operator Controller Test", func() {
err := cl.Create(ctx, operator)
Expect(err).NotTo(HaveOccurred())
})

When("the BundleDeployment does not exist", func() {
BeforeEach(func() {
By("running reconcile")
Expand Down Expand Up @@ -210,16 +214,16 @@ var _ = Describe("Operator Controller Test", func() {
},
},
}

})

When("the BundleDeployment spec is out of date", func() {
It("results in the expected BundleDeployment", func() {
BeforeEach(func() {
By("modifying the BD spec and creating the object")
bd.Spec.ProvisionerClassName = "core-rukpak-io-helm"
err := cl.Create(ctx, bd)
Expect(err).NotTo(HaveOccurred())

})
It("results in the expected BundleDeployment", func() {
By("running reconcile")
res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey})
Expect(res).To(Equal(ctrl.Result{}))
Expand Down Expand Up @@ -508,17 +512,12 @@ var _ = Describe("Operator Controller Test", func() {
})

})

AfterEach(func() {
err := cl.Delete(ctx, bd)
Expect(err).NotTo(HaveOccurred())
})

})
When("an out-of-date BundleDeployment exists", func() {
var bd *rukpakv1alpha1.BundleDeployment
BeforeEach(func() {
By("creating the expected BD")
err := cl.Create(ctx, &rukpakv1alpha1.BundleDeployment{
bd = &rukpakv1alpha1.BundleDeployment{
ObjectMeta: metav1.ObjectMeta{Name: opKey.Name},
Spec: rukpakv1alpha1.BundleDeploymentSpec{
ProvisionerClassName: "foo",
Expand All @@ -534,7 +533,8 @@ var _ = Describe("Operator Controller Test", func() {
},
},
},
})
}
err := cl.Create(ctx, bd)
Expect(err).NotTo(HaveOccurred())

By("running reconcile")
Expand Down Expand Up @@ -633,12 +633,6 @@ var _ = Describe("Operator Controller Test", func() {
err = cl.Create(ctx, operator)
Expect(err).NotTo(HaveOccurred())
})

AfterEach(func() {
err := cl.Delete(ctx, dupOperator)
Expect(err).NotTo(HaveOccurred())
})

It("sets resolution failure status", func() {
By("running reconcile")
res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey})
Expand All @@ -665,51 +659,6 @@ var _ = Describe("Operator Controller Test", func() {
Expect(cond.Message).To(Equal("installation has not been attempted as resolution failed"))
})
})
When("the existing operator status is based on bundleDeployment", func() {
const pkgName = "prometheus"
var (
bd *rukpakv1alpha1.BundleDeployment
)
BeforeEach(func() {
By("creating the expected BundleDeployment")
bd = &rukpakv1alpha1.BundleDeployment{
ObjectMeta: metav1.ObjectMeta{Name: opKey.Name},
Spec: rukpakv1alpha1.BundleDeploymentSpec{
ProvisionerClassName: "core-rukpak-io-plain",
Template: &rukpakv1alpha1.BundleTemplate{
Spec: rukpakv1alpha1.BundleSpec{
ProvisionerClassName: "core-rukpak-io-registry",
Source: rukpakv1alpha1.BundleSource{
Type: rukpakv1alpha1.SourceTypeImage,
Image: &rukpakv1alpha1.ImageSource{
Ref: "quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed",
},
},
},
},
},
}
err := cl.Create(ctx, bd)
Expect(err).NotTo(HaveOccurred())

By("creating the operator object")
operator = &operatorsv1alpha1.Operator{
ObjectMeta: metav1.ObjectMeta{Name: opKey.Name},
Spec: operatorsv1alpha1.OperatorSpec{
PackageName: pkgName,
},
}
err = cl.Create(ctx, operator)
Expect(err).NotTo(HaveOccurred())

})

AfterEach(func() {
err := cl.Delete(ctx, bd)
Expect(err).NotTo(HaveOccurred())
})

})
When("the operator specifies a channel with version that exist", func() {
var pkgName string
var pkgVer string
Expand Down Expand Up @@ -954,75 +903,64 @@ var _ = Describe("Operator Controller Test", func() {
Expect(cond.Message).To(Equal("installation has not been attempted as resolution failed"))
})
})
AfterEach(func() {
verifyInvariants(ctx, operator)
When("an invalid semver is provided that bypasses the regex validation", func() {
var (
pkgName string
fakeClient client.Client
)
BeforeEach(func() {
opKey = types.NamespacedName{Name: fmt.Sprintf("operator-validation-test-%s", rand.String(8))}

err := cl.Delete(ctx, operator)
Expect(err).To(Not(HaveOccurred()))
})
})
When("an invalid semver is provided that bypasses the regex validation", func() {
var (
operator *operatorsv1alpha1.Operator
opKey types.NamespacedName
pkgName string
fakeClient client.Client
)
BeforeEach(func() {
opKey = types.NamespacedName{Name: fmt.Sprintf("operator-validation-test-%s", rand.String(8))}

By("injecting creating a client with the bad operator CR")
pkgName = fmt.Sprintf("exists-%s", rand.String(6))
operator = &operatorsv1alpha1.Operator{
ObjectMeta: metav1.ObjectMeta{Name: opKey.Name},
Spec: operatorsv1alpha1.OperatorSpec{
PackageName: pkgName,
Version: "1.2.3-123abc_def", // bad semver that matches the regex on the CR validation
},
}

// this bypasses client/server-side CR validation and allows us to test the reconciler's validation
fakeClient = fake.NewClientBuilder().WithScheme(sch).WithObjects(operator).Build()

By("changing the reconciler client to the fake client")
reconciler.Client = fakeClient
})
AfterEach(func() {
By("changing the reconciler client back to the real client")
reconciler.Client = cl
})
By("injecting creating a client with the bad operator CR")
pkgName = fmt.Sprintf("exists-%s", rand.String(6))
operator = &operatorsv1alpha1.Operator{
ObjectMeta: metav1.ObjectMeta{Name: opKey.Name},
Spec: operatorsv1alpha1.OperatorSpec{
PackageName: pkgName,
Version: "1.2.3-123abc_def", // bad semver that matches the regex on the CR validation
},
}

It("should add an invalid spec condition and *not* re-enqueue for reconciliation", func() {
By("running reconcile")
res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey})
Expect(res).To(Equal(ctrl.Result{}))
Expect(err).ToNot(HaveOccurred())

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

By("Checking the status fields")
Expect(operator.Status.ResolvedBundleResource).To(Equal(""))
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.ConditionUnknown))
Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonResolutionUnknown))
Expect(cond.Message).To(Equal("validation has not been attempted as spec is invalid"))
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("installation has not been attempted as spec is invalid"))
// this bypasses client/server-side CR validation and allows us to test the reconciler's validation
fakeClient = fake.NewClientBuilder().WithScheme(sch).WithObjects(operator).Build()

By("changing the reconciler client to the fake client")
reconciler.Client = fakeClient
})

It("should add an invalid spec condition and *not* re-enqueue for reconciliation", func() {
By("running reconcile")
res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey})
Expect(res).To(Equal(ctrl.Result{}))
Expect(err).ToNot(HaveOccurred())

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

By("Checking the status fields")
Expect(operator.Status.ResolvedBundleResource).To(Equal(""))
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.ConditionUnknown))
Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonResolutionUnknown))
Expect(cond.Message).To(Equal("validation has not been attempted as spec is invalid"))
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("installation has not been attempted as spec is invalid"))
})
})

})
})

func verifyInvariants(ctx context.Context, op *operatorsv1alpha1.Operator) {
func verifyInvariants(ctx context.Context, c client.Client, op *operatorsv1alpha1.Operator) {
key := client.ObjectKeyFromObject(op)
err := cl.Get(ctx, key, op)
err := c.Get(ctx, key, op)
Expect(err).To(BeNil())

verifyConditionsInvariants(op)
Expand Down
28 changes: 28 additions & 0 deletions internal/controllers/suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,15 @@ limitations under the License.
package controllers_test

import (
"context"
"path/filepath"
"testing"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
catalogd "github.com/operator-framework/catalogd/pkg/apis/core/v1beta1"
rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/rest"
"sigs.k8s.io/controller-runtime/pkg/client"
Expand Down Expand Up @@ -70,14 +73,39 @@ var _ = BeforeSuite(func() {
Expect(err).NotTo(HaveOccurred())
err = rukpakv1alpha1.AddToScheme(sch)
Expect(err).NotTo(HaveOccurred())
err = catalogd.AddToScheme(sch)
Expect(err).NotTo(HaveOccurred())

cl, err = client.New(cfg, client.Options{Scheme: sch})
Expect(err).NotTo(HaveOccurred())
Expect(cl).NotTo(BeNil())
})

var _ = AfterSuite(func() {
var catalogs catalogd.CatalogList
var operators operatorsv1alpha1.OperatorList
var bundleDeployments rukpakv1alpha1.BundleDeploymentList

Expect(cl.List(context.Background(), &catalogs)).To(Succeed())
Expect(cl.List(context.Background(), &operators)).To(Succeed())
Expect(cl.List(context.Background(), &bundleDeployments)).To(Succeed())

Expect(namesFromList(&catalogs)).To(BeEmpty(), "catalogs left in the cluster")
Expect(namesFromList(&operators)).To(BeEmpty(), "operators left in the cluster")
Expect(namesFromList(&bundleDeployments)).To(BeEmpty(), "bundle deployments left in the cluster")

By("tearing down the test environment")
err := testEnv.Stop()
Expect(err).NotTo(HaveOccurred())
})

func namesFromList(list client.ObjectList) []string {
var names []string

items, err := meta.ExtractList(list)
Expect(err).NotTo(HaveOccurred())
for _, item := range items {
names = append(names, item.(client.Object).GetName())
}
return names
}
Loading

0 comments on commit 623d98b

Please sign in to comment.