Skip to content

Commit b976451

Browse files
committed
Option to restrict informer cache to a namespace
A namespace option can be passed from the manager to the cache which restricts the ListWatch for all informers to the desired namespace. This way a manager can be run with a Role instead of a ClusterRole.
1 parent df7c11e commit b976451

File tree

5 files changed

+83
-12
lines changed

5 files changed

+83
-12
lines changed

pkg/cache/cache.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,10 @@ type Options struct {
7979

8080
// Resync is the resync period. Defaults to defaultResyncTime.
8181
Resync *time.Duration
82+
83+
// Namespace restricts the cache's ListWatch to the desired namespace
84+
// Default watches all namespaces
85+
Namespace string
8286
}
8387

8488
var defaultResyncTime = 10 * time.Hour
@@ -89,7 +93,7 @@ func New(config *rest.Config, opts Options) (Cache, error) {
8993
if err != nil {
9094
return nil, err
9195
}
92-
im := internal.NewInformersMap(config, opts.Scheme, opts.Mapper, *opts.Resync)
96+
im := internal.NewInformersMap(config, opts.Scheme, opts.Mapper, *opts.Resync, opts.Namespace)
9397
return &informerCache{InformersMap: im}, nil
9498
}
9599

pkg/cache/cache_test.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,28 @@ var _ = Describe("Informer Cache", func() {
195195
Expect(actual.Namespace).To(Equal(testNamespaceOne))
196196
})
197197

198+
It("should be able to restrict cache to a namespace", func() {
199+
By("creating a namespaced cache")
200+
namespacedCache, err := cache.New(cfg, cache.Options{Namespace: testNamespaceOne})
201+
Expect(err).NotTo(HaveOccurred())
202+
203+
By("running the cache and waiting for it to sync")
204+
go func() {
205+
defer GinkgoRecover()
206+
Expect(namespacedCache.Start(stop)).To(Succeed())
207+
}()
208+
Expect(namespacedCache.WaitForCacheSync(stop)).NotTo(BeFalse())
209+
210+
By("listing pods in all namespaces")
211+
out := &kcorev1.PodList{}
212+
Expect(namespacedCache.List(context.Background(), nil, out)).To(Succeed())
213+
214+
By("verifying the returned pod is from the watched namespace")
215+
Expect(out.Items).NotTo(BeEmpty())
216+
Expect(out.Items).Should(HaveLen(1))
217+
Expect(out.Items[0].Namespace).To(Equal(testNamespaceOne))
218+
})
219+
198220
It("should deep copy the object unless told otherwise", func() {
199221
By("retrieving a specific pod from the cache")
200222
out := &kcorev1.Pod{}
@@ -333,6 +355,33 @@ var _ = Describe("Informer Cache", func() {
333355
Expect(actual.GetNamespace()).To(Equal(testNamespaceOne))
334356
})
335357

358+
It("should be able to restrict cache to a namespace", func() {
359+
By("creating a namespaced cache")
360+
namespacedCache, err := cache.New(cfg, cache.Options{Namespace: testNamespaceOne})
361+
Expect(err).NotTo(HaveOccurred())
362+
363+
By("running the cache and waiting for it to sync")
364+
go func() {
365+
defer GinkgoRecover()
366+
Expect(namespacedCache.Start(stop)).To(Succeed())
367+
}()
368+
Expect(namespacedCache.WaitForCacheSync(stop)).NotTo(BeFalse())
369+
370+
By("listing pods in all namespaces")
371+
out := &unstructured.UnstructuredList{}
372+
out.SetGroupVersionKind(schema.GroupVersionKind{
373+
Group: "",
374+
Version: "v1",
375+
Kind: "PodList",
376+
})
377+
Expect(namespacedCache.List(context.Background(), nil, out)).To(Succeed())
378+
379+
By("verifying the returned pod is from the watched namespace")
380+
Expect(out.Items).NotTo(BeEmpty())
381+
Expect(out.Items).Should(HaveLen(1))
382+
Expect(out.Items[0].GetNamespace()).To(Equal(testNamespaceOne))
383+
})
384+
336385
It("should deep copy the object unless told otherwise", func() {
337386
By("retrieving a specific pod from the cache")
338387
out := &unstructured.Unstructured{}

pkg/cache/internal/deleg_map.go

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,12 @@ type InformersMap struct {
4444
func NewInformersMap(config *rest.Config,
4545
scheme *runtime.Scheme,
4646
mapper meta.RESTMapper,
47-
resync time.Duration) *InformersMap {
47+
resync time.Duration,
48+
namespace string) *InformersMap {
4849

4950
return &InformersMap{
50-
structured: newStructuredInformersMap(config, scheme, mapper, resync),
51-
unstructured: newUnstructuredInformersMap(config, scheme, mapper, resync),
51+
structured: newStructuredInformersMap(config, scheme, mapper, resync, namespace),
52+
unstructured: newUnstructuredInformersMap(config, scheme, mapper, resync, namespace),
5253

5354
Scheme: scheme,
5455
}
@@ -85,11 +86,11 @@ func (m *InformersMap) Get(gvk schema.GroupVersionKind, obj runtime.Object) (*Ma
8586
}
8687

8788
// newStructuredInformersMap creates a new InformersMap for structured objects.
88-
func newStructuredInformersMap(config *rest.Config, scheme *runtime.Scheme, mapper meta.RESTMapper, resync time.Duration) *specificInformersMap {
89-
return newSpecificInformersMap(config, scheme, mapper, resync, createStructuredListWatch)
89+
func newStructuredInformersMap(config *rest.Config, scheme *runtime.Scheme, mapper meta.RESTMapper, resync time.Duration, namespace string) *specificInformersMap {
90+
return newSpecificInformersMap(config, scheme, mapper, resync, namespace, createStructuredListWatch)
9091
}
9192

9293
// newUnstructuredInformersMap creates a new InformersMap for unstructured objects.
93-
func newUnstructuredInformersMap(config *rest.Config, scheme *runtime.Scheme, mapper meta.RESTMapper, resync time.Duration) *specificInformersMap {
94-
return newSpecificInformersMap(config, scheme, mapper, resync, createUnstructuredListWatch)
94+
func newUnstructuredInformersMap(config *rest.Config, scheme *runtime.Scheme, mapper meta.RESTMapper, resync time.Duration, namespace string) *specificInformersMap {
95+
return newSpecificInformersMap(config, scheme, mapper, resync, namespace, createUnstructuredListWatch)
9596
}

pkg/cache/internal/informers_map.go

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,9 @@ type createListWatcherFunc func(gvk schema.GroupVersionKind, ip *specificInforme
4242
func newSpecificInformersMap(config *rest.Config,
4343
scheme *runtime.Scheme,
4444
mapper meta.RESTMapper,
45-
resync time.Duration, createListWatcher createListWatcherFunc) *specificInformersMap {
45+
resync time.Duration,
46+
namespace string,
47+
createListWatcher createListWatcherFunc) *specificInformersMap {
4648
ip := &specificInformersMap{
4749
config: config,
4850
Scheme: scheme,
@@ -52,6 +54,7 @@ func newSpecificInformersMap(config *rest.Config,
5254
paramCodec: runtime.NewParameterCodec(scheme),
5355
resync: resync,
5456
createListWatcher: createListWatcher,
57+
namespace: namespace,
5558
}
5659
return ip
5760
}
@@ -102,6 +105,10 @@ type specificInformersMap struct {
102105
// and allows for abstracting over the particulars of structured vs
103106
// unstructured objects.
104107
createListWatcher createListWatcherFunc
108+
109+
// namespace is the namespace that all ListWatches are restricted to
110+
// default or empty string means all namespaces
111+
namespace string
105112
}
106113

107114
// Start calls Run on each of the informers and sets started to true. Blocks on the stop channel.
@@ -225,14 +232,14 @@ func createStructuredListWatch(gvk schema.GroupVersionKind, ip *specificInformer
225232
return &cache.ListWatch{
226233
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
227234
res := listObj.DeepCopyObject()
228-
err := client.Get().Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec).Do().Into(res)
235+
err := client.Get().NamespaceIfScoped(ip.namespace, ip.namespace != "").Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec).Do().Into(res)
229236
return res, err
230237
},
231238
// Setup the watch function
232239
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
233240
// Watch needs to be set to true separately
234241
opts.Watch = true
235-
return client.Get().Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec).Watch()
242+
return client.Get().NamespaceIfScoped(ip.namespace, ip.namespace != "").Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec).Watch()
236243
},
237244
}, nil
238245
}
@@ -252,12 +259,18 @@ func createUnstructuredListWatch(gvk schema.GroupVersionKind, ip *specificInform
252259
// Create a new ListWatch for the obj
253260
return &cache.ListWatch{
254261
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) {
262+
if ip.namespace != "" {
263+
return dynamicClient.Resource(mapping.Resource).Namespace(ip.namespace).List(opts)
264+
}
255265
return dynamicClient.Resource(mapping.Resource).List(opts)
256266
},
257267
// Setup the watch function
258268
WatchFunc: func(opts metav1.ListOptions) (watch.Interface, error) {
259269
// Watch needs to be set to true separately
260270
opts.Watch = true
271+
if ip.namespace != "" {
272+
return dynamicClient.Resource(mapping.Resource).Namespace(ip.namespace).Watch(opts)
273+
}
261274
return dynamicClient.Resource(mapping.Resource).Watch(opts)
262275
},
263276
}, nil

pkg/manager/manager.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,10 @@ type Options struct {
106106
// will use for holding the leader lock.
107107
LeaderElectionID string
108108

109+
// Namespace if specified restricts the manager's cache to watch the desired namespace
110+
// Defaults to all namespaces
111+
Namespace string
112+
109113
// Dependency injection for testing
110114
newCache func(config *rest.Config, opts cache.Options) (cache.Cache, error)
111115
newClient func(config *rest.Config, options client.Options) (client.Client, error)
@@ -153,7 +157,7 @@ func New(config *rest.Config, options Options) (Manager, error) {
153157
}
154158

155159
// Create the cache for the cached read client and registering informers
156-
cache, err := options.newCache(config, cache.Options{Scheme: options.Scheme, Mapper: mapper, Resync: options.SyncPeriod})
160+
cache, err := options.newCache(config, cache.Options{Scheme: options.Scheme, Mapper: mapper, Resync: options.SyncPeriod, Namespace: options.Namespace})
157161
if err != nil {
158162
return nil, err
159163
}

0 commit comments

Comments
 (0)