Skip to content

Commit 6647c3f

Browse files
:controller: Resurface bundledeployment errors
This PR enables the operator object to resurface the errors in Rukpak by setting its conditions as "BundleDeploymentFailed" if any of the BD errors.
1 parent 520219c commit 6647c3f

File tree

4 files changed

+129
-16
lines changed

4 files changed

+129
-16
lines changed

controllers/operator_controller.go

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,16 @@ func (r *OperatorReconciler) ensureBundleDeployment(ctx context.Context, desired
257257
}
258258

259259
// If the existing BD already has everything that the desired BD has, no need to contact the API server.
260+
// Make sure the status of the existingBD from the server is as expected.
260261
if equality.Semantic.DeepDerivative(desiredBundleDeployment, existingBundleDeployment) {
261-
return nil
262+
return verifySuccessfulBDStatus(existingBundleDeployment)
262263
}
263-
return r.Client.Patch(ctx, desiredBundleDeployment, client.Apply, client.ForceOwnership, client.FieldOwner("operator-controller"))
264+
265+
if err = r.Client.Patch(ctx, desiredBundleDeployment, client.Apply, client.ForceOwnership, client.FieldOwner("operator-controller")); err != nil {
266+
return err
267+
}
268+
269+
return verifySuccessfulBDStatus(desiredBundleDeployment)
264270
}
265271

266272
func (r *OperatorReconciler) existingBundleDeploymentUnstructured(ctx context.Context, name string) (*unstructured.Unstructured, error) {
@@ -277,3 +283,30 @@ func (r *OperatorReconciler) existingBundleDeploymentUnstructured(ctx context.Co
277283
}
278284
return &unstructured.Unstructured{Object: unstrExistingBundleDeploymentObj}, nil
279285
}
286+
287+
func verifySuccessfulBDStatus(dep *unstructured.Unstructured) error {
288+
// convert the unstructured object from patch call to typed bundledeployment to make checking of Status easier.
289+
existingTypedBundleDeployment := &rukpakv1alpha1.BundleDeployment{}
290+
291+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(dep.UnstructuredContent(), existingTypedBundleDeployment); err != nil {
292+
return err
293+
}
294+
295+
ers := []error{}
296+
conditions := existingTypedBundleDeployment.Status.Conditions
297+
298+
// Do we error here?
299+
if conditions == nil || len(conditions) == 0 {
300+
return nil
301+
}
302+
303+
for _, condition := range conditions {
304+
// Currently we are checking for only these two types, if any other condition is added to Rukpak, validate accordingly.
305+
if condition.Type == rukpakv1alpha1.TypeHasValidBundle || condition.Type == rukpakv1alpha1.TypeInstalled {
306+
if condition.Status == metav1.ConditionFalse {
307+
ers = append(ers, fmt.Errorf("error observed by Rukpak: %v", condition.Message))
308+
}
309+
}
310+
}
311+
return utilerrors.NewAggregate(ers)
312+
}

controllers/operator_controller_test.go

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,10 +293,80 @@ var _ = Describe("Reconcile Test", func() {
293293
Expect(cond.Message).To(Equal(`duplicate identifier "required package prometheus" in input`))
294294
})
295295
})
296+
When("the existing operator status is based on bundleDeployment", func() {
297+
const pkgName = "prometheus"
298+
var (
299+
bd rukpakv1alpha1.BundleDeployment
300+
)
301+
BeforeEach(func() {
302+
By("creating the expected BundleDeployment")
303+
bd := &rukpakv1alpha1.BundleDeployment{
304+
ObjectMeta: metav1.ObjectMeta{Name: opKey.Name},
305+
Spec: rukpakv1alpha1.BundleDeploymentSpec{
306+
ProvisionerClassName: "core-rukpak-io-plain",
307+
Template: &rukpakv1alpha1.BundleTemplate{
308+
Spec: rukpakv1alpha1.BundleSpec{
309+
ProvisionerClassName: "core-rukpak-io-registry",
310+
Source: rukpakv1alpha1.BundleSource{
311+
Type: rukpakv1alpha1.SourceTypeImage,
312+
Image: &rukpakv1alpha1.ImageSource{
313+
Ref: "quay.io/operatorhubio/prometheus@sha256:5b04c49d8d3eff6a338b56ec90bdf491d501fe301c9cdfb740e5bff6769a21ed",
314+
},
315+
},
316+
},
317+
},
318+
},
319+
}
320+
err := cl.Create(ctx, bd)
321+
Expect(err).NotTo(HaveOccurred())
322+
323+
By("creating the operator object")
324+
operator = &operatorsv1alpha1.Operator{
325+
ObjectMeta: metav1.ObjectMeta{Name: opKey.Name},
326+
Spec: operatorsv1alpha1.OperatorSpec{
327+
PackageName: pkgName,
328+
},
329+
}
330+
err = cl.Create(ctx, operator)
331+
Expect(err).NotTo(HaveOccurred())
332+
333+
})
334+
It("verify if operator status is updated according to bundleDeployment", func() {
335+
err := cl.Get(ctx, opKey, &bd)
336+
Expect(err).NotTo(HaveOccurred())
337+
apimeta.SetStatusCondition(&bd.Status.Conditions, metav1.Condition{
338+
Type: rukpakv1alpha1.TypeInstalled,
339+
LastTransitionTime: metav1.Now(),
340+
Status: metav1.ConditionFalse,
341+
Message: "fail to unpack",
342+
Reason: rukpakv1alpha1.ReasonInstallFailed,
343+
})
344+
err = cl.Status().Update(ctx, &bd)
345+
Expect(err).NotTo(HaveOccurred())
346+
347+
By("running reconcile")
348+
res, err := reconciler.Reconcile(ctx, ctrl.Request{NamespacedName: opKey})
349+
Expect(res).To(Equal(ctrl.Result{}))
350+
Expect(err).To(HaveOccurred())
351+
Expect(err.Error()).To(ContainSubstring(`error observed by Rukpak`))
352+
353+
By("fetching the updated operator after reconcile")
354+
op := &operatorsv1alpha1.Operator{}
355+
err = cl.Get(ctx, opKey, op)
356+
Expect(err).NotTo(HaveOccurred())
357+
358+
By("checking the expected conditions")
359+
cond := apimeta.FindStatusCondition(op.Status.Conditions, operatorsv1alpha1.TypeReady)
360+
Expect(cond).NotTo(BeNil())
361+
Expect(cond.Status).To(Equal(metav1.ConditionFalse))
362+
Expect(cond.Reason).To(Equal(operatorsv1alpha1.ReasonBundleDeploymentFailed))
363+
Expect(cond.Message).To(ContainSubstring(`error observed by Rukpak`))
364+
})
365+
})
296366
AfterEach(func() {
297367
verifyInvariants(ctx, operator)
298368

299-
err := cl.Delete(ctx, operator)
369+
err := cl.DeleteAllOf(ctx, operator)
300370
Expect(err).To(Not(HaveOccurred()))
301371
})
302372
})

internal/resolution/bundle_cache.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,10 @@ var HardcodedEntitySource = input.NewCacheQuerier(map[deppy.Identifier]input.Ent
4848
"olm.gvk": "[{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Alertmanager\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"AlertmanagerConfig\",\"version\":\"v1alpha1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"AlertmanagerConfig\",\"version\":\"v1alpha1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PodMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PodMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Probe\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Probe\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"Prometheus\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PrometheusRule\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"PrometheusRule\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ServiceMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ServiceMonitor\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ThanosRuler\",\"version\":\"v1\"},{\"group\":\"monitoring.coreos.com\",\"kind\":\"ThanosRuler\",\"version\":\"v1\"}]",
4949
"olm.package": "{\"packageName\":\"prometheus\",\"version\":\"0.47.0\"}",
5050
}),
51+
"operatorhub/lightbend-console-operator/0.0.1": *input.NewEntity("operatorhub/lightbend-console-operator/0.0.1", map[string]string{
52+
"olm.bundle.path": `"quay.io/operatorhubio/lightbend-console-operator@sha256:2cf5f1abf71be29b7d2667ae9ca4102198c93cdef450d09faf1b26900443e285"`,
53+
"olm.channel": "{\"channelName\":\"alpha\",\"priority\":0}",
54+
"olm.gvk": "[{\"group\":\"app.lightbend.com\",\"kind\":\"Console\",\"version\":\"v1alpha1\"}]",
55+
"olm.package": "{\"packageName\":\"lightbend-console-operator\",\"version\":\"0.0.1\"}",
56+
}),
5157
})

test/e2e/install_test.go

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -21,21 +21,25 @@ const (
2121
)
2222

2323
var _ = Describe("Operator Install", func() {
24+
var (
25+
ctx context.Context
26+
pkgName string
27+
operatorName string
28+
operator *operatorv1alpha1.Operator
29+
)
2430
It("resolves the specified package with correct bundle path", func() {
25-
var (
26-
ctx context.Context = context.Background()
27-
pkgName string = "prometheus"
28-
operatorName string = fmt.Sprintf("operator-%s", rand.String(8))
29-
operator *operatorv1alpha1.Operator = &operatorv1alpha1.Operator{
30-
ObjectMeta: metav1.ObjectMeta{
31-
Name: operatorName,
32-
},
33-
Spec: operatorv1alpha1.OperatorSpec{
34-
PackageName: pkgName,
35-
},
36-
}
37-
)
3831
ctx = context.Background()
32+
pkgName = "prometheus"
33+
operatorName = fmt.Sprintf("operator-%s", rand.String(8))
34+
operator = &operatorv1alpha1.Operator{
35+
ObjectMeta: metav1.ObjectMeta{
36+
Name: operatorName,
37+
},
38+
Spec: operatorv1alpha1.OperatorSpec{
39+
PackageName: pkgName,
40+
},
41+
}
42+
3943
By("creating the Operator resource")
4044
err := c.Create(ctx, operator)
4145
Expect(err).ToNot(HaveOccurred())

0 commit comments

Comments
 (0)