Skip to content

Commit a49efb5

Browse files
committed
rebase on an early commit of #250 and implement e2e tests
Signed-off-by: Bryce Palmer <bpalmer@redhat.com>
1 parent 18046bc commit a49efb5

File tree

6 files changed

+299
-169
lines changed

6 files changed

+299
-169
lines changed

Makefile

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,10 @@ kind-cluster-cleanup: kind ## Delete the kind cluster
9999

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

106108
##@ Build
@@ -195,7 +197,7 @@ CONTROLLER_TOOLS_VERSION ?= v0.10.0
195197
.PHONY: kind
196198
kind: $(KIND) ## Download kind locally if necessary.
197199
$(KIND): $(LOCALBIN)
198-
test -s $(LOCALBIN)/kind || GOBIN=$(LOCALBIN) go install sigs.k8s.io/kind@v0.15.0
200+
test -s $(LOCALBIN)/kind || GOBIN=$(LOCALBIN) go install sigs.k8s.io/kind@v0.19.0
199201

200202
.PHONY: ginkgo
201203
ginkgo: $(GINKGO) ## Download ginkgo locally if necessary.

internal/controllers/operator_controller_test.go

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -960,6 +960,108 @@ var _ = Describe("Operator Controller Test", func() {
960960
err := cl.Delete(ctx, operator)
961961
Expect(err).To(Not(HaveOccurred()))
962962
})
963+
When("the operator specifies a package with a plain+v0 bundle", func() {
964+
var pkgName string
965+
var pkgVer string
966+
var pkgChan string
967+
BeforeEach(func() {
968+
By("initializing cluster state")
969+
pkgName = "plain"
970+
pkgVer = "0.1.0"
971+
pkgChan = "beta"
972+
operator = &operatorsv1alpha1.Operator{
973+
ObjectMeta: metav1.ObjectMeta{Name: opKey.Name},
974+
Spec: operatorsv1alpha1.OperatorSpec{
975+
PackageName: pkgName,
976+
Version: pkgVer,
977+
Channel: pkgChan,
978+
},
979+
}
980+
err := cl.Create(ctx, operator)
981+
Expect(err).NotTo(HaveOccurred())
982+
})
983+
It("sets resolution success status", func() {
984+
By("running reconcile")
985+
res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey})
986+
Expect(res).To(Equal(ctrl.Result{}))
987+
Expect(err).NotTo(HaveOccurred())
988+
989+
By("fetching updated operator after reconcile")
990+
Expect(cl.Get(ctx, opKey, operator)).NotTo(HaveOccurred())
991+
992+
By("Checking the status fields")
993+
Expect(operator.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhub/plain@sha256:plain"))
994+
Expect(operator.Status.InstalledBundleResource).To(Equal(""))
995+
996+
By("checking the expected conditions")
997+
cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved)
998+
Expect(cond).NotTo(BeNil())
999+
Expect(cond.Status).To(Equal(metav1.ConditionTrue))
1000+
Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonSuccess))
1001+
Expect(cond.Message).To(Equal("resolved to \"quay.io/operatorhub/plain@sha256:plain\""))
1002+
cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled)
1003+
Expect(cond).NotTo(BeNil())
1004+
Expect(cond.Status).To(Equal(metav1.ConditionUnknown))
1005+
Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationStatusUnknown))
1006+
Expect(cond.Message).To(Equal("bundledeployment status is unknown"))
1007+
1008+
By("fetching the bundled deployment")
1009+
bd := &rukpakv1alpha1.BundleDeployment{}
1010+
Expect(cl.Get(ctx, types.NamespacedName{Name: opKey.Name}, bd)).NotTo(HaveOccurred())
1011+
Expect(bd.Spec.ProvisionerClassName).To(Equal("core-rukpak-io-plain"))
1012+
Expect(bd.Spec.Template.Spec.ProvisionerClassName).To(Equal("core-rukpak-io-plain"))
1013+
Expect(bd.Spec.Template.Spec.Source.Type).To(Equal(rukpakv1alpha1.SourceTypeImage))
1014+
Expect(bd.Spec.Template.Spec.Source.Image).NotTo(BeNil())
1015+
Expect(bd.Spec.Template.Spec.Source.Image.Ref).To(Equal("quay.io/operatorhub/plain@sha256:plain"))
1016+
})
1017+
})
1018+
When("the operator specifies a package with a bade bundle mediatype", func() {
1019+
var pkgName string
1020+
var pkgVer string
1021+
var pkgChan string
1022+
BeforeEach(func() {
1023+
By("initializing cluster state")
1024+
pkgName = "badmedia"
1025+
pkgVer = "0.1.0"
1026+
pkgChan = "beta"
1027+
operator = &operatorsv1alpha1.Operator{
1028+
ObjectMeta: metav1.ObjectMeta{Name: opKey.Name},
1029+
Spec: operatorsv1alpha1.OperatorSpec{
1030+
PackageName: pkgName,
1031+
Version: pkgVer,
1032+
Channel: pkgChan,
1033+
},
1034+
}
1035+
err := cl.Create(ctx, operator)
1036+
Expect(err).NotTo(HaveOccurred())
1037+
})
1038+
It("sets resolution success status", func() {
1039+
By("running reconcile")
1040+
res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey})
1041+
Expect(res).To(Equal(ctrl.Result{}))
1042+
Expect(err).To(HaveOccurred())
1043+
Expect(err.Error()).To(Equal("unknown bundle mediatype: badmedia+v1"))
1044+
1045+
By("fetching updated operator after reconcile")
1046+
Expect(cl.Get(ctx, opKey, operator)).NotTo(HaveOccurred())
1047+
1048+
By("Checking the status fields")
1049+
Expect(operator.Status.ResolvedBundleResource).To(Equal("quay.io/operatorhub/badmedia@sha256:badmedia"))
1050+
Expect(operator.Status.InstalledBundleResource).To(Equal(""))
1051+
1052+
By("checking the expected conditions")
1053+
cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeResolved)
1054+
Expect(cond).NotTo(BeNil())
1055+
Expect(cond.Status).To(Equal(metav1.ConditionTrue))
1056+
Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonSuccess))
1057+
Expect(cond.Message).To(Equal("resolved to \"quay.io/operatorhub/badmedia@sha256:badmedia\""))
1058+
cond = apimeta.FindStatusCondition(operator.Status.Conditions, operatorsv1alpha1.TypeInstalled)
1059+
Expect(cond).NotTo(BeNil())
1060+
Expect(cond.Status).To(Equal(metav1.ConditionFalse))
1061+
Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonInstallationFailed))
1062+
Expect(cond.Message).To(Equal("unknown bundle mediatype: badmedia+v1"))
1063+
})
1064+
})
9631065
})
9641066
When("an invalid semver is provided that bypasses the regex validation", func() {
9651067
var (
@@ -1061,4 +1163,18 @@ var testEntitySource = input.NewCacheQuerier(map[deppy.Identifier]input.Entity{
10611163
"olm.package": `{"packageName":"badimage","version":"0.1.0"}`,
10621164
"olm.gvk": `[]`,
10631165
}),
1166+
"operatorhub/plain/0.1.0": *input.NewEntity("operatorhub/plain/0.1.0", map[string]string{
1167+
"olm.bundle.path": `"quay.io/operatorhub/plain@sha256:plain"`,
1168+
"olm.channel": `{"channelName":"beta","priority":0}`,
1169+
"olm.package": `{"packageName":"plain","version":"0.1.0"}`,
1170+
"olm.gvk": `[]`,
1171+
"olm.bundle.mediatype": `"plain+v0"`,
1172+
}),
1173+
"operatorhub/badmedia/0.1.0": *input.NewEntity("operatorhub/badmedia/0.1.0", map[string]string{
1174+
"olm.bundle.path": `"quay.io/operatorhub/badmedia@sha256:badmedia"`,
1175+
"olm.channel": `{"channelName":"beta","priority":0}`,
1176+
"olm.package": `{"packageName":"badmedia","version":"0.1.0"}`,
1177+
"olm.gvk": `[]`,
1178+
"olm.bundle.mediatype": `"badmedia+v1"`,
1179+
}),
10641180
})

internal/resolution/entitysources/catalogdsource.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/operator-framework/deppy/pkg/deppy"
99
"github.com/operator-framework/deppy/pkg/deppy/input"
10+
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/entity"
1011
"github.com/operator-framework/operator-registry/alpha/property"
1112
"sigs.k8s.io/controller-runtime/pkg/client"
1213

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

test/e2e/install_test.go

Lines changed: 128 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import (
1717
)
1818

1919
const (
20-
defaultTimeout = 30 * time.Second
20+
defaultTimeout = 120 * time.Second
2121
defaultPoll = 1 * time.Second
2222
testCatalogRef = "localhost/testdata/catalogs/test-catalog:e2e"
2323
)
@@ -27,25 +27,17 @@ var _ = Describe("Operator Install", func() {
2727
ctx context.Context
2828
pkgName string
2929
operatorName string
30+
catalogName string
3031
operator *operatorv1alpha1.Operator
3132
operatorCatalog *catalogd.Catalog
3233
)
3334
When("An operator is installed from an operator catalog", func() {
3435
BeforeEach(func() {
3536
ctx = context.Background()
36-
pkgName = "prometheus"
37-
operatorName = fmt.Sprintf("operator-%s", rand.String(8))
38-
operator = &operatorv1alpha1.Operator{
39-
ObjectMeta: metav1.ObjectMeta{
40-
Name: operatorName,
41-
},
42-
Spec: operatorv1alpha1.OperatorSpec{
43-
PackageName: pkgName,
44-
},
45-
}
37+
catalogName = fmt.Sprintf("catalog-%s", rand.String(8))
4638
operatorCatalog = &catalogd.Catalog{
4739
ObjectMeta: metav1.ObjectMeta{
48-
Name: "test-catalog",
40+
Name: catalogName,
4941
},
5042
Spec: catalogd.CatalogSpec{
5143
Source: catalogd.CatalogSource{
@@ -59,54 +51,140 @@ var _ = Describe("Operator Install", func() {
5951
err := c.Create(ctx, operatorCatalog)
6052
Expect(err).ToNot(HaveOccurred())
6153
Eventually(func(g Gomega) {
62-
err = c.Get(ctx, types.NamespacedName{Name: "test-catalog"}, operatorCatalog)
54+
err = c.Get(ctx, types.NamespacedName{Name: catalogName}, operatorCatalog)
6355
g.Expect(err).ToNot(HaveOccurred())
64-
g.Expect(len(operatorCatalog.Status.Conditions)).To(Equal(1))
65-
g.Expect(operatorCatalog.Status.Conditions[0].Message).To(ContainSubstring("successfully unpacked the catalog image"))
66-
}).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed())
67-
})
68-
It("resolves the specified package with correct bundle path", func() {
69-
By("creating the Operator resource")
70-
err := c.Create(ctx, operator)
71-
Expect(err).ToNot(HaveOccurred())
72-
73-
By("eventually reporting a successful resolution and bundle path")
74-
Eventually(func(g Gomega) {
75-
err = c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)
76-
g.Expect(err).ToNot(HaveOccurred())
77-
g.Expect(len(operator.Status.Conditions)).To(Equal(2))
78-
cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved)
56+
cond := apimeta.FindStatusCondition(operatorCatalog.Status.Conditions, catalogd.TypeUnpacked)
7957
g.Expect(cond).ToNot(BeNil())
8058
g.Expect(cond.Status).To(Equal(metav1.ConditionTrue))
81-
g.Expect(cond.Reason).To(Equal(operatorv1alpha1.ReasonSuccess))
82-
g.Expect(cond.Message).To(ContainSubstring("resolved to"))
83-
g.Expect(operator.Status.ResolvedBundleResource).ToNot(BeEmpty())
84-
}).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed())
59+
g.Expect(cond.Message).To(ContainSubstring("successfully unpacked the catalog image"))
8560

86-
By("eventually installing the package successfully")
87-
Eventually(func(g Gomega) {
88-
err = c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)
89-
g.Expect(err).ToNot(HaveOccurred())
90-
cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeInstalled)
91-
g.Expect(cond).ToNot(BeNil())
92-
g.Expect(cond.Status).To(Equal(metav1.ConditionTrue))
93-
g.Expect(cond.Reason).To(Equal(operatorv1alpha1.ReasonSuccess))
94-
g.Expect(cond.Message).To(ContainSubstring("installed from"))
95-
g.Expect(operator.Status.InstalledBundleResource).ToNot(BeEmpty())
96-
bd := rukpakv1alpha1.BundleDeployment{}
97-
err = c.Get(ctx, types.NamespacedName{Name: operatorName}, &bd)
61+
// For some reason the above condition check is returning true and the
62+
// Operators end up being created before the packages exist. Adding this check
63+
// to ensure that there are some packages that exist before actually returning from this
64+
// check.
65+
pkgList := &catalogd.PackageList{}
66+
err = c.List(ctx, pkgList)
9867
g.Expect(err).ToNot(HaveOccurred())
99-
g.Expect(len(bd.Status.Conditions)).To(Equal(2))
100-
g.Expect(bd.Status.Conditions[0].Reason).To(Equal("UnpackSuccessful"))
101-
g.Expect(bd.Status.Conditions[1].Reason).To(Equal("InstallationSucceeded"))
68+
g.Expect(pkgList.Items).To(HaveLen(2))
10269
}).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed())
103-
10470
})
71+
10572
AfterEach(func() {
10673
err := c.Delete(ctx, operatorCatalog)
10774
Expect(err).ToNot(HaveOccurred())
108-
err = c.Delete(ctx, operator)
109-
Expect(err).ToNot(HaveOccurred())
11075
})
76+
77+
When("the operator bundle format is registry+v1", func() {
78+
BeforeEach(func() {
79+
pkgName = "prometheus"
80+
operatorName = fmt.Sprintf("operator-%s", rand.String(8))
81+
operator = &operatorv1alpha1.Operator{
82+
ObjectMeta: metav1.ObjectMeta{
83+
Name: operatorName,
84+
},
85+
Spec: operatorv1alpha1.OperatorSpec{
86+
PackageName: pkgName,
87+
},
88+
}
89+
})
90+
It("resolves the specified package with correct bundle path", func() {
91+
By("creating the Operator resource")
92+
err := c.Create(ctx, operator)
93+
Expect(err).ToNot(HaveOccurred())
94+
95+
By("eventually reporting a successful resolution and bundle path")
96+
Eventually(func(g Gomega) {
97+
err = c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)
98+
g.Expect(err).ToNot(HaveOccurred())
99+
g.Expect(len(operator.Status.Conditions)).To(Equal(2))
100+
cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved)
101+
g.Expect(cond).ToNot(BeNil())
102+
g.Expect(cond.Status).To(Equal(metav1.ConditionTrue))
103+
g.Expect(cond.Reason).To(Equal(operatorv1alpha1.ReasonSuccess))
104+
g.Expect(cond.Message).To(ContainSubstring("resolved to"))
105+
g.Expect(operator.Status.ResolvedBundleResource).ToNot(BeEmpty())
106+
}).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed())
107+
108+
By("eventually installing the package successfully")
109+
Eventually(func(g Gomega) {
110+
err = c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)
111+
g.Expect(err).ToNot(HaveOccurred())
112+
cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeInstalled)
113+
g.Expect(cond).ToNot(BeNil())
114+
g.Expect(cond.Status).To(Equal(metav1.ConditionTrue))
115+
g.Expect(cond.Reason).To(Equal(operatorv1alpha1.ReasonSuccess))
116+
g.Expect(cond.Message).To(ContainSubstring("installed from"))
117+
g.Expect(operator.Status.InstalledBundleResource).ToNot(BeEmpty())
118+
bd := rukpakv1alpha1.BundleDeployment{}
119+
err = c.Get(ctx, types.NamespacedName{Name: operatorName}, &bd)
120+
g.Expect(err).ToNot(HaveOccurred())
121+
g.Expect(len(bd.Status.Conditions)).To(Equal(2))
122+
g.Expect(bd.Status.Conditions[0].Reason).To(Equal("UnpackSuccessful"))
123+
g.Expect(bd.Status.Conditions[1].Reason).To(Equal("InstallationSucceeded"))
124+
}).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed())
125+
126+
})
127+
AfterEach(func() {
128+
err := c.Delete(ctx, operator)
129+
Expect(err).ToNot(HaveOccurred())
130+
})
131+
})
132+
133+
When("the operator bundle format is plain+v0", func() {
134+
BeforeEach(func() {
135+
pkgName = "plain"
136+
operatorName = fmt.Sprintf("operator-%s", rand.String(8))
137+
operator = &operatorv1alpha1.Operator{
138+
ObjectMeta: metav1.ObjectMeta{
139+
Name: operatorName,
140+
},
141+
Spec: operatorv1alpha1.OperatorSpec{
142+
PackageName: pkgName,
143+
},
144+
}
145+
})
146+
It("resolves the specified package with correct bundle path", func() {
147+
By("creating the Operator resource")
148+
err := c.Create(ctx, operator)
149+
Expect(err).ToNot(HaveOccurred())
150+
151+
By("eventually reporting a successful resolution and bundle path")
152+
Eventually(func(g Gomega) {
153+
err = c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)
154+
g.Expect(err).ToNot(HaveOccurred())
155+
g.Expect(len(operator.Status.Conditions)).To(Equal(2))
156+
cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeResolved)
157+
g.Expect(cond).ToNot(BeNil())
158+
g.Expect(cond.Status).To(Equal(metav1.ConditionTrue))
159+
g.Expect(cond.Reason).To(Equal(operatorv1alpha1.ReasonSuccess))
160+
g.Expect(cond.Message).To(ContainSubstring("resolved to"))
161+
g.Expect(operator.Status.ResolvedBundleResource).ToNot(BeEmpty())
162+
}).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed())
163+
164+
By("eventually installing the package successfully")
165+
Eventually(func(g Gomega) {
166+
err = c.Get(ctx, types.NamespacedName{Name: operator.Name}, operator)
167+
g.Expect(err).ToNot(HaveOccurred())
168+
cond := apimeta.FindStatusCondition(operator.Status.Conditions, operatorv1alpha1.TypeInstalled)
169+
g.Expect(cond).ToNot(BeNil())
170+
g.Expect(cond.Status).To(Equal(metav1.ConditionTrue))
171+
g.Expect(cond.Reason).To(Equal(operatorv1alpha1.ReasonSuccess))
172+
g.Expect(cond.Message).To(ContainSubstring("installed from"))
173+
g.Expect(operator.Status.InstalledBundleResource).ToNot(BeEmpty())
174+
bd := rukpakv1alpha1.BundleDeployment{}
175+
err = c.Get(ctx, types.NamespacedName{Name: operatorName}, &bd)
176+
g.Expect(err).ToNot(HaveOccurred())
177+
g.Expect(len(bd.Status.Conditions)).To(Equal(2))
178+
g.Expect(bd.Status.Conditions[0].Reason).To(Equal("UnpackSuccessful"))
179+
g.Expect(bd.Status.Conditions[1].Reason).To(Equal("InstallationSucceeded"))
180+
}).WithTimeout(defaultTimeout).WithPolling(defaultPoll).Should(Succeed())
181+
182+
})
183+
AfterEach(func() {
184+
err := c.Delete(ctx, operator)
185+
Expect(err).ToNot(HaveOccurred())
186+
})
187+
})
188+
111189
})
112190
})
Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,2 @@
1-
# The base image is expected to contain
2-
# /bin/opm (with a serve subcommand) and /bin/grpc_health_probe
3-
FROM quay.io/operator-framework/opm:latest
4-
5-
# Configure the entrypoint and command
6-
ENTRYPOINT ["/bin/opm"]
7-
CMD ["serve", "/configs", "--cache-dir=/tmp/cache"]
8-
9-
# Copy declarative config root into image at /configs and pre-populate serve cache
1+
FROM scratch
102
ADD test-catalog /configs
11-
RUN ["/bin/opm", "serve", "/configs", "--cache-dir=/tmp/cache", "--cache-only"]
12-
13-
# Set DC-specific label for the location of the DC root directory
14-
# in the image
15-
LABEL operators.operatorframework.io.index.configs.v1=/configs

0 commit comments

Comments
 (0)