@@ -34,9 +34,13 @@ import (
3434// NewCacheFunc - Function for creating a new cache from the options and a rest config
3535type NewCacheFunc func (config * rest.Config , opts Options ) (Cache , error )
3636
37+ // a new global namespaced cache to handle cluster scoped resources
38+ var globalCache = ""
39+
3740// MultiNamespacedCacheBuilder - Builder function to create a new multi-namespaced cache.
3841// This will scope the cache to a list of namespaces. Listing for all namespaces
39- // will list for all the namespaces that this knows about. Note that this is not intended
42+ // will list for all the namespaces that this knows about. By default this will create
43+ // a global cache for cluster scoped resource (having empty namespace). Note that this is not intended
4044// to be used for excluding namespaces, this is better done via a Predicate. Also note that
4145// you may face performance issues when using this with a high number of namespaces.
4246func MultiNamespacedCacheBuilder (namespaces []string ) NewCacheFunc {
@@ -45,6 +49,8 @@ func MultiNamespacedCacheBuilder(namespaces []string) NewCacheFunc {
4549 if err != nil {
4650 return nil , err
4751 }
52+ // create a cache for cluster scoped resources
53+ namespaces = append (namespaces , globalCache )
4854 caches := map [string ]Cache {}
4955 for _ , ns := range namespaces {
5056 opts .Namespace = ns
@@ -143,7 +149,16 @@ func (c *multiNamespaceCache) List(ctx context.Context, list client.ObjectList,
143149 if ! ok {
144150 return fmt .Errorf ("unable to get: %v because of unknown namespace for the cache" , listOpts .Namespace )
145151 }
146- return cache .List (ctx , list , opts ... )
152+ err := cache .List (ctx , list , opts ... )
153+ if err != nil {
154+ return err
155+ }
156+ items , err := apimeta .ExtractList (list )
157+ if err != nil {
158+ return err
159+ }
160+ uniqueItems := removeDuplicates (items )
161+ return apimeta .SetList (list , uniqueItems )
147162 }
148163
149164 listAccessor , err := meta .ListAccessor (list )
@@ -174,9 +189,28 @@ func (c *multiNamespaceCache) List(ctx context.Context, list client.ObjectList,
174189 // The last list call should have the most correct resource version.
175190 resourceVersion = accessor .GetResourceVersion ()
176191 }
192+
193+ uniqueItems := removeDuplicates (allItems )
177194 listAccessor .SetResourceVersion (resourceVersion )
178195
179- return apimeta .SetList (list , allItems )
196+ return apimeta .SetList (list , uniqueItems )
197+ }
198+
199+ // removeDuplicates removes the duplicate objects obtained from all namespaces so that
200+ // the resulting list has objects with unique name and namespace.
201+ func removeDuplicates (items []runtime.Object ) []runtime.Object {
202+ objects := make (map [string ]bool )
203+ unique := []runtime.Object {}
204+
205+ for _ , obj := range items {
206+ metaObj , _ := meta .Accessor (obj )
207+ key := metaObj .GetName () + " " + metaObj .GetNamespace ()
208+ if _ , value := objects [key ]; ! value {
209+ objects [key ] = true
210+ unique = append (unique , obj )
211+ }
212+ }
213+ return unique
180214}
181215
182216// multiNamespaceInformer knows how to handle interacting with the underlying informer across multiple namespaces
0 commit comments