diff --git a/pkg/provider/kubernetes/crd/client.go b/pkg/provider/kubernetes/crd/client.go index 19896b7273..296201502d 100644 --- a/pkg/provider/kubernetes/crd/client.go +++ b/pkg/provider/kubernetes/crd/client.go @@ -177,7 +177,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< } for _, ns := range namespaces { - factoryCrd := traefikinformers.NewSharedInformerFactoryWithOptions(c.csCrd, resyncPeriod, traefikinformers.WithNamespace(ns), traefikinformers.WithTweakListOptions(matchesLabelSelector)) + factoryCrd := traefikinformers.NewSharedInformerFactoryWithOptions(c.csCrd, resyncPeriod, traefikinformers.WithNamespace(ns), traefikinformers.WithTweakListOptions(matchesLabelSelector), traefikinformers.WithTransform(k8s.StripManagedFields)) _, err := factoryCrd.Traefik().V1alpha1().IngressRoutes().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err @@ -219,7 +219,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< return nil, err } - factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns)) + factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTransform(k8s.StripManagedFields)) _, err = factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err @@ -233,7 +233,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< return nil, err } - factorySecret := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm)) + factorySecret := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm), kinformers.WithTransform(k8s.StripManagedFields)) _, err = factorySecret.Core().V1().Secrets().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err @@ -271,7 +271,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< } if !c.disableClusterScopeInformer { - c.clusterScopeFactory = kinformers.NewSharedInformerFactory(c.csKube, resyncPeriod) + c.clusterScopeFactory = kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithTransform(k8s.StripManagedFields)) _, err := c.clusterScopeFactory.Core().V1().Nodes().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err diff --git a/pkg/provider/kubernetes/gateway/client.go b/pkg/provider/kubernetes/gateway/client.go index 34d15d5724..f55df849b2 100644 --- a/pkg/provider/kubernetes/gateway/client.go +++ b/pkg/provider/kubernetes/gateway/client.go @@ -148,20 +148,20 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< options.LabelSelector = c.labelSelector } - c.factoryNamespace = kinformers.NewSharedInformerFactory(c.csKube, resyncPeriod) + c.factoryNamespace = kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithTransform(k8s.StripManagedFields)) _, err := c.factoryNamespace.Core().V1().Namespaces().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err } - c.factoryGatewayClass = gateinformers.NewSharedInformerFactoryWithOptions(c.csGateway, resyncPeriod, gateinformers.WithTweakListOptions(labelSelectorOptions)) + c.factoryGatewayClass = gateinformers.NewSharedInformerFactoryWithOptions(c.csGateway, resyncPeriod, gateinformers.WithTweakListOptions(labelSelectorOptions), gateinformers.WithTransform(k8s.StripManagedFields)) _, err = c.factoryGatewayClass.Gateway().V1().GatewayClasses().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err } for _, ns := range namespaces { - factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns)) + factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTransform(k8s.StripManagedFields)) _, err = factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err @@ -171,7 +171,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< return nil, err } - factoryGateway := gateinformers.NewSharedInformerFactoryWithOptions(c.csGateway, resyncPeriod, gateinformers.WithNamespace(ns)) + factoryGateway := gateinformers.NewSharedInformerFactoryWithOptions(c.csGateway, resyncPeriod, gateinformers.WithNamespace(ns), gateinformers.WithTransform(k8s.StripManagedFields)) _, err = factoryGateway.Gateway().V1().Gateways().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err @@ -209,7 +209,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< } } - factorySecret := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm)) + factorySecret := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm), kinformers.WithTransform(k8s.StripManagedFields)) _, err = factorySecret.Core().V1().Secrets().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err diff --git a/pkg/provider/kubernetes/ingress-nginx/client.go b/pkg/provider/kubernetes/ingress-nginx/client.go index dd04754f26..9d723e38e0 100644 --- a/pkg/provider/kubernetes/ingress-nginx/client.go +++ b/pkg/provider/kubernetes/ingress-nginx/client.go @@ -158,7 +158,7 @@ func (c *clientWrapper) WatchAll(ctx context.Context, namespace, namespaceSelect } for _, ns := range c.watchedNamespaces { - factoryIngress := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns)) + factoryIngress := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTransform(k8s.StripManagedFields)) _, err := factoryIngress.Networking().V1().Ingresses().Informer().AddEventHandler(eventHandler) if err != nil { @@ -167,7 +167,7 @@ func (c *clientWrapper) WatchAll(ctx context.Context, namespace, namespaceSelect c.factoriesIngress[ns] = factoryIngress - factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns)) + factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTransform(k8s.StripManagedFields)) _, err = factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err @@ -178,14 +178,14 @@ func (c *clientWrapper) WatchAll(ctx context.Context, namespace, namespaceSelect } c.factoriesKube[ns] = factoryKube - factorySecret := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm)) + factorySecret := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm), kinformers.WithTransform(k8s.StripManagedFields)) _, err = factorySecret.Core().V1().Secrets().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err } c.factoriesSecret[ns] = factorySecret - factoryConfigMap := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm)) + factoryConfigMap := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm), kinformers.WithTransform(k8s.StripManagedFields)) _, err = factoryConfigMap.Core().V1().ConfigMaps().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err @@ -226,7 +226,7 @@ func (c *clientWrapper) WatchAll(ctx context.Context, namespace, namespaceSelect } } - c.clusterScopeFactory = kinformers.NewSharedInformerFactory(c.clientset, resyncPeriod) + c.clusterScopeFactory = kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithTransform(k8s.StripManagedFields)) if !c.ignoreIngressClasses { _, err = c.clusterScopeFactory.Networking().V1().IngressClasses().Informer().AddEventHandler(eventHandler) diff --git a/pkg/provider/kubernetes/ingress/client.go b/pkg/provider/kubernetes/ingress/client.go index 9645409acf..49efbe9aa1 100644 --- a/pkg/provider/kubernetes/ingress/client.go +++ b/pkg/provider/kubernetes/ingress/client.go @@ -158,7 +158,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< } for _, ns := range namespaces { - factoryIngress := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(matchesLabelSelector)) + factoryIngress := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(matchesLabelSelector), kinformers.WithTransform(k8s.StripManagedFields)) _, err := factoryIngress.Networking().V1().Ingresses().Informer().AddEventHandler(eventHandler) if err != nil { @@ -167,7 +167,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< c.factoriesIngress[ns] = factoryIngress - factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns)) + factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTransform(k8s.StripManagedFields)) _, err = factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err @@ -178,7 +178,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< } c.factoriesKube[ns] = factoryKube - factorySecret := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm)) + factorySecret := kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTweakListOptions(notOwnedByHelm), kinformers.WithTransform(k8s.StripManagedFields)) _, err = factorySecret.Core().V1().Secrets().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err @@ -213,7 +213,7 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< } if !c.disableIngressClassInformer || !c.disableClusterScopeInformer { - c.clusterScopeFactory = kinformers.NewSharedInformerFactory(c.clientset, resyncPeriod) + c.clusterScopeFactory = kinformers.NewSharedInformerFactoryWithOptions(c.clientset, resyncPeriod, kinformers.WithTransform(k8s.StripManagedFields)) _, err := c.clusterScopeFactory.Networking().V1().IngressClasses().Informer().AddEventHandler(eventHandler) if err != nil { diff --git a/pkg/provider/kubernetes/k8s/transform.go b/pkg/provider/kubernetes/k8s/transform.go new file mode 100644 index 0000000000..4092203c73 --- /dev/null +++ b/pkg/provider/kubernetes/k8s/transform.go @@ -0,0 +1,16 @@ +package k8s + +import metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + +// StripManagedFields drops metadata.managedFields as objects enter the informer cache. +// Traefik never reads them, and they inflate the cache footprint and the cost of copying +// and comparing cached objects, which matters under heavy resource churn. +func StripManagedFields(obj any) (any, error) { + object, ok := obj.(metav1.Object) + if !ok { + return obj, nil + } + + object.SetManagedFields(nil) + return obj, nil +} diff --git a/pkg/provider/kubernetes/k8s/transform_test.go b/pkg/provider/kubernetes/k8s/transform_test.go new file mode 100644 index 0000000000..9f90343e82 --- /dev/null +++ b/pkg/provider/kubernetes/k8s/transform_test.go @@ -0,0 +1,43 @@ +package k8s + +import ( + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestStripManagedFields(t *testing.T) { + service := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: "whoami", + ManagedFields: []metav1.ManagedFieldsEntry{ + { + Manager: "kubectl", + Operation: metav1.ManagedFieldsOperationApply, + APIVersion: "v1", + Time: &metav1.Time{Time: time.Now()}, + }, + }, + }, + } + + got, err := StripManagedFields(service) + require.NoError(t, err) + + assert.Same(t, service, got) + assert.Nil(t, service.ManagedFields) + assert.Equal(t, "whoami", service.Name) +} + +func TestStripManagedFieldsIgnoresNonKubernetesObjects(t *testing.T) { + obj := struct{ Name string }{Name: "whoami"} + + got, err := StripManagedFields(obj) + require.NoError(t, err) + + assert.Equal(t, obj, got) +} diff --git a/pkg/provider/kubernetes/knative/client.go b/pkg/provider/kubernetes/knative/client.go index a56b646ad4..2d60dc32a2 100644 --- a/pkg/provider/kubernetes/knative/client.go +++ b/pkg/provider/kubernetes/knative/client.go @@ -122,13 +122,13 @@ func (c *clientWrapper) WatchAll(namespaces []string, stopCh <-chan struct{}) (< for _, ns := range namespaces { factory := knativenetworkinginformers.NewSharedInformerFactoryWithOptions(c.csKnativeNetworking, resyncPeriod, knativenetworkinginformers.WithNamespace(ns), knativenetworkinginformers.WithTweakListOptions(func(opts *metav1.ListOptions) { opts.LabelSelector = c.labelSelector - })) + }), knativenetworkinginformers.WithTransform(k8s.StripManagedFields)) _, err := factory.Networking().V1alpha1().Ingresses().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err } - factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns)) + factoryKube := kinformers.NewSharedInformerFactoryWithOptions(c.csKube, resyncPeriod, kinformers.WithNamespace(ns), kinformers.WithTransform(k8s.StripManagedFields)) _, err = factoryKube.Core().V1().Services().Informer().AddEventHandler(eventHandler) if err != nil { return nil, err