Skip to content

Commit ef9f9a3

Browse files
operators/olm: use a partial object metadata watch for copied CSVs
All we ever ned to know about copied CSVs is their metadata. No need to prune objects in memory, it's better to never allocate the memory to deserilize them in the first place. Signed-off-by: Steve Kuznetsov <[email protected]>
1 parent 58adbea commit ef9f9a3

File tree

16 files changed

+1283
-258
lines changed

16 files changed

+1283
-258
lines changed

cmd/olm/main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/sirupsen/logrus"
1515
"github.com/spf13/pflag"
1616
corev1 "k8s.io/api/core/v1"
17+
"k8s.io/client-go/metadata"
1718
"k8s.io/klog"
1819
ctrl "sigs.k8s.io/controller-runtime"
1920

@@ -154,6 +155,10 @@ func main() {
154155
if err != nil {
155156
logger.WithError(err).Fatal("error configuring custom resource client")
156157
}
158+
metadataClient, err := metadata.NewForConfig(config)
159+
if err != nil {
160+
logger.WithError(err).Fatal("error configuring metadata client")
161+
}
157162

158163
// Create a new instance of the operator.
159164
op, err := olm.NewOperator(
@@ -162,6 +167,7 @@ func main() {
162167
olm.WithWatchedNamespaces(namespaces...),
163168
olm.WithResyncPeriod(queueinformer.ResyncWithJitter(*wakeupInterval, 0.2)),
164169
olm.WithExternalClient(crClient),
170+
olm.WithMetadataClient(metadataClient),
165171
olm.WithOperatorClient(opClient),
166172
olm.WithRestConfig(config),
167173
olm.WithConfigClient(versionedConfigClient),

deploy/chart/crds/0000_50_olm_00-clusterserviceversions.crd.yaml

Lines changed: 88 additions & 37 deletions
Large diffs are not rendered by default.

deploy/chart/crds/0000_50_olm_00-subscriptions.crd.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -644,7 +644,7 @@ spec:
644644
type: object
645645
properties:
646646
claims:
647-
description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable."
647+
description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers."
648648
type: array
649649
items:
650650
description: ResourceClaim references one entry in PodSpec.ResourceClaims.
@@ -668,7 +668,7 @@ spec:
668668
- type: string
669669
x-kubernetes-int-or-string: true
670670
requests:
671-
description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
671+
description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
672672
type: object
673673
additionalProperties:
674674
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
@@ -1002,7 +1002,7 @@ spec:
10021002
description: 'medium represents what type of storage medium should back this directory. The default is "" which means to use the node''s default medium. Must be an empty string (default) or Memory. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir'
10031003
type: string
10041004
sizeLimit:
1005-
description: 'sizeLimit is the total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium. The maximum usage on memory medium EmptyDir would be the minimum value between the SizeLimit specified here and the sum of memory limits of all containers in a pod. The default is nil which means that the limit is undefined. More info: http://kubernetes.io/docs/user-guide/volumes#emptydir'
1005+
description: 'sizeLimit is the total amount of local storage required for this EmptyDir volume. The size limit is also applicable for memory medium. The maximum usage on memory medium EmptyDir would be the minimum value between the SizeLimit specified here and the sum of memory limits of all containers in a pod. The default is nil which means that the limit is undefined. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir'
10061006
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$
10071007
anyOf:
10081008
- type: integer
@@ -1070,7 +1070,7 @@ spec:
10701070
type: object
10711071
properties:
10721072
claims:
1073-
description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable."
1073+
description: "Claims lists the names of resources, defined in spec.resourceClaims, that are used by this container. \n This is an alpha field and requires enabling the DynamicResourceAllocation feature gate. \n This field is immutable. It can only be set for containers."
10741074
type: array
10751075
items:
10761076
description: ResourceClaim references one entry in PodSpec.ResourceClaims.
@@ -1094,7 +1094,7 @@ spec:
10941094
- type: string
10951095
x-kubernetes-int-or-string: true
10961096
requests:
1097-
description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
1097+
description: 'Requests describes the minimum amount of compute resources required. If Requests is omitted for a container, it defaults to Limits if that is explicitly specified, otherwise to an implementation-defined value. Requests cannot exceed Limits. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/'
10981098
type: object
10991099
additionalProperties:
11001100
pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$

pkg/controller/operators/catalog/operator.go

Lines changed: 25 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -204,29 +204,31 @@ func NewOperator(ctx context.Context, kubeconfigPath string, clock utilclock.Clo
204204
// Fields are pruned from local copies of the objects managed
205205
// by this informer in order to reduce cached size.
206206
prunedCSVInformer := cache.NewSharedIndexInformer(
207-
pruning.NewListerWatcher(op.client, metav1.NamespaceAll, func(options *metav1.ListOptions) {
208-
options.LabelSelector = fmt.Sprintf("!%s", v1alpha1.CopiedLabelKey)
209-
}, pruning.PrunerFunc(func(csv *v1alpha1.ClusterServiceVersion) {
210-
*csv = v1alpha1.ClusterServiceVersion{
211-
TypeMeta: csv.TypeMeta,
212-
ObjectMeta: metav1.ObjectMeta{
213-
Name: csv.Name,
214-
Namespace: csv.Namespace,
215-
Labels: csv.Labels,
216-
Annotations: csv.Annotations,
217-
},
218-
Spec: v1alpha1.ClusterServiceVersionSpec{
219-
CustomResourceDefinitions: csv.Spec.CustomResourceDefinitions,
220-
APIServiceDefinitions: csv.Spec.APIServiceDefinitions,
221-
Replaces: csv.Spec.Replaces,
222-
Version: csv.Spec.Version,
223-
},
224-
Status: v1alpha1.ClusterServiceVersionStatus{
225-
Phase: csv.Status.Phase,
226-
Reason: csv.Status.Reason,
227-
},
228-
}
229-
})),
207+
pruning.NewListerWatcher(op.client, metav1.NamespaceAll,
208+
func(options *metav1.ListOptions) {
209+
options.LabelSelector = fmt.Sprintf("!%s", v1alpha1.CopiedLabelKey)
210+
},
211+
pruning.PrunerFunc(func(csv *v1alpha1.ClusterServiceVersion) {
212+
*csv = v1alpha1.ClusterServiceVersion{
213+
TypeMeta: csv.TypeMeta,
214+
ObjectMeta: metav1.ObjectMeta{
215+
Name: csv.Name,
216+
Namespace: csv.Namespace,
217+
Labels: csv.Labels,
218+
Annotations: csv.Annotations,
219+
},
220+
Spec: v1alpha1.ClusterServiceVersionSpec{
221+
CustomResourceDefinitions: csv.Spec.CustomResourceDefinitions,
222+
APIServiceDefinitions: csv.Spec.APIServiceDefinitions,
223+
Replaces: csv.Spec.Replaces,
224+
Version: csv.Spec.Version,
225+
},
226+
Status: v1alpha1.ClusterServiceVersionStatus{
227+
Phase: csv.Status.Phase,
228+
Reason: csv.Status.Reason,
229+
},
230+
}
231+
})),
230232
&v1alpha1.ClusterServiceVersion{},
231233
resyncPeriod(),
232234
cache.Indexers{

pkg/controller/operators/olm/config.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"time"
66

77
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/queueinformer"
8+
"k8s.io/client-go/metadata"
89

910
"github.com/pkg/errors"
1011
"github.com/sirupsen/logrus"
@@ -29,6 +30,7 @@ type operatorConfig struct {
2930
clock utilclock.Clock
3031
logger *logrus.Logger
3132
operatorClient operatorclient.ClientInterface
33+
metadataClient metadata.Interface
3234
externalClient versioned.Interface
3335
strategyResolver install.StrategyResolverInterface
3436
apiReconciler APIIntersectionReconciler
@@ -159,6 +161,12 @@ func WithOperatorClient(operatorClient operatorclient.ClientInterface) OperatorO
159161
}
160162
}
161163

164+
func WithMetadataClient(metadataClient metadata.Interface) OperatorOption {
165+
return func(config *operatorConfig) {
166+
config.metadataClient = metadataClient
167+
}
168+
}
169+
162170
func WithExternalClient(externalClient versioned.Interface) OperatorOption {
163171
return func(config *operatorConfig) {
164172
config.externalClient = externalClient

pkg/controller/operators/olm/operator.go

Lines changed: 23 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import (
2424
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
2525
"k8s.io/client-go/informers"
2626
k8sscheme "k8s.io/client-go/kubernetes/scheme"
27+
"k8s.io/client-go/metadata/metadatainformer"
28+
"k8s.io/client-go/metadata/metadatalister"
2729
"k8s.io/client-go/tools/cache"
2830
"k8s.io/client-go/tools/record"
2931
"k8s.io/client-go/util/workqueue"
@@ -35,12 +37,10 @@ import (
3537
"github.com/operator-framework/api/pkg/operators/v1alpha1"
3638
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/clientset/versioned"
3739
"github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/informers/externalversions"
38-
operatorsv1alpha1listers "github.com/operator-framework/operator-lifecycle-manager/pkg/api/client/listers/operators/v1alpha1"
3940
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/certs"
4041
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/install"
41-
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/internal/pruning"
4242
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/operators/olm/overrides"
43-
resolver "github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver"
43+
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry/resolver"
4444
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/clients"
4545
csvutility "github.com/operator-framework/operator-lifecycle-manager/pkg/lib/csv"
4646
"github.com/operator-framework/operator-lifecycle-manager/pkg/lib/event"
@@ -75,7 +75,7 @@ type Operator struct {
7575
client versioned.Interface
7676
lister operatorlister.OperatorLister
7777
protectedCopiedCSVNamespaces map[string]struct{}
78-
copiedCSVLister operatorsv1alpha1listers.ClusterServiceVersionLister
78+
copiedCSVLister metadatalister.Lister
7979
ogQueueSet *queueinformer.ResourceQueueSet
8080
csvQueueSet *queueinformer.ResourceQueueSet
8181
olmConfigQueue workqueue.RateLimitingInterface
@@ -127,6 +127,9 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat
127127
if err := k8sscheme.AddToScheme(scheme); err != nil {
128128
return nil, err
129129
}
130+
if err := metav1.AddMetaToScheme(scheme); err != nil {
131+
return nil, err
132+
}
130133

131134
op := &Operator{
132135
Operator: queueOperator,
@@ -208,44 +211,20 @@ func newOperatorWithConfig(ctx context.Context, config *operatorConfig) (*Operat
208211
return nil, err
209212
}
210213

211-
// A separate informer solely for CSV copies. Fields
212-
// are pruned from local copies of the objects managed
214+
// A separate informer solely for CSV copies. Object metadata requests are used
213215
// by this informer in order to reduce cached size.
214-
copiedCSVInformer := cache.NewSharedIndexInformer(
215-
pruning.NewListerWatcher(
216-
op.client,
217-
namespace,
218-
func(opts *metav1.ListOptions) {
219-
opts.LabelSelector = v1alpha1.CopiedLabelKey
220-
},
221-
pruning.PrunerFunc(func(csv *v1alpha1.ClusterServiceVersion) {
222-
nonstatus, status := copyableCSVHash(csv)
223-
*csv = v1alpha1.ClusterServiceVersion{
224-
TypeMeta: csv.TypeMeta,
225-
ObjectMeta: csv.ObjectMeta,
226-
Status: v1alpha1.ClusterServiceVersionStatus{
227-
Phase: csv.Status.Phase,
228-
Reason: csv.Status.Reason,
229-
},
230-
}
231-
if csv.Annotations == nil {
232-
csv.Annotations = make(map[string]string, 2)
233-
}
234-
// These annotation keys are
235-
// intentionally invalid -- all writes
236-
// to copied CSVs are regenerated from
237-
// the corresponding non-copied CSV,
238-
// so it should never be transmitted
239-
// back to the API server.
240-
csv.Annotations["$copyhash-nonstatus"] = nonstatus
241-
csv.Annotations["$copyhash-status"] = status
242-
}),
243-
),
244-
&v1alpha1.ClusterServiceVersion{},
216+
gvr := v1alpha1.SchemeGroupVersion.WithResource("clusterserviceversions")
217+
copiedCSVInformer := metadatainformer.NewFilteredMetadataInformer(
218+
config.metadataClient,
219+
gvr,
220+
namespace,
245221
config.resyncPeriod(),
246222
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
247-
)
248-
op.copiedCSVLister = operatorsv1alpha1listers.NewClusterServiceVersionLister(copiedCSVInformer.GetIndexer())
223+
func(options *metav1.ListOptions) {
224+
options.LabelSelector = v1alpha1.CopiedLabelKey
225+
},
226+
).Informer()
227+
op.copiedCSVLister = metadatalister.New(copiedCSVInformer.GetIndexer(), gvr)
249228

250229
// Register separate queue for gcing copied csvs
251230
copiedCSVGCQueue := workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), fmt.Sprintf("%s/csv-gc", namespace))
@@ -1195,17 +1174,16 @@ func (a *Operator) handleClusterServiceVersionDeletion(obj interface{}) {
11951174
}
11961175
}
11971176

1198-
func (a *Operator) removeDanglingChildCSVs(csv *v1alpha1.ClusterServiceVersion) error {
1177+
func (a *Operator) removeDanglingChildCSVs(csv *metav1.PartialObjectMetadata) error {
11991178
logger := a.logger.WithFields(logrus.Fields{
12001179
"id": queueinformer.NewLoopID(),
12011180
"csv": csv.GetName(),
12021181
"namespace": csv.GetNamespace(),
1203-
"phase": csv.Status.Phase,
12041182
"labels": csv.GetLabels(),
12051183
"annotations": csv.GetAnnotations(),
12061184
})
12071185

1208-
if !csv.IsCopied() {
1186+
if !v1alpha1.IsCopied(csv) {
12091187
logger.Warning("removeDanglingChild called on a parent. this is a no-op but should be avoided.")
12101188
return nil
12111189
}
@@ -1244,7 +1222,7 @@ func (a *Operator) removeDanglingChildCSVs(csv *v1alpha1.ClusterServiceVersion)
12441222
return nil
12451223
}
12461224

1247-
func (a *Operator) deleteChild(csv *v1alpha1.ClusterServiceVersion, logger *logrus.Entry) error {
1225+
func (a *Operator) deleteChild(csv *metav1.PartialObjectMetadata, logger *logrus.Entry) error {
12481226
logger.Debug("gcing csv")
12491227
return a.client.OperatorsV1alpha1().ClusterServiceVersions(csv.GetNamespace()).Delete(context.TODO(), csv.GetName(), metav1.DeleteOptions{})
12501228
}
@@ -1683,12 +1661,12 @@ func (a *Operator) createCSVCopyingDisabledEvent(csv *v1alpha1.ClusterServiceVer
16831661
}
16841662

16851663
func (a *Operator) syncGcCsv(obj interface{}) (syncError error) {
1686-
clusterServiceVersion, ok := obj.(*v1alpha1.ClusterServiceVersion)
1664+
clusterServiceVersion, ok := obj.(*metav1.PartialObjectMetadata)
16871665
if !ok {
16881666
a.logger.Debugf("wrong type: %#v", obj)
16891667
return fmt.Errorf("casting ClusterServiceVersion failed")
16901668
}
1691-
if clusterServiceVersion.IsCopied() {
1669+
if v1alpha1.IsCopied(clusterServiceVersion) {
16921670
syncError = a.removeDanglingChildCSVs(clusterServiceVersion)
16931671
return
16941672
}

0 commit comments

Comments
 (0)