diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go b/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go index 44f38ef521..b7fb0b9896 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go @@ -62,7 +62,7 @@ import ( olmerrors "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/errors" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/subscription" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/pruning" + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/listerwatcher" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/grpc" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler" @@ -228,38 +228,53 @@ func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clo // Fields are pruned from local copies of the objects managed // by this informer in order to reduce cached size. - prunedCSVInformer := cache.NewSharedIndexInformer( - pruning.NewListerWatcher(op.client, metav1.NamespaceAll, + prunedCSVInformer := cache.NewSharedIndexInformerWithOptions( + listerwatcher.NewListerWatcher( + op.client, + metav1.NamespaceAll, func(options *metav1.ListOptions) { options.LabelSelector = fmt.Sprintf("!%s", v1alpha1.CopiedLabelKey) }, - pruning.PrunerFunc(func(csv *v1alpha1.ClusterServiceVersion) { - *csv = v1alpha1.ClusterServiceVersion{ - TypeMeta: csv.TypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: csv.Name, - Namespace: csv.Namespace, - Labels: csv.Labels, - Annotations: csv.Annotations, - }, - Spec: v1alpha1.ClusterServiceVersionSpec{ - CustomResourceDefinitions: csv.Spec.CustomResourceDefinitions, - APIServiceDefinitions: csv.Spec.APIServiceDefinitions, - Replaces: csv.Spec.Replaces, - Version: csv.Spec.Version, - }, - Status: v1alpha1.ClusterServiceVersionStatus{ - Phase: csv.Status.Phase, - Reason: csv.Status.Reason, - }, - } - })), + ), &v1alpha1.ClusterServiceVersion{}, - resyncPeriod(), - cache.Indexers{ - cache.NamespaceIndex: cache.MetaNamespaceIndexFunc, + cache.SharedIndexInformerOptions{ + ResyncPeriod: resyncPeriod(), + Indexers: cache.Indexers{ + cache.NamespaceIndex: cache.MetaNamespaceIndexFunc, + }, }, ) + + // Transformed the CSV to be just the necessary data + prunedCSVTransformFunc := func(i interface{}) (interface{}, error) { + if csv, ok := i.(*v1alpha1.ClusterServiceVersion); ok { + *csv = v1alpha1.ClusterServiceVersion{ + TypeMeta: csv.TypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: csv.Name, + Namespace: csv.Namespace, + Labels: csv.Labels, + Annotations: csv.Annotations, + }, + Spec: v1alpha1.ClusterServiceVersionSpec{ + CustomResourceDefinitions: csv.Spec.CustomResourceDefinitions, + APIServiceDefinitions: csv.Spec.APIServiceDefinitions, + Replaces: csv.Spec.Replaces, + Version: csv.Spec.Version, + }, + Status: v1alpha1.ClusterServiceVersionStatus{ + Phase: csv.Status.Phase, + Reason: csv.Status.Reason, + }, + } + return csv, nil + } + return nil, fmt.Errorf("unable to convert input to CSV") + } + + if err := prunedCSVInformer.SetTransform(prunedCSVTransformFunc); err != nil { + return nil, err + } csvLister := operatorsv1alpha1listers.NewClusterServiceVersionLister(prunedCSVInformer.GetIndexer()) op.lister.OperatorsV1alpha1().RegisterClusterServiceVersionLister(metav1.NamespaceAll, csvLister) if err := op.RegisterInformer(prunedCSVInformer); err != nil { diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/internal/listerwatcher/listerwatcher.go b/staging/operator-lifecycle-manager/pkg/controller/operators/internal/listerwatcher/listerwatcher.go new file mode 100644 index 0000000000..59bac9f9f4 --- /dev/null +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/internal/listerwatcher/listerwatcher.go @@ -0,0 +1,25 @@ +package listerwatcher + +import ( + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/tools/cache" + + "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" +) + +func NewListerWatcher(client versioned.Interface, namespace string, override func(*metav1.ListOptions)) cache.ListerWatcher { + return &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + override(&options) + return client.OperatorsV1alpha1().ClusterServiceVersions(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + override(&options) + return client.OperatorsV1alpha1().ClusterServiceVersions(namespace).Watch(context.TODO(), options) + }, + } +} diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/internal/pruning/listerwatcher.go b/staging/operator-lifecycle-manager/pkg/controller/operators/internal/pruning/listerwatcher.go deleted file mode 100644 index 6aa3ce5271..0000000000 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/internal/pruning/listerwatcher.go +++ /dev/null @@ -1,52 +0,0 @@ -package pruning - -import ( - "context" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/tools/cache" - - "github.com/operator-framework/api/pkg/operators/v1alpha1" - "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" -) - -type Pruner interface { - Prune(*v1alpha1.ClusterServiceVersion) -} - -type PrunerFunc func(*v1alpha1.ClusterServiceVersion) - -func (f PrunerFunc) Prune(csv *v1alpha1.ClusterServiceVersion) { - f(csv) -} - -func NewListerWatcher(client versioned.Interface, namespace string, override func(*metav1.ListOptions), p Pruner) cache.ListerWatcher { - return &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - override(&options) - list, err := client.OperatorsV1alpha1().ClusterServiceVersions(namespace).List(context.TODO(), options) - if err != nil { - return list, err - } - for i := range list.Items { - p.Prune(&list.Items[i]) - } - return list, nil - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - override(&options) - w, err := client.OperatorsV1alpha1().ClusterServiceVersions(namespace).Watch(context.TODO(), options) - if err != nil { - return w, err - } - return watch.Filter(w, watch.FilterFunc(func(e watch.Event) (watch.Event, bool) { - if csv, ok := e.Object.(*v1alpha1.ClusterServiceVersion); ok { - p.Prune(csv) - } - return e, true - })), nil - }, - } -} diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operator.go b/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operator.go index f7ad0c07b6..e565b5be31 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operator.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operator.go @@ -8,8 +8,6 @@ import ( "sync" "time" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/labeller" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/plugins" "github.com/sirupsen/logrus" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -42,11 +40,16 @@ import ( operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" "github.com/operator-framework/api/pkg/operators/v1alpha1" + "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/informers/externalversions" + operatorsv1alpha1listers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/certs" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/listerwatcher" + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/labeller" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/overrides" + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/plugins" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/clients" csvutility "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/csv" @@ -62,6 +65,14 @@ import ( "github.com/operator-framework/operator-lifecycle-manager/pkg/metrics" ) +const ( + // These annotation keys are intentionally invalid -- all writes + // to copied CSVs are regenerated from the corresponding non-copied CSV, + // so it should never be transmitted back to the API server. + copyCSVStatusHash = "$copyhash-status" + copyCSVSpecHash = "$copyhash-spec" +) + var ( ErrRequirementsNotMet = errors.New("requirements were not met") ErrCRDOwnerConflict = errors.New("conflicting CRD owner in namespace") @@ -81,12 +92,12 @@ type Operator struct { client versioned.Interface lister operatorlister.OperatorLister protectedCopiedCSVNamespaces map[string]struct{} - copiedCSVLister metadatalister.Lister + copiedCSVLister operatorsv1alpha1listers.ClusterServiceVersionLister ogQueueSet *queueinformer.ResourceQueueSet csvQueueSet *queueinformer.ResourceQueueSet olmConfigQueue workqueue.TypedRateLimitingInterface[types.NamespacedName] csvCopyQueueSet *queueinformer.ResourceQueueSet - copiedCSVGCQueueSet *queueinformer.ResourceQueueSet + copiedCSVQueueSet *queueinformer.ResourceQueueSet nsQueueSet workqueue.TypedRateLimitingInterface[types.NamespacedName] apiServiceQueue workqueue.TypedRateLimitingInterface[types.NamespacedName] csvIndexers map[string]cache.Indexer @@ -205,8 +216,8 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat Name: "olmConfig", }), - csvCopyQueueSet: queueinformer.NewEmptyResourceQueueSet(), - copiedCSVGCQueueSet: queueinformer.NewEmptyResourceQueueSet(), + csvCopyQueueSet: queueinformer.NewEmptyResourceQueueSet(), + copiedCSVQueueSet: queueinformer.NewEmptyResourceQueueSet(), apiServiceQueue: workqueue.NewTypedRateLimitingQueueWithConfig[types.NamespacedName]( workqueue.DefaultTypedControllerRateLimiter[types.NamespacedName](), workqueue.TypedRateLimitingQueueConfig[types.NamespacedName]{ @@ -293,42 +304,93 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat return nil, err } - // A separate informer solely for CSV copies. Object metadata requests are used + // A separate informer solely for CSV copies. Fields + // are pruned from local copies of the objects managed // by this informer in order to reduce cached size. - gvr := v1alpha1.SchemeGroupVersion.WithResource("clusterserviceversions") - copiedCSVInformer := metadatainformer.NewFilteredMetadataInformer( - config.metadataClient, - gvr, - namespace, - config.resyncPeriod(), - cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, - func(options *metav1.ListOptions) { - options.LabelSelector = v1alpha1.CopiedLabelKey + copiedCSVInformer := cache.NewSharedIndexInformerWithOptions( + listerwatcher.NewListerWatcher( + op.client, + namespace, + func(opts *metav1.ListOptions) { + opts.LabelSelector = v1alpha1.CopiedLabelKey + }, + ), + &v1alpha1.ClusterServiceVersion{}, + cache.SharedIndexInformerOptions{ + ResyncPeriod: config.resyncPeriod(), + Indexers: cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, }, - ).Informer() - op.copiedCSVLister = metadatalister.New(copiedCSVInformer.GetIndexer(), gvr) + ) + + // Transform the copied CSV to be just the Metadata + // However, because we have the full copied CSV, we can calculate + // a hash over the full CSV to store as an annotation + copiedCSVTransformFunc := func(i interface{}) (interface{}, error) { + if csv, ok := i.(*v1alpha1.ClusterServiceVersion); ok { + specHash, statusHash, err := copyableCSVHash(csv) + if err != nil { + return nil, err + } + *csv = v1alpha1.ClusterServiceVersion{ + TypeMeta: csv.TypeMeta, + ObjectMeta: csv.ObjectMeta, + Spec: v1alpha1.ClusterServiceVersionSpec{}, + Status: v1alpha1.ClusterServiceVersionStatus{}, + } + // copy only the nececessary labels + labels := csv.Labels + csv.Labels = make(map[string]string, 2) + if l, ok := labels[v1alpha1.CopiedLabelKey]; ok { + csv.Labels[v1alpha1.CopiedLabelKey] = l + } + if l, ok := labels[install.OLMManagedLabelKey]; ok { + csv.Labels[install.OLMManagedLabelKey] = l + } + + // copy only the nececessary annotations + annotations := csv.Annotations + csv.Annotations = make(map[string]string, 4) + if a, ok := annotations[operatorsv1.OperatorGroupAnnotationKey]; ok { + csv.Annotations[operatorsv1.OperatorGroupAnnotationKey] = a + } + if a, ok := annotations[operatorsv1.OperatorGroupNamespaceAnnotationKey]; ok { + csv.Annotations[operatorsv1.OperatorGroupNamespaceAnnotationKey] = a + } + // fake CSV hashes for tracking purposes only + csv.Annotations[copyCSVSpecHash] = specHash + csv.Annotations[copyCSVStatusHash] = statusHash + return csv, nil + } + return nil, fmt.Errorf("unable to convert input to CSV") + } + + if err := copiedCSVInformer.SetTransform(copiedCSVTransformFunc); err != nil { + return nil, err + } + op.copiedCSVLister = operatorsv1alpha1listers.NewClusterServiceVersionLister(copiedCSVInformer.GetIndexer()) informersByNamespace[namespace].CopiedCSVInformer = copiedCSVInformer informersByNamespace[namespace].CopiedCSVLister = op.copiedCSVLister - // Register separate queue for gcing copied csvs - copiedCSVGCQueue := workqueue.NewTypedRateLimitingQueueWithConfig[types.NamespacedName]( + // Register separate queue for gcing/syncing/deletion of copied csvs + copiedCSVQueue := workqueue.NewTypedRateLimitingQueueWithConfig[types.NamespacedName]( workqueue.DefaultTypedControllerRateLimiter[types.NamespacedName](), workqueue.TypedRateLimitingQueueConfig[types.NamespacedName]{ - Name: fmt.Sprintf("%s/csv-gc", namespace), + Name: fmt.Sprintf("%s/csv-copied-sync", namespace), }) - op.copiedCSVGCQueueSet.Set(namespace, copiedCSVGCQueue) - copiedCSVGCQueueInformer, err := queueinformer.NewQueueInformer( + op.copiedCSVQueueSet.Set(namespace, copiedCSVQueue) + copiedCSVQueueInformer, err := queueinformer.NewQueueInformer( ctx, queueinformer.WithInformer(copiedCSVInformer), queueinformer.WithLogger(op.logger), - queueinformer.WithQueue(copiedCSVGCQueue), + queueinformer.WithQueue(copiedCSVQueue), queueinformer.WithIndexer(copiedCSVInformer.GetIndexer()), - queueinformer.WithSyncer(queueinformer.LegacySyncHandler(op.syncGcCsv).ToSyncer()), + queueinformer.WithSyncer(queueinformer.LegacySyncHandler(op.syncCopiedCsv).ToSyncer()), + queueinformer.WithDeletionHandler(op.handleCopiedCSVDeletion), ) if err != nil { return nil, err } - if err := op.RegisterQueueInformer(copiedCSVGCQueueInformer); err != nil { + if err := op.RegisterQueueInformer(copiedCSVQueueInformer); err != nil { return nil, err } @@ -1205,7 +1267,7 @@ func (a *Operator) handleClusterServiceVersionDeletion(obj interface{}) { for _, namespace := range namespaces { if namespace != operatorNamespace { logger.WithField("targetNamespace", namespace).Debug("requeueing child csv for deletion") - if err := a.copiedCSVGCQueueSet.Requeue(namespace, clusterServiceVersion.GetName()); err != nil { + if err := a.copiedCSVQueueSet.Requeue(namespace, clusterServiceVersion.GetName()); err != nil { logger.WithError(err).Warn("unable to requeue") } } @@ -1272,7 +1334,7 @@ func (a *Operator) handleClusterServiceVersionDeletion(obj interface{}) { } } -func (a *Operator) removeDanglingChildCSVs(csv *metav1.PartialObjectMetadata) error { +func (a *Operator) removeDanglingChildCSVs(csv *v1alpha1.ClusterServiceVersion) error { logger := a.logger.WithFields(logrus.Fields{ "id": queueinformer.NewLoopID(), "csv": csv.GetName(), @@ -1320,7 +1382,7 @@ func (a *Operator) removeDanglingChildCSVs(csv *metav1.PartialObjectMetadata) er return nil } -func (a *Operator) deleteChild(csv *metav1.PartialObjectMetadata, logger *logrus.Entry) error { +func (a *Operator) deleteChild(csv *v1alpha1.ClusterServiceVersion, logger *logrus.Entry) error { logger.Debug("gcing csv") return a.client.OperatorsV1alpha1().ClusterServiceVersions(csv.GetNamespace()).Delete(context.TODO(), csv.GetName(), metav1.DeleteOptions{}) } @@ -1704,9 +1766,13 @@ func (a *Operator) syncCopyCSV(obj interface{}) (syncError error) { return } - logger.WithFields(logrus.Fields{ - "targetNamespaces": strings.Join(operatorGroup.Status.Namespaces, ","), - }).Debug("copying csv to targets") + if len(operatorGroup.Status.Namespaces) == 1 && operatorGroup.Status.Namespaces[0] == "" { + logger.Debug("copying csv to targets in all namespaces") + } else { + logger.WithFields(logrus.Fields{ + "targetNamespaces": strings.Join(operatorGroup.Status.Namespaces, ","), + }).Debug("copying csv to targets") + } copiedCSVsAreEnabled, err := a.copiedCSVsAreEnabled() if err != nil { @@ -1864,17 +1930,52 @@ func (a *Operator) createCSVCopyingDisabledEvent(csv *v1alpha1.ClusterServiceVer return nil } -func (a *Operator) syncGcCsv(obj interface{}) (syncError error) { - clusterServiceVersion, ok := obj.(*metav1.PartialObjectMetadata) +func (a *Operator) requeueParentCsv(csv *v1alpha1.ClusterServiceVersion) error { + name := csv.GetName() + copiedNamespace := csv.GetNamespace() + a.logger.WithField("csv", fmt.Sprintf("%s/%s", copiedNamespace, name)).Debug("syncing copied CSV") + + // Requeue parent CSV to deal with any changes to the copied CSV + copiedFromNamespace, ok := csv.GetLabels()[v1alpha1.CopiedLabelKey] + if !ok { + a.logger.Infof("no %q label found in CSV, skipping requeue", v1alpha1.CopiedLabelKey) + return nil + } + return a.csvCopyQueueSet.Requeue(copiedFromNamespace, name) +} + +func (a *Operator) handleCopiedCSVDeletion(obj interface{}) { + csv, ok := obj.(*v1alpha1.ClusterServiceVersion) + if !ok { + a.logger.Debugf("casting ClusterServiceVersion failed: wrong type: %#v", obj) + return + } + if !v1alpha1.IsCopied(csv) { + return + } + + // Trigger parent reconciliation + a.requeueParentCsv(csv) +} + +func (a *Operator) syncCopiedCsv(obj interface{}) error { + csv, ok := obj.(*v1alpha1.ClusterServiceVersion) if !ok { a.logger.Debugf("wrong type: %#v", obj) return fmt.Errorf("casting ClusterServiceVersion failed") } - if v1alpha1.IsCopied(clusterServiceVersion) { - syncError = a.removeDanglingChildCSVs(clusterServiceVersion) - return + if !v1alpha1.IsCopied(csv) { + return nil } - return + + // check for any garbage collection + err := a.removeDanglingChildCSVs(csv) + if err != nil { + return err + } + + // Trigger parent reconciliation + return a.requeueParentCsv(csv) } // operatorGroupFromAnnotations returns the OperatorGroup for the CSV only if the CSV is active one in the group diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operator_test.go b/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operator_test.go index a28a67bdd4..fbaa32492a 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operator_test.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operator_test.go @@ -20,7 +20,6 @@ import ( "github.com/google/go-cmp/cmp" configfake "github.com/openshift/client-go/config/clientset/versioned/fake" - hashutil "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubernetes/pkg/util/hash" "github.com/sirupsen/logrus" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -44,6 +43,7 @@ import ( metadatafake "k8s.io/client-go/metadata/fake" "k8s.io/client-go/pkg/version" "k8s.io/client-go/rest" + clienttesting "k8s.io/client-go/testing" "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" apiregistrationv1 "k8s.io/kube-aggregator/pkg/apis/apiregistration/v1" @@ -54,7 +54,6 @@ import ( operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" "github.com/operator-framework/api/pkg/operators/v1alpha1" opregistry "github.com/operator-framework/operator-registry/pkg/registry" - clienttesting "k8s.io/client-go/testing" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/fake" @@ -64,6 +63,7 @@ import ( resolvercache "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/clientfake" csvutility "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/csv" + hashutil "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/kubernetes/pkg/util/hash" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/labeler" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister" @@ -5050,7 +5050,10 @@ func TestSyncOperatorGroups(t *testing.T) { }, targetNamespace: { withLabels( - withAnnotations(targetCSV.DeepCopy(), map[string]string{operatorsv1.OperatorGroupAnnotationKey: "operator-group-1", operatorsv1.OperatorGroupNamespaceAnnotationKey: operatorNamespace}), + withAnnotations(targetCSV.DeepCopy(), map[string]string{ + operatorsv1.OperatorGroupAnnotationKey: "operator-group-1", + operatorsv1.OperatorGroupNamespaceAnnotationKey: operatorNamespace, + }), labels.Merge(targetCSV.GetLabels(), map[string]string{v1alpha1.CopiedLabelKey: operatorNamespace}), ), &rbacv1.Role{ @@ -5155,7 +5158,10 @@ func TestSyncOperatorGroups(t *testing.T) { }, targetNamespace: { withLabels( - withAnnotations(targetCSV.DeepCopy(), map[string]string{operatorsv1.OperatorGroupAnnotationKey: "operator-group-1", operatorsv1.OperatorGroupNamespaceAnnotationKey: operatorNamespace}), + withAnnotations(targetCSV.DeepCopy(), map[string]string{ + operatorsv1.OperatorGroupAnnotationKey: "operator-group-1", + operatorsv1.OperatorGroupNamespaceAnnotationKey: operatorNamespace, + }), labels.Merge(targetCSV.GetLabels(), map[string]string{v1alpha1.CopiedLabelKey: operatorNamespace}), ), &rbacv1.Role{ @@ -5312,7 +5318,10 @@ func TestSyncOperatorGroups(t *testing.T) { }, targetNamespace: { withLabels( - withAnnotations(targetCSV.DeepCopy(), map[string]string{operatorsv1.OperatorGroupAnnotationKey: "operator-group-1", operatorsv1.OperatorGroupNamespaceAnnotationKey: operatorNamespace}), + withAnnotations(targetCSV.DeepCopy(), map[string]string{ + operatorsv1.OperatorGroupAnnotationKey: "operator-group-1", + operatorsv1.OperatorGroupNamespaceAnnotationKey: operatorNamespace, + }), labels.Merge(targetCSV.GetLabels(), map[string]string{v1alpha1.CopiedLabelKey: operatorNamespace}), ), }, diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operatorgroup.go b/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operatorgroup.go index 94e17b6ae4..739541ce9f 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operatorgroup.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operatorgroup.go @@ -361,7 +361,7 @@ func (a *Operator) pruneProvidedAPIs(group *operatorsv1.OperatorGroup, groupProv } // Prune providedAPIs annotation if the cluster has fewer providedAPIs (handles CSV deletion) - //if intersection := groupProvidedAPIs.Intersection(providedAPIsFromCSVs); len(intersection) < len(groupProvidedAPIs) { + // if intersection := groupProvidedAPIs.Intersection(providedAPIsFromCSVs); len(intersection) < len(groupProvidedAPIs) { if len(intersection) < len(groupProvidedAPIs) { difference := groupProvidedAPIs.Difference(intersection) logger := logger.WithFields(logrus.Fields{ @@ -704,7 +704,7 @@ func (a *Operator) ensureCSVsInNamespaces(csv *v1alpha1.ClusterServiceVersion, o var copyPrototype v1alpha1.ClusterServiceVersion csvCopyPrototype(csv, ©Prototype) - nonstatus, status, err := copyableCSVHash(©Prototype) + specHash, statusHash, err := copyableCSVHash(©Prototype) if err != nil { return err } @@ -715,7 +715,7 @@ func (a *Operator) ensureCSVsInNamespaces(csv *v1alpha1.ClusterServiceVersion, o } if targets.Contains(ns.GetName()) { var targetCSV *v1alpha1.ClusterServiceVersion - if targetCSV, err = a.copyToNamespace(©Prototype, csv.GetNamespace(), ns.GetName(), nonstatus, status); err != nil { + if targetCSV, err = a.copyToNamespace(©Prototype, csv.GetNamespace(), ns.GetName(), specHash, statusHash); err != nil { logger.WithError(err).Debug("error copying to target") continue } @@ -779,21 +779,21 @@ func copyableCSVHash(original *v1alpha1.ClusterServiceVersion) (string, string, Spec: original.Spec, } - newHash, err := hashutil.DeepHashObject(&shallow) + specHash, err := hashutil.DeepHashObject(&shallow) if err != nil { return "", "", err } - originalHash, err := hashutil.DeepHashObject(&original.Status) + statusHash, err := hashutil.DeepHashObject(&original.Status) if err != nil { return "", "", err } - return newHash, originalHash, nil + return specHash, statusHash, nil } // If returned error is not nil, the returned ClusterServiceVersion // has only the Name, Namespace, and UID fields set. -func (a *Operator) copyToNamespace(prototype *v1alpha1.ClusterServiceVersion, nsFrom, nsTo, nonstatus, status string) (*v1alpha1.ClusterServiceVersion, error) { +func (a *Operator) copyToNamespace(prototype *v1alpha1.ClusterServiceVersion, nsFrom, nsTo, specHash, statusHash string) (*v1alpha1.ClusterServiceVersion, error) { if nsFrom == nsTo { return nil, fmt.Errorf("bug: can not copy to active namespace %v", nsFrom) } @@ -802,7 +802,7 @@ func (a *Operator) copyToNamespace(prototype *v1alpha1.ClusterServiceVersion, ns prototype.ResourceVersion = "" prototype.UID = "" - existing, err := a.copiedCSVLister.Namespace(nsTo).Get(prototype.GetName()) + existing, err := a.copiedCSVLister.ClusterServiceVersions(nsTo).Get(prototype.GetName()) if apierrors.IsNotFound(err) { created, err := a.client.OperatorsV1alpha1().ClusterServiceVersions(nsTo).Create(context.TODO(), prototype, metav1.CreateOptions{}) if err != nil { @@ -826,11 +826,12 @@ func (a *Operator) copyToNamespace(prototype *v1alpha1.ClusterServiceVersion, ns prototype.Namespace = existing.Namespace prototype.ResourceVersion = existing.ResourceVersion prototype.UID = existing.UID - existingNonStatus := existing.Annotations["$copyhash-nonstatus"] - existingStatus := existing.Annotations["$copyhash-status"] + // Get the non-status and status hash of the existing copied CSV + existingSpecHash := existing.Annotations[copyCSVSpecHash] + existingStatusHash := existing.Annotations[copyCSVStatusHash] var updated *v1alpha1.ClusterServiceVersion - if existingNonStatus != nonstatus { + if existingSpecHash != specHash { if updated, err = a.client.OperatorsV1alpha1().ClusterServiceVersions(nsTo).Update(context.TODO(), prototype, metav1.UpdateOptions{}); err != nil { return nil, fmt.Errorf("failed to update: %w", err) } @@ -839,7 +840,7 @@ func (a *Operator) copyToNamespace(prototype *v1alpha1.ClusterServiceVersion, ns updated = prototype } - if existingStatus != status { + if existingStatusHash != statusHash { updated.Status = prototype.Status if _, err = a.client.OperatorsV1alpha1().ClusterServiceVersions(nsTo).UpdateStatus(context.TODO(), updated, metav1.UpdateOptions{}); err != nil { return nil, fmt.Errorf("failed to update status: %w", err) @@ -855,7 +856,7 @@ func (a *Operator) copyToNamespace(prototype *v1alpha1.ClusterServiceVersion, ns } func (a *Operator) pruneFromNamespace(operatorGroupName, namespace string) error { - fetchedCSVs, err := a.copiedCSVLister.Namespace(namespace).List(labels.Everything()) + fetchedCSVs, err := a.copiedCSVLister.ClusterServiceVersions(namespace).List(labels.Everything()) if err != nil { return err } @@ -863,7 +864,7 @@ func (a *Operator) pruneFromNamespace(operatorGroupName, namespace string) error for _, csv := range fetchedCSVs { if v1alpha1.IsCopied(csv) && csv.GetAnnotations()[operatorsv1.OperatorGroupAnnotationKey] == operatorGroupName { a.logger.Debugf("Found CSV '%v' in namespace %v to delete", csv.GetName(), namespace) - if err := a.copiedCSVGCQueueSet.Requeue(csv.GetNamespace(), csv.GetName()); err != nil { + if err := a.copiedCSVQueueSet.Requeue(csv.GetNamespace(), csv.GetName()); err != nil { return err } } diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operatorgroup_test.go b/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operatorgroup_test.go index 628e9ec3dd..bf7c2f688e 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operatorgroup_test.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/olm/operatorgroup_test.go @@ -4,7 +4,6 @@ import ( "fmt" "testing" - "github.com/google/go-cmp/cmp" "github.com/sirupsen/logrus/hooks/test" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -12,12 +11,13 @@ import ( "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/metadata/metadatalister" ktesting "k8s.io/client-go/testing" "github.com/operator-framework/api/pkg/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned/fake" + listersv1alpha1 "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver/cache" + "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorlister/operatorlisterfakes" "github.com/operator-framework/operator-registry/pkg/registry" ) @@ -31,7 +31,7 @@ func TestCopyToNamespace(t *testing.T) { Hash string StatusHash string Prototype v1alpha1.ClusterServiceVersion - ExistingCopy *metav1.PartialObjectMetadata + ExistingCopy *v1alpha1.ClusterServiceVersion ExpectedResult *v1alpha1.ClusterServiceVersion ExpectedError error ExpectedActions []ktesting.Action @@ -107,7 +107,7 @@ func TestCopyToNamespace(t *testing.T) { Phase: "waxing gibbous", }, }, - ExistingCopy: &metav1.PartialObjectMetadata{ + ExistingCopy: &v1alpha1.ClusterServiceVersion{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "to", @@ -160,15 +160,15 @@ func TestCopyToNamespace(t *testing.T) { Phase: "waxing gibbous", }, }, - ExistingCopy: &metav1.PartialObjectMetadata{ + ExistingCopy: &v1alpha1.ClusterServiceVersion{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "to", UID: "uid", ResourceVersion: "42", Annotations: map[string]string{ - "$copyhash-nonstatus": "hn", - "$copyhash-status": "hs-2", + "$copyhash-spec": "hn", + "$copyhash-status": "hs-2", }, }, }, @@ -213,15 +213,15 @@ func TestCopyToNamespace(t *testing.T) { Phase: "waxing gibbous", }, }, - ExistingCopy: &metav1.PartialObjectMetadata{ + ExistingCopy: &v1alpha1.ClusterServiceVersion{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "to", UID: "uid", ResourceVersion: "42", Annotations: map[string]string{ - "$copyhash-nonstatus": "hn-2", - "$copyhash-status": "hs-2", + "$copyhash-spec": "hn-2", + "$copyhash-status": "hs-2", }, }, }, @@ -274,14 +274,14 @@ func TestCopyToNamespace(t *testing.T) { Name: "name", }, }, - ExistingCopy: &metav1.PartialObjectMetadata{ + ExistingCopy: &v1alpha1.ClusterServiceVersion{ ObjectMeta: metav1.ObjectMeta{ Name: "name", Namespace: "to", UID: "uid", Annotations: map[string]string{ - "$copyhash-nonstatus": "hn", - "$copyhash-status": "hs", + "$copyhash-spec": "hn", + "$copyhash-status": "hs", }, }, }, @@ -295,20 +295,21 @@ func TestCopyToNamespace(t *testing.T) { }, } { t.Run(tc.Name, func(t *testing.T) { + lister := &operatorlisterfakes.FakeOperatorLister{} + v1alpha1lister := &operatorlisterfakes.FakeOperatorsV1alpha1Lister{} + lister.OperatorsV1alpha1Returns(v1alpha1lister) client := fake.NewSimpleClientset() - var lister metadatalister.Lister + if tc.ExistingCopy != nil { - client = fake.NewSimpleClientset(&v1alpha1.ClusterServiceVersion{ - ObjectMeta: tc.ExistingCopy.ObjectMeta, - }) - lister = FakeClusterServiceVersionLister{tc.ExistingCopy} + client = fake.NewSimpleClientset(tc.ExistingCopy) + v1alpha1lister.ClusterServiceVersionListerReturns(FakeClusterServiceVersionLister{tc.ExistingCopy}) } else { - lister = FakeClusterServiceVersionLister{{}} + v1alpha1lister.ClusterServiceVersionListerReturns(FakeClusterServiceVersionLister(nil)) } logger, _ := test.NewNullLogger() o := &Operator{ - copiedCSVLister: lister, + copiedCSVLister: v1alpha1lister.ClusterServiceVersionLister(), client: client, logger: logger, } @@ -320,25 +321,21 @@ func TestCopyToNamespace(t *testing.T) { } else { require.EqualError(t, err, tc.ExpectedError.Error()) } - if diff := cmp.Diff(tc.ExpectedResult, result); diff != "" { - t.Errorf("incorrect result: %v", diff) - } + assert.Equal(t, tc.ExpectedResult, result) actions := client.Actions() if len(actions) == 0 { actions = nil } - if diff := cmp.Diff(tc.ExpectedActions, actions); diff != "" { - t.Errorf("incorrect actions: %v", diff) - } + assert.Equal(t, tc.ExpectedActions, actions) }) } } -type FakeClusterServiceVersionLister []*metav1.PartialObjectMetadata +type FakeClusterServiceVersionLister []*v1alpha1.ClusterServiceVersion -func (l FakeClusterServiceVersionLister) List(selector labels.Selector) ([]*metav1.PartialObjectMetadata, error) { - var result []*metav1.PartialObjectMetadata +func (l FakeClusterServiceVersionLister) List(selector labels.Selector) ([]*v1alpha1.ClusterServiceVersion, error) { + var result []*v1alpha1.ClusterServiceVersion for _, csv := range l { if !selector.Matches(labels.Set(csv.GetLabels())) { continue @@ -348,8 +345,8 @@ func (l FakeClusterServiceVersionLister) List(selector labels.Selector) ([]*meta return result, nil } -func (l FakeClusterServiceVersionLister) Namespace(namespace string) metadatalister.NamespaceLister { - var filtered []*metav1.PartialObjectMetadata +func (l FakeClusterServiceVersionLister) ClusterServiceVersions(namespace string) listersv1alpha1.ClusterServiceVersionNamespaceLister { + var filtered []*v1alpha1.ClusterServiceVersion for _, csv := range l { if csv.GetNamespace() != namespace { continue @@ -359,7 +356,7 @@ func (l FakeClusterServiceVersionLister) Namespace(namespace string) metadatalis return FakeClusterServiceVersionLister(filtered) } -func (l FakeClusterServiceVersionLister) Get(name string) (*metav1.PartialObjectMetadata, error) { +func (l FakeClusterServiceVersionLister) Get(name string) (*v1alpha1.ClusterServiceVersion, error) { for _, csv := range l { if csv.GetName() == name { return csv, nil @@ -369,8 +366,8 @@ func (l FakeClusterServiceVersionLister) Get(name string) (*metav1.PartialObject } var ( - _ metadatalister.Lister = FakeClusterServiceVersionLister{} - _ metadatalister.NamespaceLister = FakeClusterServiceVersionLister{} + _ listersv1alpha1.ClusterServiceVersionLister = FakeClusterServiceVersionLister{} + _ listersv1alpha1.ClusterServiceVersionNamespaceLister = FakeClusterServiceVersionLister{} ) func TestCSVCopyPrototype(t *testing.T) { diff --git a/staging/operator-lifecycle-manager/pkg/controller/operators/olm/plugins/operator_plugin.go b/staging/operator-lifecycle-manager/pkg/controller/operators/olm/plugins/operator_plugin.go index 0f38f0543b..8cce23c2a4 100644 --- a/staging/operator-lifecycle-manager/pkg/controller/operators/olm/plugins/operator_plugin.go +++ b/staging/operator-lifecycle-manager/pkg/controller/operators/olm/plugins/operator_plugin.go @@ -8,6 +8,7 @@ import ( operatorsv1informers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/informers/externalversions/operators/v1" operatorsv1alpha1informers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/informers/externalversions/operators/v1alpha1" operatorsv2informers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/informers/externalversions/operators/v2" + operatorsv1alpha1listers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer" "github.com/sirupsen/logrus" @@ -33,7 +34,7 @@ type HostOperator interface { type Informers struct { CSVInformer operatorsv1alpha1informers.ClusterServiceVersionInformer CopiedCSVInformer cache.SharedIndexInformer - CopiedCSVLister metadatalister.Lister + CopiedCSVLister operatorsv1alpha1listers.ClusterServiceVersionLister OperatorGroupInformer operatorsv1informers.OperatorGroupInformer OperatorConditionInformer operatorsv2informers.OperatorConditionInformer SubscriptionInformer operatorsv1alpha1informers.SubscriptionInformer diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go index 44f38ef521..b7fb0b9896 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/operator.go @@ -62,7 +62,7 @@ import ( olmerrors "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/errors" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalog/subscription" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/pruning" + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/listerwatcher" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/grpc" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/reconciler" @@ -228,38 +228,53 @@ func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clo // Fields are pruned from local copies of the objects managed // by this informer in order to reduce cached size. - prunedCSVInformer := cache.NewSharedIndexInformer( - pruning.NewListerWatcher(op.client, metav1.NamespaceAll, + prunedCSVInformer := cache.NewSharedIndexInformerWithOptions( + listerwatcher.NewListerWatcher( + op.client, + metav1.NamespaceAll, func(options *metav1.ListOptions) { options.LabelSelector = fmt.Sprintf("!%s", v1alpha1.CopiedLabelKey) }, - pruning.PrunerFunc(func(csv *v1alpha1.ClusterServiceVersion) { - *csv = v1alpha1.ClusterServiceVersion{ - TypeMeta: csv.TypeMeta, - ObjectMeta: metav1.ObjectMeta{ - Name: csv.Name, - Namespace: csv.Namespace, - Labels: csv.Labels, - Annotations: csv.Annotations, - }, - Spec: v1alpha1.ClusterServiceVersionSpec{ - CustomResourceDefinitions: csv.Spec.CustomResourceDefinitions, - APIServiceDefinitions: csv.Spec.APIServiceDefinitions, - Replaces: csv.Spec.Replaces, - Version: csv.Spec.Version, - }, - Status: v1alpha1.ClusterServiceVersionStatus{ - Phase: csv.Status.Phase, - Reason: csv.Status.Reason, - }, - } - })), + ), &v1alpha1.ClusterServiceVersion{}, - resyncPeriod(), - cache.Indexers{ - cache.NamespaceIndex: cache.MetaNamespaceIndexFunc, + cache.SharedIndexInformerOptions{ + ResyncPeriod: resyncPeriod(), + Indexers: cache.Indexers{ + cache.NamespaceIndex: cache.MetaNamespaceIndexFunc, + }, }, ) + + // Transformed the CSV to be just the necessary data + prunedCSVTransformFunc := func(i interface{}) (interface{}, error) { + if csv, ok := i.(*v1alpha1.ClusterServiceVersion); ok { + *csv = v1alpha1.ClusterServiceVersion{ + TypeMeta: csv.TypeMeta, + ObjectMeta: metav1.ObjectMeta{ + Name: csv.Name, + Namespace: csv.Namespace, + Labels: csv.Labels, + Annotations: csv.Annotations, + }, + Spec: v1alpha1.ClusterServiceVersionSpec{ + CustomResourceDefinitions: csv.Spec.CustomResourceDefinitions, + APIServiceDefinitions: csv.Spec.APIServiceDefinitions, + Replaces: csv.Spec.Replaces, + Version: csv.Spec.Version, + }, + Status: v1alpha1.ClusterServiceVersionStatus{ + Phase: csv.Status.Phase, + Reason: csv.Status.Reason, + }, + } + return csv, nil + } + return nil, fmt.Errorf("unable to convert input to CSV") + } + + if err := prunedCSVInformer.SetTransform(prunedCSVTransformFunc); err != nil { + return nil, err + } csvLister := operatorsv1alpha1listers.NewClusterServiceVersionLister(prunedCSVInformer.GetIndexer()) op.lister.OperatorsV1alpha1().RegisterClusterServiceVersionLister(metav1.NamespaceAll, csvLister) if err := op.RegisterInformer(prunedCSVInformer); err != nil { diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/listerwatcher/listerwatcher.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/listerwatcher/listerwatcher.go new file mode 100644 index 0000000000..59bac9f9f4 --- /dev/null +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/listerwatcher/listerwatcher.go @@ -0,0 +1,25 @@ +package listerwatcher + +import ( + "context" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/watch" + "k8s.io/client-go/tools/cache" + + "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" +) + +func NewListerWatcher(client versioned.Interface, namespace string, override func(*metav1.ListOptions)) cache.ListerWatcher { + return &cache.ListWatch{ + ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { + override(&options) + return client.OperatorsV1alpha1().ClusterServiceVersions(namespace).List(context.TODO(), options) + }, + WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { + override(&options) + return client.OperatorsV1alpha1().ClusterServiceVersions(namespace).Watch(context.TODO(), options) + }, + } +} diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/pruning/listerwatcher.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/pruning/listerwatcher.go deleted file mode 100644 index 6aa3ce5271..0000000000 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/pruning/listerwatcher.go +++ /dev/null @@ -1,52 +0,0 @@ -package pruning - -import ( - "context" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/tools/cache" - - "github.com/operator-framework/api/pkg/operators/v1alpha1" - "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" -) - -type Pruner interface { - Prune(*v1alpha1.ClusterServiceVersion) -} - -type PrunerFunc func(*v1alpha1.ClusterServiceVersion) - -func (f PrunerFunc) Prune(csv *v1alpha1.ClusterServiceVersion) { - f(csv) -} - -func NewListerWatcher(client versioned.Interface, namespace string, override func(*metav1.ListOptions), p Pruner) cache.ListerWatcher { - return &cache.ListWatch{ - ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { - override(&options) - list, err := client.OperatorsV1alpha1().ClusterServiceVersions(namespace).List(context.TODO(), options) - if err != nil { - return list, err - } - for i := range list.Items { - p.Prune(&list.Items[i]) - } - return list, nil - }, - WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { - override(&options) - w, err := client.OperatorsV1alpha1().ClusterServiceVersions(namespace).Watch(context.TODO(), options) - if err != nil { - return w, err - } - return watch.Filter(w, watch.FilterFunc(func(e watch.Event) (watch.Event, bool) { - if csv, ok := e.Object.(*v1alpha1.ClusterServiceVersion); ok { - p.Prune(csv) - } - return e, true - })), nil - }, - } -} diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/operator.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/operator.go index f7ad0c07b6..e565b5be31 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/operator.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/operator.go @@ -8,8 +8,6 @@ import ( "sync" "time" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/labeller" - "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/plugins" "github.com/sirupsen/logrus" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" @@ -42,11 +40,16 @@ import ( operatorsv1 "github.com/operator-framework/api/pkg/operators/v1" "github.com/operator-framework/api/pkg/operators/v1alpha1" + "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned" "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/informers/externalversions" + operatorsv1alpha1listers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/certs" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install" + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/listerwatcher" + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/labeller" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/overrides" + "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/plugins" "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/clients" csvutility "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/csv" @@ -62,6 +65,14 @@ import ( "github.com/operator-framework/operator-lifecycle-manager/pkg/metrics" ) +const ( + // These annotation keys are intentionally invalid -- all writes + // to copied CSVs are regenerated from the corresponding non-copied CSV, + // so it should never be transmitted back to the API server. + copyCSVStatusHash = "$copyhash-status" + copyCSVSpecHash = "$copyhash-spec" +) + var ( ErrRequirementsNotMet = errors.New("requirements were not met") ErrCRDOwnerConflict = errors.New("conflicting CRD owner in namespace") @@ -81,12 +92,12 @@ type Operator struct { client versioned.Interface lister operatorlister.OperatorLister protectedCopiedCSVNamespaces map[string]struct{} - copiedCSVLister metadatalister.Lister + copiedCSVLister operatorsv1alpha1listers.ClusterServiceVersionLister ogQueueSet *queueinformer.ResourceQueueSet csvQueueSet *queueinformer.ResourceQueueSet olmConfigQueue workqueue.TypedRateLimitingInterface[types.NamespacedName] csvCopyQueueSet *queueinformer.ResourceQueueSet - copiedCSVGCQueueSet *queueinformer.ResourceQueueSet + copiedCSVQueueSet *queueinformer.ResourceQueueSet nsQueueSet workqueue.TypedRateLimitingInterface[types.NamespacedName] apiServiceQueue workqueue.TypedRateLimitingInterface[types.NamespacedName] csvIndexers map[string]cache.Indexer @@ -205,8 +216,8 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat Name: "olmConfig", }), - csvCopyQueueSet: queueinformer.NewEmptyResourceQueueSet(), - copiedCSVGCQueueSet: queueinformer.NewEmptyResourceQueueSet(), + csvCopyQueueSet: queueinformer.NewEmptyResourceQueueSet(), + copiedCSVQueueSet: queueinformer.NewEmptyResourceQueueSet(), apiServiceQueue: workqueue.NewTypedRateLimitingQueueWithConfig[types.NamespacedName]( workqueue.DefaultTypedControllerRateLimiter[types.NamespacedName](), workqueue.TypedRateLimitingQueueConfig[types.NamespacedName]{ @@ -293,42 +304,93 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat return nil, err } - // A separate informer solely for CSV copies. Object metadata requests are used + // A separate informer solely for CSV copies. Fields + // are pruned from local copies of the objects managed // by this informer in order to reduce cached size. - gvr := v1alpha1.SchemeGroupVersion.WithResource("clusterserviceversions") - copiedCSVInformer := metadatainformer.NewFilteredMetadataInformer( - config.metadataClient, - gvr, - namespace, - config.resyncPeriod(), - cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, - func(options *metav1.ListOptions) { - options.LabelSelector = v1alpha1.CopiedLabelKey + copiedCSVInformer := cache.NewSharedIndexInformerWithOptions( + listerwatcher.NewListerWatcher( + op.client, + namespace, + func(opts *metav1.ListOptions) { + opts.LabelSelector = v1alpha1.CopiedLabelKey + }, + ), + &v1alpha1.ClusterServiceVersion{}, + cache.SharedIndexInformerOptions{ + ResyncPeriod: config.resyncPeriod(), + Indexers: cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, }, - ).Informer() - op.copiedCSVLister = metadatalister.New(copiedCSVInformer.GetIndexer(), gvr) + ) + + // Transform the copied CSV to be just the Metadata + // However, because we have the full copied CSV, we can calculate + // a hash over the full CSV to store as an annotation + copiedCSVTransformFunc := func(i interface{}) (interface{}, error) { + if csv, ok := i.(*v1alpha1.ClusterServiceVersion); ok { + specHash, statusHash, err := copyableCSVHash(csv) + if err != nil { + return nil, err + } + *csv = v1alpha1.ClusterServiceVersion{ + TypeMeta: csv.TypeMeta, + ObjectMeta: csv.ObjectMeta, + Spec: v1alpha1.ClusterServiceVersionSpec{}, + Status: v1alpha1.ClusterServiceVersionStatus{}, + } + // copy only the nececessary labels + labels := csv.Labels + csv.Labels = make(map[string]string, 2) + if l, ok := labels[v1alpha1.CopiedLabelKey]; ok { + csv.Labels[v1alpha1.CopiedLabelKey] = l + } + if l, ok := labels[install.OLMManagedLabelKey]; ok { + csv.Labels[install.OLMManagedLabelKey] = l + } + + // copy only the nececessary annotations + annotations := csv.Annotations + csv.Annotations = make(map[string]string, 4) + if a, ok := annotations[operatorsv1.OperatorGroupAnnotationKey]; ok { + csv.Annotations[operatorsv1.OperatorGroupAnnotationKey] = a + } + if a, ok := annotations[operatorsv1.OperatorGroupNamespaceAnnotationKey]; ok { + csv.Annotations[operatorsv1.OperatorGroupNamespaceAnnotationKey] = a + } + // fake CSV hashes for tracking purposes only + csv.Annotations[copyCSVSpecHash] = specHash + csv.Annotations[copyCSVStatusHash] = statusHash + return csv, nil + } + return nil, fmt.Errorf("unable to convert input to CSV") + } + + if err := copiedCSVInformer.SetTransform(copiedCSVTransformFunc); err != nil { + return nil, err + } + op.copiedCSVLister = operatorsv1alpha1listers.NewClusterServiceVersionLister(copiedCSVInformer.GetIndexer()) informersByNamespace[namespace].CopiedCSVInformer = copiedCSVInformer informersByNamespace[namespace].CopiedCSVLister = op.copiedCSVLister - // Register separate queue for gcing copied csvs - copiedCSVGCQueue := workqueue.NewTypedRateLimitingQueueWithConfig[types.NamespacedName]( + // Register separate queue for gcing/syncing/deletion of copied csvs + copiedCSVQueue := workqueue.NewTypedRateLimitingQueueWithConfig[types.NamespacedName]( workqueue.DefaultTypedControllerRateLimiter[types.NamespacedName](), workqueue.TypedRateLimitingQueueConfig[types.NamespacedName]{ - Name: fmt.Sprintf("%s/csv-gc", namespace), + Name: fmt.Sprintf("%s/csv-copied-sync", namespace), }) - op.copiedCSVGCQueueSet.Set(namespace, copiedCSVGCQueue) - copiedCSVGCQueueInformer, err := queueinformer.NewQueueInformer( + op.copiedCSVQueueSet.Set(namespace, copiedCSVQueue) + copiedCSVQueueInformer, err := queueinformer.NewQueueInformer( ctx, queueinformer.WithInformer(copiedCSVInformer), queueinformer.WithLogger(op.logger), - queueinformer.WithQueue(copiedCSVGCQueue), + queueinformer.WithQueue(copiedCSVQueue), queueinformer.WithIndexer(copiedCSVInformer.GetIndexer()), - queueinformer.WithSyncer(queueinformer.LegacySyncHandler(op.syncGcCsv).ToSyncer()), + queueinformer.WithSyncer(queueinformer.LegacySyncHandler(op.syncCopiedCsv).ToSyncer()), + queueinformer.WithDeletionHandler(op.handleCopiedCSVDeletion), ) if err != nil { return nil, err } - if err := op.RegisterQueueInformer(copiedCSVGCQueueInformer); err != nil { + if err := op.RegisterQueueInformer(copiedCSVQueueInformer); err != nil { return nil, err } @@ -1205,7 +1267,7 @@ func (a *Operator) handleClusterServiceVersionDeletion(obj interface{}) { for _, namespace := range namespaces { if namespace != operatorNamespace { logger.WithField("targetNamespace", namespace).Debug("requeueing child csv for deletion") - if err := a.copiedCSVGCQueueSet.Requeue(namespace, clusterServiceVersion.GetName()); err != nil { + if err := a.copiedCSVQueueSet.Requeue(namespace, clusterServiceVersion.GetName()); err != nil { logger.WithError(err).Warn("unable to requeue") } } @@ -1272,7 +1334,7 @@ func (a *Operator) handleClusterServiceVersionDeletion(obj interface{}) { } } -func (a *Operator) removeDanglingChildCSVs(csv *metav1.PartialObjectMetadata) error { +func (a *Operator) removeDanglingChildCSVs(csv *v1alpha1.ClusterServiceVersion) error { logger := a.logger.WithFields(logrus.Fields{ "id": queueinformer.NewLoopID(), "csv": csv.GetName(), @@ -1320,7 +1382,7 @@ func (a *Operator) removeDanglingChildCSVs(csv *metav1.PartialObjectMetadata) er return nil } -func (a *Operator) deleteChild(csv *metav1.PartialObjectMetadata, logger *logrus.Entry) error { +func (a *Operator) deleteChild(csv *v1alpha1.ClusterServiceVersion, logger *logrus.Entry) error { logger.Debug("gcing csv") return a.client.OperatorsV1alpha1().ClusterServiceVersions(csv.GetNamespace()).Delete(context.TODO(), csv.GetName(), metav1.DeleteOptions{}) } @@ -1704,9 +1766,13 @@ func (a *Operator) syncCopyCSV(obj interface{}) (syncError error) { return } - logger.WithFields(logrus.Fields{ - "targetNamespaces": strings.Join(operatorGroup.Status.Namespaces, ","), - }).Debug("copying csv to targets") + if len(operatorGroup.Status.Namespaces) == 1 && operatorGroup.Status.Namespaces[0] == "" { + logger.Debug("copying csv to targets in all namespaces") + } else { + logger.WithFields(logrus.Fields{ + "targetNamespaces": strings.Join(operatorGroup.Status.Namespaces, ","), + }).Debug("copying csv to targets") + } copiedCSVsAreEnabled, err := a.copiedCSVsAreEnabled() if err != nil { @@ -1864,17 +1930,52 @@ func (a *Operator) createCSVCopyingDisabledEvent(csv *v1alpha1.ClusterServiceVer return nil } -func (a *Operator) syncGcCsv(obj interface{}) (syncError error) { - clusterServiceVersion, ok := obj.(*metav1.PartialObjectMetadata) +func (a *Operator) requeueParentCsv(csv *v1alpha1.ClusterServiceVersion) error { + name := csv.GetName() + copiedNamespace := csv.GetNamespace() + a.logger.WithField("csv", fmt.Sprintf("%s/%s", copiedNamespace, name)).Debug("syncing copied CSV") + + // Requeue parent CSV to deal with any changes to the copied CSV + copiedFromNamespace, ok := csv.GetLabels()[v1alpha1.CopiedLabelKey] + if !ok { + a.logger.Infof("no %q label found in CSV, skipping requeue", v1alpha1.CopiedLabelKey) + return nil + } + return a.csvCopyQueueSet.Requeue(copiedFromNamespace, name) +} + +func (a *Operator) handleCopiedCSVDeletion(obj interface{}) { + csv, ok := obj.(*v1alpha1.ClusterServiceVersion) + if !ok { + a.logger.Debugf("casting ClusterServiceVersion failed: wrong type: %#v", obj) + return + } + if !v1alpha1.IsCopied(csv) { + return + } + + // Trigger parent reconciliation + a.requeueParentCsv(csv) +} + +func (a *Operator) syncCopiedCsv(obj interface{}) error { + csv, ok := obj.(*v1alpha1.ClusterServiceVersion) if !ok { a.logger.Debugf("wrong type: %#v", obj) return fmt.Errorf("casting ClusterServiceVersion failed") } - if v1alpha1.IsCopied(clusterServiceVersion) { - syncError = a.removeDanglingChildCSVs(clusterServiceVersion) - return + if !v1alpha1.IsCopied(csv) { + return nil } - return + + // check for any garbage collection + err := a.removeDanglingChildCSVs(csv) + if err != nil { + return err + } + + // Trigger parent reconciliation + return a.requeueParentCsv(csv) } // operatorGroupFromAnnotations returns the OperatorGroup for the CSV only if the CSV is active one in the group diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/operatorgroup.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/operatorgroup.go index 94e17b6ae4..739541ce9f 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/operatorgroup.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/operatorgroup.go @@ -361,7 +361,7 @@ func (a *Operator) pruneProvidedAPIs(group *operatorsv1.OperatorGroup, groupProv } // Prune providedAPIs annotation if the cluster has fewer providedAPIs (handles CSV deletion) - //if intersection := groupProvidedAPIs.Intersection(providedAPIsFromCSVs); len(intersection) < len(groupProvidedAPIs) { + // if intersection := groupProvidedAPIs.Intersection(providedAPIsFromCSVs); len(intersection) < len(groupProvidedAPIs) { if len(intersection) < len(groupProvidedAPIs) { difference := groupProvidedAPIs.Difference(intersection) logger := logger.WithFields(logrus.Fields{ @@ -704,7 +704,7 @@ func (a *Operator) ensureCSVsInNamespaces(csv *v1alpha1.ClusterServiceVersion, o var copyPrototype v1alpha1.ClusterServiceVersion csvCopyPrototype(csv, ©Prototype) - nonstatus, status, err := copyableCSVHash(©Prototype) + specHash, statusHash, err := copyableCSVHash(©Prototype) if err != nil { return err } @@ -715,7 +715,7 @@ func (a *Operator) ensureCSVsInNamespaces(csv *v1alpha1.ClusterServiceVersion, o } if targets.Contains(ns.GetName()) { var targetCSV *v1alpha1.ClusterServiceVersion - if targetCSV, err = a.copyToNamespace(©Prototype, csv.GetNamespace(), ns.GetName(), nonstatus, status); err != nil { + if targetCSV, err = a.copyToNamespace(©Prototype, csv.GetNamespace(), ns.GetName(), specHash, statusHash); err != nil { logger.WithError(err).Debug("error copying to target") continue } @@ -779,21 +779,21 @@ func copyableCSVHash(original *v1alpha1.ClusterServiceVersion) (string, string, Spec: original.Spec, } - newHash, err := hashutil.DeepHashObject(&shallow) + specHash, err := hashutil.DeepHashObject(&shallow) if err != nil { return "", "", err } - originalHash, err := hashutil.DeepHashObject(&original.Status) + statusHash, err := hashutil.DeepHashObject(&original.Status) if err != nil { return "", "", err } - return newHash, originalHash, nil + return specHash, statusHash, nil } // If returned error is not nil, the returned ClusterServiceVersion // has only the Name, Namespace, and UID fields set. -func (a *Operator) copyToNamespace(prototype *v1alpha1.ClusterServiceVersion, nsFrom, nsTo, nonstatus, status string) (*v1alpha1.ClusterServiceVersion, error) { +func (a *Operator) copyToNamespace(prototype *v1alpha1.ClusterServiceVersion, nsFrom, nsTo, specHash, statusHash string) (*v1alpha1.ClusterServiceVersion, error) { if nsFrom == nsTo { return nil, fmt.Errorf("bug: can not copy to active namespace %v", nsFrom) } @@ -802,7 +802,7 @@ func (a *Operator) copyToNamespace(prototype *v1alpha1.ClusterServiceVersion, ns prototype.ResourceVersion = "" prototype.UID = "" - existing, err := a.copiedCSVLister.Namespace(nsTo).Get(prototype.GetName()) + existing, err := a.copiedCSVLister.ClusterServiceVersions(nsTo).Get(prototype.GetName()) if apierrors.IsNotFound(err) { created, err := a.client.OperatorsV1alpha1().ClusterServiceVersions(nsTo).Create(context.TODO(), prototype, metav1.CreateOptions{}) if err != nil { @@ -826,11 +826,12 @@ func (a *Operator) copyToNamespace(prototype *v1alpha1.ClusterServiceVersion, ns prototype.Namespace = existing.Namespace prototype.ResourceVersion = existing.ResourceVersion prototype.UID = existing.UID - existingNonStatus := existing.Annotations["$copyhash-nonstatus"] - existingStatus := existing.Annotations["$copyhash-status"] + // Get the non-status and status hash of the existing copied CSV + existingSpecHash := existing.Annotations[copyCSVSpecHash] + existingStatusHash := existing.Annotations[copyCSVStatusHash] var updated *v1alpha1.ClusterServiceVersion - if existingNonStatus != nonstatus { + if existingSpecHash != specHash { if updated, err = a.client.OperatorsV1alpha1().ClusterServiceVersions(nsTo).Update(context.TODO(), prototype, metav1.UpdateOptions{}); err != nil { return nil, fmt.Errorf("failed to update: %w", err) } @@ -839,7 +840,7 @@ func (a *Operator) copyToNamespace(prototype *v1alpha1.ClusterServiceVersion, ns updated = prototype } - if existingStatus != status { + if existingStatusHash != statusHash { updated.Status = prototype.Status if _, err = a.client.OperatorsV1alpha1().ClusterServiceVersions(nsTo).UpdateStatus(context.TODO(), updated, metav1.UpdateOptions{}); err != nil { return nil, fmt.Errorf("failed to update status: %w", err) @@ -855,7 +856,7 @@ func (a *Operator) copyToNamespace(prototype *v1alpha1.ClusterServiceVersion, ns } func (a *Operator) pruneFromNamespace(operatorGroupName, namespace string) error { - fetchedCSVs, err := a.copiedCSVLister.Namespace(namespace).List(labels.Everything()) + fetchedCSVs, err := a.copiedCSVLister.ClusterServiceVersions(namespace).List(labels.Everything()) if err != nil { return err } @@ -863,7 +864,7 @@ func (a *Operator) pruneFromNamespace(operatorGroupName, namespace string) error for _, csv := range fetchedCSVs { if v1alpha1.IsCopied(csv) && csv.GetAnnotations()[operatorsv1.OperatorGroupAnnotationKey] == operatorGroupName { a.logger.Debugf("Found CSV '%v' in namespace %v to delete", csv.GetName(), namespace) - if err := a.copiedCSVGCQueueSet.Requeue(csv.GetNamespace(), csv.GetName()); err != nil { + if err := a.copiedCSVQueueSet.Requeue(csv.GetNamespace(), csv.GetName()); err != nil { return err } } diff --git a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/plugins/operator_plugin.go b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/plugins/operator_plugin.go index 0f38f0543b..8cce23c2a4 100644 --- a/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/plugins/operator_plugin.go +++ b/vendor/github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/plugins/operator_plugin.go @@ -8,6 +8,7 @@ import ( operatorsv1informers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/informers/externalversions/operators/v1" operatorsv1alpha1informers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/informers/externalversions/operators/v1alpha1" operatorsv2informers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/informers/externalversions/operators/v2" + operatorsv1alpha1listers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1alpha1" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/operatorclient" "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer" "github.com/sirupsen/logrus" @@ -33,7 +34,7 @@ type HostOperator interface { type Informers struct { CSVInformer operatorsv1alpha1informers.ClusterServiceVersionInformer CopiedCSVInformer cache.SharedIndexInformer - CopiedCSVLister metadatalister.Lister + CopiedCSVLister operatorsv1alpha1listers.ClusterServiceVersionLister OperatorGroupInformer operatorsv1informers.OperatorGroupInformer OperatorConditionInformer operatorsv2informers.OperatorConditionInformer SubscriptionInformer operatorsv1alpha1informers.SubscriptionInformer diff --git a/vendor/modules.txt b/vendor/modules.txt index 8c48924af4..647d6af60c 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -668,7 +668,7 @@ github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operator github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/catalogtemplate github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/decorators github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/alongside -github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/pruning +github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/listerwatcher github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/labeller github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/overrides