Skip to content

Commit b936e0b

Browse files
committed
feat(sub): add installplan reconciler to syncer
- Add InstallPlan status reconciler to Subscription Syncer - Add InstallPlan Informer option to Subscription Syncer config - Plumb now function through transitionInstallPlanState(...)
1 parent 5df0994 commit b936e0b

File tree

4 files changed

+93
-20
lines changed

4 files changed

+93
-20
lines changed

pkg/controller/operators/catalog/operator.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,7 @@ func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clo
169169
subscription.WithOperatorLister(op.lister),
170170
subscription.WithSubscriptionInformer(subInformer.Informer()),
171171
subscription.WithCatalogInformer(catsrcInformer.Informer()),
172+
subscription.WithInstallPlanInformer(ipInformer.Informer()),
172173
subscription.WithSubscriptionQueue(subQueue),
173174
subscription.WithAppendedReconcilers(subscription.ReconcilerFromLegacySyncHandler(op.syncSubscriptions, nil)),
174175
subscription.WithRegistryReconcilerFactory(op.reconciler),
@@ -919,7 +920,7 @@ func (o *Operator) syncInstallPlans(obj interface{}) (syncError error) {
919920
return
920921
}
921922

922-
outInstallPlan, syncError := transitionInstallPlanState(logger.Logger, o, *plan)
923+
outInstallPlan, syncError := transitionInstallPlanState(logger.Logger, o, *plan, o.now())
923924

924925
if syncError != nil {
925926
logger = logger.WithField("syncError", syncError)
@@ -964,7 +965,7 @@ type installPlanTransitioner interface {
964965

965966
var _ installPlanTransitioner = &Operator{}
966967

967-
func transitionInstallPlanState(log *logrus.Logger, transitioner installPlanTransitioner, in v1alpha1.InstallPlan) (*v1alpha1.InstallPlan, error) {
968+
func transitionInstallPlanState(log *logrus.Logger, transitioner installPlanTransitioner, in v1alpha1.InstallPlan, now metav1.Time) (*v1alpha1.InstallPlan, error) {
968969
out := in.DeepCopy()
969970

970971
switch in.Status.Phase {
@@ -981,11 +982,11 @@ func transitionInstallPlanState(log *logrus.Logger, transitioner installPlanTran
981982
log.Debug("attempting to install")
982983
if err := transitioner.ExecutePlan(out); err != nil {
983984
out.Status.SetCondition(v1alpha1.ConditionFailed(v1alpha1.InstallPlanInstalled,
984-
v1alpha1.InstallPlanReasonComponentFailed, err))
985+
v1alpha1.InstallPlanReasonComponentFailed, err.Error(), &now))
985986
out.Status.Phase = v1alpha1.InstallPlanPhaseFailed
986987
return out, err
987988
}
988-
out.Status.SetCondition(v1alpha1.ConditionMet(v1alpha1.InstallPlanInstalled))
989+
out.Status.SetCondition(v1alpha1.ConditionMet(v1alpha1.InstallPlanInstalled, &now))
989990
out.Status.Phase = v1alpha1.InstallPlanPhaseComplete
990991
return out, nil
991992
default:

pkg/controller/operators/catalog/operator_test.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ func (m *mockTransitioner) ExecutePlan(plan *v1alpha1.InstallPlan) error {
5858
func TestTransitionInstallPlan(t *testing.T) {
5959
errMsg := "transition test error"
6060
err := errors.New(errMsg)
61+
clockFake := utilclock.NewFakeClock(time.Date(2018, time.January, 26, 20, 40, 0, 0, time.UTC))
62+
now := metav1.NewTime(clockFake.Now())
6163

6264
installed := &v1alpha1.InstallPlanCondition{
6365
Type: v1alpha1.InstallPlanInstalled,
@@ -103,7 +105,7 @@ func TestTransitionInstallPlan(t *testing.T) {
103105
transitioner := &mockTransitioner{tt.transError}
104106

105107
// Attempt to transition phases.
106-
out, _ := transitionInstallPlanState(logrus.New(), transitioner, *plan)
108+
out, _ := transitionInstallPlanState(logrus.New(), transitioner, *plan, now)
107109

108110
// Assert that the final phase is as expected.
109111
require.Equal(t, tt.expected, out.Status.Phase)

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

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ type syncerConfig struct {
2121
lister operatorlister.OperatorLister
2222
subscriptionInformer cache.SharedIndexInformer
2323
catalogInformer cache.SharedIndexInformer
24+
installPlanInformer cache.SharedIndexInformer
2425
subscriptionQueue workqueue.RateLimitingInterface
2526
reconcilers kubestate.ReconcilerChain
2627
registryReconcilerFactory reconciler.RegistryReconcilerFactory
@@ -72,13 +73,20 @@ func WithSubscriptionInformer(subscriptionInformer cache.SharedIndexInformer) Sy
7273
}
7374
}
7475

75-
// WithCatalogInformer sets the informer a syncer will wire dependent subscription notifications to.
76+
// WithCatalogInformer sets a CatalogSource informer to act as an event source for dependent Subscriptions.
7677
func WithCatalogInformer(catalogInformer cache.SharedIndexInformer) SyncerOption {
7778
return func(config *syncerConfig) {
7879
config.catalogInformer = catalogInformer
7980
}
8081
}
8182

83+
// WithInstallPlanInformer sets an InstallPlan informer to act as an event source for dependent Subscriptions.
84+
func WithInstallPlanInformer(installPlanInformer cache.SharedIndexInformer) SyncerOption {
85+
return func(config *syncerConfig) {
86+
config.installPlanInformer = installPlanInformer
87+
}
88+
}
89+
8290
// WithOperatorLister sets a syncer's operator lister.
8391
func WithOperatorLister(lister operatorlister.OperatorLister) SyncerOption {
8492
return func(config *syncerConfig) {
@@ -138,6 +146,8 @@ func (s *syncerConfig) validate() (err error) {
138146
err = newInvalidConfigError("nil subscription informer")
139147
case s.catalogInformer == nil:
140148
err = newInvalidConfigError("nil catalog informer")
149+
case s.installPlanInformer == nil:
150+
err = newInvalidConfigError("nil installplan informer")
141151
case s.subscriptionQueue == nil:
142152
err = newInvalidConfigError("nil subscription queue")
143153
case len(s.reconcilers) == 0:

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

Lines changed: 74 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ import (
1212

1313
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/install"
1414
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/apis/operators/v1alpha1"
15+
listers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1alpha1"
1516
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubestate"
17+
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/ownerutil"
1618
)
1719

1820
var scheme = runtime.NewScheme()
@@ -28,6 +30,7 @@ type subscriptionSyncer struct {
2830
clock utilclock.Clock
2931
reconcilers kubestate.ReconcilerChain
3032
subscriptionCache cache.Indexer
33+
installPlanLister listers.InstallPlanLister
3134
globalCatalogNamespace string
3235
notify kubestate.NotifyFunc
3336
}
@@ -96,9 +99,9 @@ func (s *subscriptionSyncer) catalogSubscriptionKeys(namespace string) ([]string
9699
return keys, err
97100
}
98101

99-
// catalogNotification notifies dependent subscriptions of the change with the given object.
100-
// The given object is assumed to be a Subscription, Subscription tombstone, or a cache.ExplicitKey.
101-
func (s *subscriptionSyncer) catalogNotification(ctx context.Context, obj interface{}) {
102+
// notifyOnCatalog notifies dependent subscriptions of the change with the given object.
103+
// The given object is assumed to be a CatalogSource, CatalogSource tombstone, or a cache.ExplicitKey.
104+
func (s *subscriptionSyncer) notifyOnCatalog(ctx context.Context, obj interface{}) {
102105
k, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
103106
if err != nil {
104107
s.logger.WithField("resource", obj).Warn("could not unpack key")
@@ -127,6 +130,66 @@ func (s *subscriptionSyncer) catalogNotification(ctx context.Context, obj interf
127130
logger.Trace("dependent subscriptions notified")
128131
}
129132

133+
// notifyOnInstallPlan notifies dependent subscriptions of the change with the given object.
134+
// The given object is assumed to be an InstallPlan, InstallPlan tombstone, or a cache.ExplicitKey.
135+
func (s *subscriptionSyncer) notifyOnInstallPlan(ctx context.Context, obj interface{}) {
136+
plan, ok := obj.(*v1alpha1.InstallPlan)
137+
if !ok {
138+
s.logger.WithField("obj", fmt.Sprintf("%v", obj)).Trace("could not cast as installplan directly while notifying subscription syncer")
139+
if tombstone, ok := obj.(cache.DeletedFinalStateUnknown); ok {
140+
if plan, ok = tombstone.Obj.(*v1alpha1.InstallPlan); !ok {
141+
s.logger.WithField("tombstone", tombstone).Warn("could not cast as installplan")
142+
return
143+
}
144+
} else {
145+
k, err := cache.DeletionHandlingMetaNamespaceKeyFunc(obj)
146+
if err != nil {
147+
s.logger.WithField("resource", obj).Warn("could not unpack key")
148+
return
149+
}
150+
logger := s.logger.WithField("key", k)
151+
152+
ns, name, err := cache.SplitMetaNamespaceKey(k)
153+
if err != nil {
154+
logger.Warn("could not split meta key")
155+
return
156+
}
157+
158+
if plan, err = s.installPlanLister.InstallPlans(ns).Get(name); err != nil {
159+
logger.WithError(err).Warn("could not get installplan")
160+
return
161+
}
162+
}
163+
}
164+
165+
logger := s.logger.WithFields(logrus.Fields{
166+
"namespace": plan.GetNamespace(),
167+
"installplan": plan.GetName(),
168+
})
169+
170+
// Notify dependent owner Subscriptions
171+
owners := ownerutil.GetOwnersByKind(plan, v1alpha1.SubscriptionKind)
172+
for _, owner := range owners {
173+
subKey := fmt.Sprintf("%s/%s", plan.GetNamespace(), owner.Name)
174+
logger.Tracef("notifying subscription %s", subKey)
175+
s.Notify(kubestate.NewResourceEvent(kubestate.ResourceUpdated, cache.ExplicitKey(subKey)))
176+
}
177+
}
178+
179+
func eventHandlers(ctx context.Context, notify func(ctx context.Context, obj interface{})) cache.ResourceEventHandlerFuncs {
180+
return cache.ResourceEventHandlerFuncs{
181+
AddFunc: func(obj interface{}) {
182+
notify(ctx, obj)
183+
},
184+
UpdateFunc: func(oldObj, newObj interface{}) {
185+
notify(ctx, newObj)
186+
},
187+
DeleteFunc: func(obj interface{}) {
188+
notify(ctx, obj)
189+
},
190+
}
191+
}
192+
130193
// NewSyncer returns a syncer that syncs Subscription resources.
131194
func NewSyncer(ctx context.Context, options ...SyncerOption) (kubestate.Syncer, error) {
132195
config := defaultSyncerConfig()
@@ -145,6 +208,7 @@ func newSyncerWithConfig(ctx context.Context, config *syncerConfig) (kubestate.S
145208
clock: config.clock,
146209
reconcilers: config.reconcilers,
147210
subscriptionCache: config.subscriptionInformer.GetIndexer(),
211+
installPlanLister: config.lister.OperatorsV1alpha1().InstallPlanLister(),
148212
notify: func(event kubestate.ResourceEvent) {
149213
// Notify Subscriptions by enqueuing to the Subscription queue.
150214
config.subscriptionQueue.Add(event)
@@ -154,6 +218,11 @@ func newSyncerWithConfig(ctx context.Context, config *syncerConfig) (kubestate.S
154218
// Build a reconciler chain from the default and configured reconcilers
155219
// Default reconcilers should always come first in the chain
156220
defaultReconcilers := kubestate.ReconcilerChain{
221+
&installPlanReconciler{
222+
now: s.now,
223+
client: config.client,
224+
installPlanLister: config.lister.OperatorsV1alpha1().InstallPlanLister(),
225+
},
157226
&catalogHealthReconciler{
158227
now: s.now,
159228
client: config.client,
@@ -165,17 +234,8 @@ func newSyncerWithConfig(ctx context.Context, config *syncerConfig) (kubestate.S
165234
s.reconcilers = append(defaultReconcilers, s.reconcilers...)
166235

167236
// Add dependency notifications
168-
config.catalogInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{
169-
AddFunc: func(obj interface{}) {
170-
s.catalogNotification(ctx, obj)
171-
},
172-
UpdateFunc: func(oldObj, newObj interface{}) {
173-
s.catalogNotification(ctx, newObj)
174-
},
175-
DeleteFunc: func(obj interface{}) {
176-
s.catalogNotification(ctx, obj)
177-
},
178-
})
237+
config.installPlanInformer.AddEventHandler(eventHandlers(ctx, s.notifyOnInstallPlan))
238+
config.catalogInformer.AddEventHandler(eventHandlers(ctx, s.notifyOnCatalog))
179239

180240
return s, nil
181241
}

0 commit comments

Comments
 (0)