Skip to content

Commit 12f94f1

Browse files
🐛 filter global namespace while looking for cluster scoped resources (#1520)
* 🐛 filter global namespace while looking for cluster scoped resources In the current implementation of multinamespaced cache, we look at global namespace while listing objects from all namespaced. Global namespace should not be looked at while fetching namespaced resources. * create a cluser scoped cache separately Signed-off-by: varshaprasad96 <[email protected]>
1 parent 7181f11 commit 12f94f1

File tree

2 files changed

+80
-9
lines changed

2 files changed

+80
-9
lines changed

pkg/cache/multi_namespace_cache.go

Lines changed: 73 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ const globalCache = "_cluster-scope"
4141
// MultiNamespacedCacheBuilder - Builder function to create a new multi-namespaced cache.
4242
// This will scope the cache to a list of namespaces. Listing for all namespaces
4343
// will list for all the namespaces that this knows about. By default this will create
44-
// a global cache for cluster scoped resource (having empty namespace). Note that this is not intended
44+
// a global cache for cluster scoped resource. Note that this is not intended
4545
// to be used for excluding namespaces, this is better done via a Predicate. Also note that
4646
// you may face performance issues when using this with a high number of namespaces.
4747
func MultiNamespacedCacheBuilder(namespaces []string) NewCacheFunc {
@@ -50,9 +50,15 @@ func MultiNamespacedCacheBuilder(namespaces []string) NewCacheFunc {
5050
if err != nil {
5151
return nil, err
5252
}
53-
// create a cache for cluster scoped resources
54-
namespaces = append(namespaces, globalCache)
53+
5554
caches := map[string]Cache{}
55+
56+
// create a cache for cluster scoped resources
57+
gCache, err := New(config, opts)
58+
if err != nil {
59+
return nil, fmt.Errorf("error creating global cache %v", err)
60+
}
61+
5662
for _, ns := range namespaces {
5763
opts.Namespace = ns
5864
c, err := New(config, opts)
@@ -61,7 +67,7 @@ func MultiNamespacedCacheBuilder(namespaces []string) NewCacheFunc {
6167
}
6268
caches[ns] = c
6369
}
64-
return &multiNamespaceCache{namespaceToCache: caches, Scheme: opts.Scheme, RESTMapper: opts.Mapper}, nil
70+
return &multiNamespaceCache{namespaceToCache: caches, Scheme: opts.Scheme, RESTMapper: opts.Mapper, clusterCache: gCache}, nil
6571
}
6672
}
6773

@@ -73,36 +79,82 @@ type multiNamespaceCache struct {
7379
namespaceToCache map[string]Cache
7480
Scheme *runtime.Scheme
7581
RESTMapper meta.RESTMapper
82+
clusterCache Cache
7683
}
7784

7885
var _ Cache = &multiNamespaceCache{}
7986

8087
// Methods for multiNamespaceCache to conform to the Informers interface
8188
func (c *multiNamespaceCache) GetInformer(ctx context.Context, obj client.Object) (Informer, error) {
8289
informers := map[string]Informer{}
90+
91+
// If the object is clusterscoped, get the informer from clusterCache,
92+
// if not use the namespaced caches.
93+
isNamespaced, err := objectutil.IsAPINamespaced(obj, c.Scheme, c.RESTMapper)
94+
if err != nil {
95+
return nil, err
96+
}
97+
if !isNamespaced {
98+
clusterCacheInf, err := c.clusterCache.GetInformer(ctx, obj)
99+
if err != nil {
100+
return nil, err
101+
}
102+
informers[globalCache] = clusterCacheInf
103+
104+
return &multiNamespaceInformer{namespaceToInformer: informers}, nil
105+
}
106+
83107
for ns, cache := range c.namespaceToCache {
84108
informer, err := cache.GetInformer(ctx, obj)
85109
if err != nil {
86110
return nil, err
87111
}
88112
informers[ns] = informer
89113
}
114+
90115
return &multiNamespaceInformer{namespaceToInformer: informers}, nil
91116
}
92117

93118
func (c *multiNamespaceCache) GetInformerForKind(ctx context.Context, gvk schema.GroupVersionKind) (Informer, error) {
94119
informers := map[string]Informer{}
120+
121+
// If the object is clusterscoped, get the informer from clusterCache,
122+
// if not use the namespaced caches.
123+
isNamespaced, err := objectutil.IsAPINamespacedWithGVK(gvk, c.Scheme, c.RESTMapper)
124+
if err != nil {
125+
return nil, err
126+
}
127+
if !isNamespaced {
128+
clusterCacheInf, err := c.clusterCache.GetInformerForKind(ctx, gvk)
129+
if err != nil {
130+
return nil, err
131+
}
132+
informers[globalCache] = clusterCacheInf
133+
134+
return &multiNamespaceInformer{namespaceToInformer: informers}, nil
135+
}
136+
95137
for ns, cache := range c.namespaceToCache {
96138
informer, err := cache.GetInformerForKind(ctx, gvk)
97139
if err != nil {
98140
return nil, err
99141
}
100142
informers[ns] = informer
101143
}
144+
102145
return &multiNamespaceInformer{namespaceToInformer: informers}, nil
103146
}
104147

105148
func (c *multiNamespaceCache) Start(ctx context.Context) error {
149+
// start global cache
150+
go func() {
151+
err := c.clusterCache.Start(ctx)
152+
if err != nil {
153+
log.Error(err, "cluster scoped cache failed to start")
154+
}
155+
}()
156+
157+
// start namespaced caches
106158
for ns, cache := range c.namespaceToCache {
107159
go func(ns string, cache Cache) {
108160
err := cache.Start(ctx)
@@ -111,6 +163,7 @@ func (c *multiNamespaceCache) Start(ctx context.Context) error {
111163
}
112164
}(ns, cache)
113165
}
166+
114167
<-ctx.Done()
115168
return nil
116169
}
@@ -122,10 +175,24 @@ func (c *multiNamespaceCache) WaitForCacheSync(ctx context.Context) bool {
122175
synced = s
123176
}
124177
}
178+
179+
// check if cluster scoped cache has synced
180+
if !c.clusterCache.WaitForCacheSync(ctx) {
181+
synced = false
182+
}
125183
return synced
126184
}
127185

128186
func (c *multiNamespaceCache) IndexField(ctx context.Context, obj client.Object, field string, extractValue client.IndexerFunc) error {
187+
isNamespaced, err := objectutil.IsAPINamespaced(obj, c.Scheme, c.RESTMapper)
188+
if err != nil {
189+
return nil
190+
}
191+
192+
if !isNamespaced {
193+
return c.clusterCache.IndexField(ctx, obj, field, extractValue)
194+
}
195+
129196
for _, cache := range c.namespaceToCache {
130197
if err := cache.IndexField(ctx, obj, field, extractValue); err != nil {
131198
return err
@@ -142,8 +209,7 @@ func (c *multiNamespaceCache) Get(ctx context.Context, key client.ObjectKey, obj
142209

143210
if !isNamespaced {
144211
// Look into the global cache to fetch the object
145-
cache := c.namespaceToCache[globalCache]
146-
return cache.Get(ctx, key, obj)
212+
return c.clusterCache.Get(ctx, key, obj)
147213
}
148214

149215
cache, ok := c.namespaceToCache[key.Namespace]
@@ -165,8 +231,7 @@ func (c *multiNamespaceCache) List(ctx context.Context, list client.ObjectList,
165231

166232
if !isNamespaced {
167233
// Look at the global cache to get the objects with the specified GVK
168-
cache := c.namespaceToCache[globalCache]
169-
return cache.List(ctx, list, opts...)
234+
return c.clusterCache.List(ctx, list, opts...)
170235
}
171236

172237
if listOpts.Namespace != corev1.NamespaceAll {

pkg/internal/objectutil/objectutil.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,13 @@ func IsAPINamespaced(obj runtime.Object, scheme *runtime.Scheme, restmapper apim
5555
return false, err
5656
}
5757

58-
restmapping, err := restmapper.RESTMapping(schema.GroupKind{Group: gvk.Group, Kind: gvk.Kind})
58+
return IsAPINamespacedWithGVK(gvk, scheme, restmapper)
59+
}
60+
61+
// IsAPINamespacedWithGVK returns true if the object having the provided
62+
// GVK is namespace scoped.
63+
func IsAPINamespacedWithGVK(gk schema.GroupVersionKind, scheme *runtime.Scheme, restmapper apimeta.RESTMapper) (bool, error) {
64+
restmapping, err := restmapper.RESTMapping(schema.GroupKind{Group: gk.Group, Kind: gk.Kind})
5965
if err != nil {
6066
return false, fmt.Errorf("failed to get restmapping: %w", err)
6167
}

0 commit comments

Comments
 (0)