diff --git a/pkg/cache/cache.go b/pkg/cache/cache.go index e23045bf40..f8e7405174 100644 --- a/pkg/cache/cache.go +++ b/pkg/cache/cache.go @@ -201,6 +201,9 @@ type Options struct { // DefaultTransform will be used as transform for all object types // unless there is already one set in ByObject or DefaultNamespaces. + // + // A typical usecase for this is to use TransformStripManagedFields + // to reduce the caches memory usage. DefaultTransform toolscache.TransformFunc // DefaultWatchErrorHandler will be used to the WatchErrorHandler which is called @@ -344,6 +347,20 @@ func New(cfg *rest.Config, opts Options) (Cache, error) { return delegating, nil } +// TransformStripManagedFields strips the managed fields of an object before it is committed to the cache. +// If you are not explicitly accessing managedFields from your code, setting this as `DefaultTransform` +// on the cache can lead to a significant reduction in memory usage. +func TransformStripManagedFields() toolscache.TransformFunc { + return func(in any) (any, error) { + // Nilcheck managed fields to avoid hitting https://github.com/kubernetes/kubernetes/issues/124337 + if obj, err := meta.Accessor(in); err == nil && obj.GetManagedFields() != nil { + obj.SetManagedFields(nil) + } + + return in, nil + } +} + func optionDefaultsToConfig(opts *Options) Config { return Config{ LabelSelector: opts.DefaultLabelSelector, diff --git a/pkg/cache/cache_test.go b/pkg/cache/cache_test.go index 2d88b43ef3..d6c1c4aae4 100644 --- a/pkg/cache/cache_test.go +++ b/pkg/cache/cache_test.go @@ -2421,6 +2421,25 @@ func CacheTest(createCacheFunc func(config *rest.Config, opts cache.Options) (ca }) } +var _ = Describe("TransformStripManagedFields", func() { + It("should strip managed fields from an object", func() { + obj := &corev1.Pod{ObjectMeta: metav1.ObjectMeta{ + ManagedFields: []metav1.ManagedFieldsEntry{{ + Manager: "foo", + }}, + }} + transformed, err := cache.TransformStripManagedFields()(obj) + Expect(err).NotTo(HaveOccurred()) + Expect(transformed).To(Equal(&corev1.Pod{ObjectMeta: metav1.ObjectMeta{}})) + }) + + It("should not trip over an unexpected object", func() { + transformed, err := cache.TransformStripManagedFields()("foo") + Expect(err).NotTo(HaveOccurred()) + Expect(transformed).To(Equal("foo")) + }) +}) + // ensureNamespace installs namespace of a given name if not exists. func ensureNamespace(namespace string, client client.Client) error { ns := corev1.Namespace{