Skip to content

Commit

Permalink
Using apimachinery to manipulate the lists
Browse files Browse the repository at this point in the history
  • Loading branch information
Shawn Hurley committed Feb 20, 2019
1 parent 5559c19 commit e2d83e8
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 17 deletions.
5 changes: 2 additions & 3 deletions pkg/cache/cache_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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": <pod-name>
Expand All @@ -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() {
Expand Down Expand Up @@ -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": <pod-name>
Expand All @@ -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() {
Expand Down
59 changes: 45 additions & 14 deletions pkg/cache/multi_namespace_cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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{}
Expand All @@ -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
Expand Down

0 comments on commit e2d83e8

Please sign in to comment.