diff --git a/pkg/cache/cache_test.go b/pkg/cache/cache_test.go index 287651abc7..e5e5d9ca65 100644 --- a/pkg/cache/cache_test.go +++ b/pkg/cache/cache_test.go @@ -182,6 +182,7 @@ func CacheTest(createCacheFunc func(config *rest.Config, opts cache.Options) (ca It("should support filtering by labels from multiple namespaces", func() { By("creating another pod with the same label but different namespace") anotherPod := createPod("test-pod-2", testNamespaceOne, kcorev1.RestartPolicyAlways) + defer deletePod(anotherPod) By("listing pods with a particular label") // NB: each pod has a "test-label": @@ -195,8 +196,6 @@ func CacheTest(createCacheFunc func(config *rest.Config, opts cache.Options) (ca for _, actual := range out.Items { Expect(actual.Labels["test-label"]).To(Equal("test-pod-2")) } - - deletePod(anotherPod) }) It("should be able to list objects by namespace", func() { @@ -313,6 +312,7 @@ func CacheTest(createCacheFunc func(config *rest.Config, opts cache.Options) (ca It("should support filtering by labels from multiple namespaces", func() { By("creating another pod with the same label but different namespace") anotherPod := createPod("test-pod-2", testNamespaceOne, kcorev1.RestartPolicyAlways) + defer deletePod(anotherPod) By("listing pods with a particular label") // NB: each pod has a "test-label": @@ -333,7 +333,6 @@ func CacheTest(createCacheFunc func(config *rest.Config, opts cache.Options) (ca Expect(actual.GetLabels()["test-label"]).To(Equal("test-pod-2")) } - deletePod(anotherPod) }) It("should be able to list objects by namespace", func() { diff --git a/pkg/cache/multi_namespace_cache.go b/pkg/cache/multi_namespace_cache.go index 4145f7562a..41f790e2fa 100644 --- a/pkg/cache/multi_namespace_cache.go +++ b/pkg/cache/multi_namespace_cache.go @@ -18,11 +18,12 @@ package cache import ( "context" - "encoding/json" "fmt" "time" corev1 "k8s.io/api/core/v1" + apimeta "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -133,6 +134,24 @@ func (c *multiNamespaceCache) Get(ctx context.Context, key client.ObjectKey, obj return cache.Get(ctx, key, obj) } +func (c *multiNamespaceCache) getNewObj(list runtime.Object) (runtime.Object, error) { + // Get all the objects in the namespaces we are watching. + gvk, err := apiutil.GVKForObject(list, c.Scheme) + if err != nil { + return nil, err + } + if _, ok := list.(*unstructured.UnstructuredList); ok { + obj := &unstructured.UnstructuredList{} + obj.SetGroupVersionKind(gvk) + return obj, nil + } + listObj, err := c.Scheme.New(gvk) + if err != nil { + return nil, err + } + return listObj, nil +} + // List multi namespace cache will get all the objects in the namespaces that the cache is watching if asked for all namespaces. func (c *multiNamespaceCache) List(ctx context.Context, list runtime.Object, opts ...client.ListOptionFunc) error { listOpts := client.ListOptions{} @@ -145,28 +164,40 @@ func (c *multiNamespaceCache) List(ctx context.Context, list runtime.Object, opt return cache.List(ctx, list, opts...) } - // Get all the objects in the namespaces we are watching. - gvk, err := apiutil.GVKForObject(list, c.Scheme) + listAccessor, ok := list.(metav1.ListInterface) + if !ok { + return fmt.Errorf("object: %T must be a list type", list) + } + + allItems, err := apimeta.ExtractList(list) if err != nil { return err } - allItems := &unstructured.UnstructuredList{} + var resourceVersion string for _, cache := range c.namespaceToCache { - items := &unstructured.UnstructuredList{} - items.SetGroupVersionKind(gvk) - err := cache.List(ctx, items, opts...) + listObj, err := c.getNewObj(list) + if err != nil { + return err + } + err = cache.List(ctx, listObj, opts...) + if err != nil { + return err + } + items, err := apimeta.ExtractList(listObj) if err != nil { return err } - allItems.Items = append(allItems.Items, items.Items...) + accessor, ok := listObj.(metav1.ListInterface) + if !ok { + return fmt.Errorf("object: %T must be a list type", list) + } + allItems = append(allItems, items...) // The last list call should have the most correct resource version. - allItems.Object = items.Object - } - data, err := allItems.MarshalJSON() - if err != nil { - return err + resourceVersion = accessor.GetResourceVersion() } - return json.Unmarshal(data, list) + listAccessor.SetResourceVersion(resourceVersion) + + return apimeta.SetList(list, allItems) } // multiNamespaceInformer knows how to handle interacting with the underlying informer across multiple namespaces