WARNING: THIS SITE IS A MIRROR OF GITHUB.COM / IT CANNOT LOGIN OR REGISTER ACCOUNTS / THE CONTENTS ARE PROVIDED AS-IS / THIS SITE ASSUMES NO RESPONSIBILITY FOR ANY DISPLAYED CONTENT OR LINKS / IF YOU FOUND SOMETHING MAY NOT GOOD FOR EVERYONE, CONTACT ADMIN AT ilovescratch@foxmail.com
Skip to content

Commit 56dc174

Browse files
committed
Adopt existing sub when packagemanifest not found
Previously, if the pacakagemanifest was missing, perhaps due to the operator no longer being available in the catalog, the operator policy would be marked as invalid, and not report on the potentially helpful status of the subscription. Now the existing subscription can be used for the default values, and a more helpful status can be reported. Refs: - https://issues.redhat.com/browse/ACM-14617 Signed-off-by: Justin Kulikauskas <[email protected]>
1 parent 90571b0 commit 56dc174

File tree

3 files changed

+106
-5
lines changed

3 files changed

+106
-5
lines changed

controllers/operatorpolicy_controller.go

Lines changed: 59 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -571,7 +571,8 @@ func (r *OperatorPolicyReconciler) checkSubOverlap(
571571
}
572572

573573
// applySubscriptionDefaults will set the subscription channel, source, and sourceNamespace when they are unset by
574-
// utilizing the PackageManifest API.
574+
// utilizing the PackageManifest API. If the PackageManifest can not be found, and a subscription already exists for the
575+
// operator, then information from the found subscription will be used.
575576
func (r *OperatorPolicyReconciler) applySubscriptionDefaults(
576577
ctx context.Context, policy *policyv1beta1.OperatorPolicy, subscription *operatorv1alpha1.Subscription,
577578
) error {
@@ -594,10 +595,14 @@ func (r *OperatorPolicyReconciler) applySubscriptionDefaults(
594595
)
595596
if err != nil {
596597
if k8serrors.IsNotFound(err) {
597-
return fmt.Errorf(
598-
"%wthe subscription defaults could not be determined because the PackageManifest was not found",
599-
ErrPackageManifest,
600-
)
598+
if !r.usingExistingSubIfFound(policy, subscription) {
599+
return fmt.Errorf(
600+
"%wthe subscription defaults could not be determined because the PackageManifest was not found",
601+
ErrPackageManifest,
602+
)
603+
}
604+
605+
return nil
601606
}
602607

603608
log.Error(err, "Failed to get the PackageManifest", "name", subSpec.Package)
@@ -679,6 +684,55 @@ func (r *OperatorPolicyReconciler) applySubscriptionDefaults(
679684
return nil
680685
}
681686

687+
// usingExistingSubIfFound attempts to find an existing subscription for the operator, and uses values
688+
// from that object to help build the desired subscription. It is meant to be used when the PackageManifest
689+
// for the operator is not found. It returns true when a subscription was found and used successfully.
690+
func (r *OperatorPolicyReconciler) usingExistingSubIfFound(
691+
policy *policyv1beta1.OperatorPolicy, subscription *operatorv1alpha1.Subscription,
692+
) bool {
693+
if subscription.Namespace == "" {
694+
// check for an already-known subscription to "adopt"
695+
subs := policy.Status.RelatedObjsOfKind("Subscription")
696+
697+
if len(subs) == 1 {
698+
// Note: RelatedObjsOfKind returns a map - in this case we just want the one object
699+
for _, sub := range subs {
700+
subscription.Namespace = sub.Object.Metadata.Namespace
701+
}
702+
}
703+
}
704+
705+
if subscription.Namespace != "" {
706+
watcher := opPolIdentifier(policy.Namespace, policy.Name)
707+
708+
gotSub, err := r.DynamicWatcher.Get(watcher, subscriptionGVK, subscription.Namespace, subscription.Name)
709+
if err != nil || gotSub == nil {
710+
return false
711+
}
712+
713+
foundSub := new(operatorv1alpha1.Subscription)
714+
if err := runtime.DefaultUnstructuredConverter.FromUnstructured(gotSub.Object, foundSub); err != nil {
715+
return false
716+
}
717+
718+
if subscription.Spec.Channel == "" {
719+
subscription.Spec.Channel = foundSub.Spec.Channel
720+
}
721+
722+
if subscription.Spec.CatalogSource == "" {
723+
subscription.Spec.CatalogSource = foundSub.Spec.CatalogSource
724+
}
725+
726+
if subscription.Spec.CatalogSourceNamespace == "" {
727+
subscription.Spec.CatalogSourceNamespace = foundSub.Spec.CatalogSourceNamespace
728+
}
729+
730+
return true
731+
}
732+
733+
return false
734+
}
735+
682736
// buildSubscription bootstraps the subscription spec defined in the operator policy
683737
// with the apiversion and kind in preparation for resource creation.
684738
// If an error is returned, it will include details on why the policy spec if invalid and

test/e2e/case38_install_operator_test.go

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -246,6 +246,19 @@ var _ = Describe("Testing OperatorPolicy", Ordered, Label("supports-hosted"), fu
246246
createObjWithParent(parentPolicyYAML, parentPolicyName,
247247
opPolYAML, testNamespace, gvrPolicy, gvrOperatorPolicy)
248248
})
249+
AfterAll(func(ctx context.Context) {
250+
By("Fixing the catalog source")
251+
KubectlTarget("patch", "catalogsource", "operatorhubio-catalog", "--namespace=olm", "--type=json", "-p",
252+
`[{"op": "replace", "path": "/spec/image", "value": "quay.io/operatorhubio/catalog:latest"}]`)
253+
254+
By("Waiting for a packagemanifest to reappear")
255+
Eventually(func() error {
256+
_, err := targetK8sDynamic.Resource(gvrPackageManifest).Namespace("default").Get(
257+
ctx, "airflow-helm-operator", metav1.GetOptions{})
258+
259+
return err
260+
}, olmWaitTimeout*2, 3).Should(Succeed())
261+
})
249262

250263
It("Should create the Subscription with default values", func(ctx context.Context) {
251264
By("Verifying the policy is compliant")
@@ -284,6 +297,34 @@ var _ = Describe("Testing OperatorPolicy", Ordered, Label("supports-hosted"), fu
284297
sourceNamespace, _, _ := unstructured.NestedString(sub.Object, "spec", "sourceNamespace")
285298
Expect(sourceNamespace).To(Equal("olm"))
286299
})
300+
301+
It("Should adopt an existing subscription if removed from the catalog", func(ctx context.Context) {
302+
By("Breaking the catalog source")
303+
KubectlTarget("patch", "catalogsource", "operatorhubio-catalog", "--namespace=olm", "--type=json", "-p",
304+
`[{"op": "replace", "path": "/spec/image", "value": "quay.io/operatorhubio/catalog:broken"}]`)
305+
306+
By("ensuring the packagemanifest is gone")
307+
Eventually(func() error {
308+
_, err := targetK8sDynamic.Resource(gvrPackageManifest).Namespace("default").Get(
309+
ctx, "airflow-helm-operator", metav1.GetOptions{})
310+
311+
return err
312+
}, olmWaitTimeout, 3).ShouldNot(Succeed())
313+
314+
By("Checking the validation condition")
315+
check(
316+
opPolName,
317+
false,
318+
[]policyv1.RelatedObject{},
319+
metav1.Condition{
320+
Type: "ValidPolicySpec",
321+
Status: metav1.ConditionTrue,
322+
Reason: "PolicyValidated",
323+
Message: `the policy spec is valid`,
324+
},
325+
`the policy spec is valid`,
326+
)
327+
})
287328
})
288329

289330
Describe("Testing an operator policy with invalid partial defaults", Ordered, func() {

test/e2e/e2e_suite_test.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ var (
5353
gvrOperatorGroup schema.GroupVersionResource
5454
gvrInstallPlan schema.GroupVersionResource
5555
gvrClusterServiceVersion schema.GroupVersionResource
56+
gvrPackageManifest schema.GroupVersionResource
5657
defaultImageRegistry string
5758
IsHosted bool
5859
targetK8sClient kubernetes.Interface
@@ -122,6 +123,11 @@ var _ = BeforeSuite(func() {
122123
Version: "v1alpha1",
123124
Resource: "clusterserviceversions",
124125
}
126+
gvrPackageManifest = schema.GroupVersionResource{
127+
Group: "packages.operators.coreos.com",
128+
Version: "v1",
129+
Resource: "packagemanifests",
130+
}
125131
gvrAPIService = schema.GroupVersionResource{
126132
Group: "apiregistration.k8s.io",
127133
Version: "v1",

0 commit comments

Comments
 (0)