Skip to content

Commit 5df0994

Browse files
committed
feat(sub): add installplan status reconciler
- Add Subscription InstallPlan status reconciler - Simplify Subscription catalog health reconciler transition loop conditions
1 parent 40c89a3 commit 5df0994

File tree

2 files changed

+1385
-135
lines changed

2 files changed

+1385
-135
lines changed

pkg/controller/operators/catalog/subscription/reconciler.go

Lines changed: 108 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ import (
44
"context"
55
"errors"
66
"fmt"
7+
"sort"
78

9+
apierrors "k8s.io/apimachinery/pkg/api/errors"
810
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
911
"k8s.io/apimachinery/pkg/labels"
1012
utilerrors "k8s.io/apimachinery/pkg/util/errors"
@@ -19,6 +21,34 @@ import (
1921
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer"
2022
)
2123

24+
// ReconcilerFromLegacySyncHandler returns a reconciler that invokes the given legacy sync handler and on delete funcs.
25+
// Since the reconciler does not return an updated kubestate, it MUST be the last reconciler in a given chain.
26+
func ReconcilerFromLegacySyncHandler(sync queueinformer.LegacySyncHandler, onDelete func(obj interface{})) kubestate.Reconciler {
27+
var rec kubestate.ReconcilerFunc = func(ctx context.Context, in kubestate.State) (out kubestate.State, err error) {
28+
out = in
29+
switch s := in.(type) {
30+
case SubscriptionExistsState:
31+
if sync != nil {
32+
err = sync(s.Subscription())
33+
}
34+
case SubscriptionDeletedState:
35+
if onDelete != nil {
36+
onDelete(s.Subscription())
37+
}
38+
case SubscriptionState:
39+
if sync != nil {
40+
err = sync(s.Subscription())
41+
}
42+
default:
43+
utilruntime.HandleError(fmt.Errorf("unexpected subscription state in legacy reconciler: %T", s))
44+
}
45+
46+
return
47+
}
48+
49+
return rec
50+
}
51+
2252
// catalogHealthReconciler reconciles catalog health status for subscriptions.
2353
type catalogHealthReconciler struct {
2454
now func() *metav1.Time
@@ -34,47 +64,46 @@ func (c *catalogHealthReconciler) Reconcile(ctx context.Context, in kubestate.St
3464
var prev kubestate.State
3565

3666
// loop until this state can no longer transition
37-
for err == nil && out == nil && next != nil && !next.Terminal() && prev != next {
67+
for err == nil && next != nil && next != prev && !next.Terminal() {
3868
select {
3969
case <-ctx.Done():
40-
err = errors.New("subscription catalog health reconciliation timed out")
70+
err = errors.New("subscription catalog health reconciliation context closed")
4171
default:
72+
prev = next
73+
4274
switch s := next.(type) {
4375
case CatalogHealthKnownState:
4476
// Target state already known, no work to do
45-
out = s
77+
next = s
4678
case CatalogHealthState:
4779
// Gather catalog health and transition state
4880
ns := s.Subscription().GetNamespace()
4981
var catalogHealth []v1alpha1.SubscriptionCatalogHealth
50-
catalogHealth, err = c.catalogHealth(ns)
51-
if err != nil {
82+
if catalogHealth, err = c.catalogHealth(ns); err != nil {
5283
break
5384
}
5485

55-
prev = s
5686
next, err = s.UpdateHealth(c.now(), c.client.OperatorsV1alpha1().Subscriptions(ns), catalogHealth...)
5787
case SubscriptionExistsState:
5888
if s == nil {
89+
err = errors.New("nil state")
5990
break
6091
}
6192
if s.Subscription() == nil {
93+
err = errors.New("nil subscription in state")
6294
break
6395
}
6496

6597
// Set up fresh state
6698
next = NewCatalogHealthState(s)
6799
default:
68100
// Ignore all other typestates
69-
utilruntime.HandleError(fmt.Errorf("unexpected subscription state in catalog health reconciler %T", next))
70-
out = s
101+
next = s
71102
}
72103
}
73104
}
74105

75-
if prev == next {
76-
out = prev
77-
}
106+
out = next
78107

79108
return
80109
}
@@ -96,6 +125,11 @@ func (c *catalogHealthReconciler) catalogHealth(namespace string) ([]v1alpha1.Su
96125
catalogs = append(catalogs, globals...)
97126
}
98127

128+
// Sort to ensure ordering
129+
sort.Slice(catalogs, func(i, j int) bool {
130+
return catalogs[i].GetNamespace()+catalogs[i].GetName() < catalogs[j].GetNamespace()+catalogs[j].GetName()
131+
})
132+
99133
catalogHealth := make([]v1alpha1.SubscriptionCatalogHealth, len(catalogs))
100134
now := c.now()
101135
var errs []error
@@ -151,30 +185,71 @@ func (c *catalogHealthReconciler) healthy(catalog *v1alpha1.CatalogSource) (bool
151185
return c.registryReconcilerFactory.ReconcilerForSource(catalog).CheckRegistryServer(catalog)
152186
}
153187

154-
// ReconcilerFromLegacySyncHandler returns a reconciler that invokes the given legacy sync handler and on delete funcs.
155-
// Since the reconciler does not return an updated kubestate, it MUST be the last reconciler in a given chain.
156-
func ReconcilerFromLegacySyncHandler(sync queueinformer.LegacySyncHandler, onDelete func(obj interface{})) kubestate.Reconciler {
157-
var rec kubestate.ReconcilerFunc = func(ctx context.Context, in kubestate.State) (out kubestate.State, err error) {
158-
out = in
159-
switch s := in.(type) {
160-
case SubscriptionExistsState:
161-
if sync != nil {
162-
err = sync(s.Subscription())
163-
}
164-
case SubscriptionDeletedState:
165-
if onDelete != nil {
166-
onDelete(s.Subscription())
167-
}
168-
case SubscriptionState:
169-
if sync != nil {
170-
err = sync(s.Subscription())
171-
}
188+
// installPlanReconciler reconciles InstallPlan status for Subscriptions.
189+
type installPlanReconciler struct {
190+
now func() *metav1.Time
191+
client versioned.Interface
192+
installPlanLister listers.InstallPlanLister
193+
}
194+
195+
// Reconcile reconciles Subscription InstallPlan conditions.
196+
func (i *installPlanReconciler) Reconcile(ctx context.Context, in kubestate.State) (out kubestate.State, err error) {
197+
next := in
198+
var prev kubestate.State
199+
200+
// loop until this state can no longer transition
201+
for err == nil && next != nil && prev != next && !next.Terminal() {
202+
select {
203+
case <-ctx.Done():
204+
err = errors.New("subscription installplan reconciliation context closed")
172205
default:
173-
utilruntime.HandleError(fmt.Errorf("unexpected subscription state in legacy reconciler: %T", s))
174-
}
206+
prev = next
175207

176-
return
208+
switch s := next.(type) {
209+
case NoInstallPlanReferencedState:
210+
// No InstallPlan was referenced, no work to do
211+
next = s
212+
case InstallPlanKnownState:
213+
// Target state already known, no work to do
214+
next = s
215+
case InstallPlanReferencedState:
216+
// Check the stated InstallPlan
217+
ref := s.Subscription().Status.InstallPlanRef // Should never be nil in this typestate
218+
subClient := i.client.OperatorsV1alpha1().Subscriptions(ref.Namespace)
219+
220+
var plan *v1alpha1.InstallPlan
221+
if plan, err = i.installPlanLister.InstallPlans(ref.Namespace).Get(ref.Name); err != nil {
222+
if apierrors.IsNotFound(err) {
223+
next, err = s.InstallPlanNotFound(i.now(), subClient)
224+
}
225+
226+
break
227+
}
228+
229+
next, err = s.CheckInstallPlanStatus(i.now(), subClient, &plan.Status)
230+
case InstallPlanState:
231+
next = s.CheckReference()
232+
case SubscriptionExistsState:
233+
if s == nil {
234+
err = errors.New("nil state")
235+
break
236+
}
237+
if s.Subscription() == nil {
238+
err = errors.New("nil subscription in state")
239+
break
240+
}
241+
242+
// Set up fresh state
243+
next = newInstallPlanState(s)
244+
default:
245+
// Ignore all other typestates
246+
utilruntime.HandleError(fmt.Errorf("unexpected subscription state in installplan reconciler %T", next))
247+
next = s
248+
}
249+
}
177250
}
178251

179-
return rec
252+
out = next
253+
254+
return
180255
}

0 commit comments

Comments
 (0)