Skip to content

Commit 7b640d5

Browse files
committed
fix(installplan): fix bug where too many installplans can be created
1 parent 85363e8 commit 7b640d5

File tree

2 files changed

+73
-37
lines changed

2 files changed

+73
-37
lines changed

pkg/controller/operators/catalog/operator.go

Lines changed: 69 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,17 @@ var timeNow = func() metav1.Time { return metav1.NewTime(time.Now().UTC()) }
5656
// resolving dependencies in a catalog.
5757
type Operator struct {
5858
*queueinformer.Operator
59-
client versioned.Interface
60-
lister operatorlister.OperatorLister
61-
namespace string
62-
sources map[resolver.CatalogKey]resolver.SourceRef
63-
sourcesLock sync.RWMutex
64-
sourcesLastUpdate metav1.Time
65-
resolver resolver.Resolver
66-
subQueue workqueue.RateLimitingInterface
67-
catSrcQueueSet queueinformer.ResourceQueueSet
68-
reconciler reconciler.ReconcilerFactory
59+
client versioned.Interface
60+
lister operatorlister.OperatorLister
61+
namespace string
62+
sources map[resolver.CatalogKey]resolver.SourceRef
63+
sourcesLock sync.RWMutex
64+
sourcesLastUpdate metav1.Time
65+
resolver resolver.Resolver
66+
subQueue workqueue.RateLimitingInterface
67+
catSrcQueueSet queueinformer.ResourceQueueSet
68+
namespaceResolveQueue workqueue.RateLimitingInterface
69+
reconciler reconciler.ReconcilerFactory
6970
}
7071

7172
// NewOperator creates a new Catalog Operator.
@@ -209,6 +210,23 @@ func NewOperator(kubeconfigPath string, logger *logrus.Logger, wakeupInterval ti
209210
OpClient: op.OpClient,
210211
Lister: op.lister,
211212
}
213+
214+
namespaceInformer := informers.NewSharedInformerFactory(op.OpClient.KubernetesInterface(), wakeupInterval).Core().V1().Namespaces()
215+
resolvingNamespaceQueue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "resolver")
216+
namespaceQueueInformer := queueinformer.NewInformer(
217+
resolvingNamespaceQueue,
218+
namespaceInformer.Informer(),
219+
op.syncResolvingNamespace,
220+
nil,
221+
"resolver",
222+
metrics.NewMetricsNil(),
223+
logger,
224+
)
225+
226+
op.RegisterQueueInformer(namespaceQueueInformer)
227+
op.lister.CoreV1().RegisterNamespaceLister(namespaceInformer.Lister())
228+
op.namespaceResolveQueue = resolvingNamespaceQueue
229+
212230
return op, nil
213231
}
214232

@@ -456,9 +474,8 @@ func (o *Operator) syncCatalogSources(obj interface{}) (syncError error) {
456474
return err
457475
}
458476

459-
// Sync any dependent Subscriptions
460-
// TODO: this should go away, we should resync the namespace instead
461-
o.syncDependentSubscriptions(logger, out.GetName(), out.GetNamespace())
477+
// Trigger a resolve, will pick up any subscriptions that depend on the catalog
478+
o.namespaceResolveQueue.AddRateLimited(out.GetNamespace())
462479

463480
return nil
464481
}
@@ -487,37 +504,22 @@ func (o *Operator) syncDependentSubscriptions(logger *logrus.Entry, catalogSourc
487504
}
488505
}
489506

490-
func (o *Operator) syncSubscriptions(obj interface{}) error {
491-
sub, ok := obj.(*v1alpha1.Subscription)
507+
func (o *Operator) syncResolvingNamespace(obj interface{}) error {
508+
ns, ok := obj.(*corev1.Namespace)
492509
if !ok {
493510
o.Log.Debugf("wrong type: %#v", obj)
494-
return fmt.Errorf("casting Subscription failed")
511+
return fmt.Errorf("casting Namespace failed")
495512
}
496-
namespace := sub.GetNamespace()
497-
513+
namespace := ns.GetName()
498514
logger := o.Log.WithFields(logrus.Fields{
499-
"sub": sub.GetName(),
500-
"namespace": sub.GetNamespace(),
501-
"source": sub.Spec.CatalogSource,
502-
"pkg": sub.Spec.Package,
503-
"channel": sub.Spec.Channel,
515+
"namespace": namespace,
504516
})
505-
506-
// record the current state of the desired corresponding CSV in the status. no-op if we don't know the csv yet.
507-
sub, err := o.ensureSubscriptionCSVState(logger, sub)
508-
if err != nil {
509-
return err
510-
}
511-
512-
// return early if the subscription is up to date
513-
if o.nothingToUpdate(logger, sub) {
514-
return nil
515-
}
516-
517517
// get the set of sources that should be used for resolution and best-effort get their connections working
518-
logger.Debugf("resolving sources for %s", namespace)
518+
logger.Debug("resolving sources")
519519
resolverSources := o.ensureResolverSources(logger, namespace)
520520

521+
logger.Debug("resolving subscriptions in namespace")
522+
521523
// resolve a set of steps to apply to a cluster, a set of subscriptions to create/update, and any errors
522524
steps, subs, err := o.resolver.ResolveSteps(namespace, resolver.NewNamespaceSourceQuerier(resolverSources))
523525
if err != nil {
@@ -533,7 +535,6 @@ func (o *Operator) syncSubscriptions(obj interface{}) error {
533535
break
534536
}
535537
}
536-
537538
installplanReference, err := o.createInstallPlan(namespace, subs, installPlanApproval, steps)
538539
if err != nil {
539540
logger.WithError(err).Debug("error creating installplan")
@@ -547,6 +548,37 @@ func (o *Operator) syncSubscriptions(obj interface{}) error {
547548
return nil
548549
}
549550

551+
func (o *Operator) syncSubscriptions(obj interface{}) error {
552+
sub, ok := obj.(*v1alpha1.Subscription)
553+
if !ok {
554+
o.Log.Debugf("wrong type: %#v", obj)
555+
return fmt.Errorf("casting Subscription failed")
556+
}
557+
558+
logger := o.Log.WithFields(logrus.Fields{
559+
"sub": sub.GetName(),
560+
"namespace": sub.GetNamespace(),
561+
"source": sub.Spec.CatalogSource,
562+
"pkg": sub.Spec.Package,
563+
"channel": sub.Spec.Channel,
564+
})
565+
566+
// record the current state of the desired corresponding CSV in the status. no-op if we don't know the csv yet.
567+
sub, err := o.ensureSubscriptionCSVState(logger, sub)
568+
if err != nil {
569+
return err
570+
}
571+
572+
// return early if the subscription is up to date
573+
if o.nothingToUpdate(logger, sub) {
574+
return nil
575+
}
576+
577+
o.namespaceResolveQueue.AddRateLimited(sub.GetNamespace())
578+
579+
return nil
580+
}
581+
550582
func (o *Operator) ensureResolverSources(logger *logrus.Entry, namespace string) map[resolver.CatalogKey]registryclient.Interface {
551583
// TODO: record connection status onto an object
552584
resolverSources := make(map[resolver.CatalogKey]registryclient.Interface, 0)

test/e2e/catalog_e2e_test.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,10 @@ func TestConfigMapUpdateTriggersRegistryPodRollout(t *testing.T) {
220220
require.NotNil(t, subscription)
221221
_, err = fetchCSV(t, crc, subscription.Status.CurrentCSV, testNamespace, buildCSVConditionChecker(v1alpha1.CSVPhaseSucceeded))
222222
require.NoError(t, err)
223+
224+
ipList, err := crc.OperatorsV1alpha1().InstallPlans(testNamespace).List(metav1.ListOptions{})
225+
require.NoError(t, err)
226+
require.Equal(t, 1, len(ipList.Items))
223227
}
224228

225229
func TestConfigMapReplaceTriggersRegistryPodRollout(t *testing.T) {

0 commit comments

Comments
 (0)