-
Notifications
You must be signed in to change notification settings - Fork 1.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Option to restrict informer cache to a namespace #136
Option to restrict informer cache to a namespace #136
Conversation
@DirectXMan12 PTAL |
@@ -128,7 +132,7 @@ func New(config *rest.Config, options Options) (Manager, error) { | |||
} | |||
|
|||
// Create the cache for the cached read client and registering informers | |||
cache, err := options.newCache(config, cache.Options{Scheme: options.Scheme, Mapper: mapper, Resync: options.SyncPeriod}) | |||
cache, err := options.newCache(config, cache.Options{Scheme: options.Scheme, Mapper: mapper, Resync: options.SyncPeriod, Namespace: options.Namespace}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When I need to watch all namespace events, set nil to options.Namespace
or set other value?
Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To watch all namespaces you set options.Namespaces = ""
or metav1.NamespaceAll which is the same thing.
But since the default string value is empty you can just not set the Namespace option in cache.Options
and it should watch all namespaces by default.
pkg/cache/internal/informers_map.go
Outdated
@@ -217,14 +223,14 @@ func (ip *InformersMap) newListWatch(gvk schema.GroupVersionKind) (*cache.ListWa | |||
return &cache.ListWatch{ | |||
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) { | |||
res := listObj.DeepCopyObject() | |||
err := client.Get().Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec).Do().Into(res) | |||
err := client.Get().Namespace(ip.namespace).Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec).Do().Into(res) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this need to be NamespaceIfScoped(ip.namespace, ip.namespace == "")
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You could do without NamespaceIfScoped
since the URL constructed ignores namespaces if namespace == ""
.
https://github.com/kubernetes/client-go/blob/master/rest/request.go#L424
But that behavior is implicit so I've added NamespaceIfScoped(ip.namespace, ip.namespace != "")
to make the distinction more clear.
cb979d2
to
9ddd976
Compare
@DirectXMan12 The request is explicitly namespace scoped now. PTAL. |
@droot can you please review this in case @DirectXMan12 is busy. |
@hasbro17 change looks good to me. @DirectXMan12 , would you like to take a final look ? |
@droot thanks. I think @DirectXMan12 should be back on Monday to take a look. |
9ddd976
to
b976451
Compare
Rebased and added another namespaced cache test for an unstructured object. |
pkg/cache/cache_test.go
Outdated
Expect(out.Items).Should(HaveLen(1)) | ||
Expect(out.Items[0].GetNamespace()).To(Equal(testNamespaceOne)) | ||
}) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
+1 for covering this.
pkg/manager/manager.go
Outdated
@@ -106,6 +106,10 @@ type Options struct { | |||
// will use for holding the leader lock. | |||
LeaderElectionID string | |||
|
|||
// Namespace if specified restricts the manager's cache to watch the desired namespace | |||
// Defaults to all namespaces |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May be make it more explicit:
watch
objects
in the desired namespace
Also one more q: if I specify the namespace here and later setup a watch for a cluster scoped resource (say Node), will I get error, what would be the UX ?
We should document that behavior here probably.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tested it out by specifying the manager's namespace and added a controller to watch nodes:
$ ./main --namespace=default
{"level":"info","ts":1537598131.6718707,"logger":"kubebuilder.controller","caller":"controller/controller.go:120","msg":"Starting EventSource","Controller":"node-controller","Source":{"Type":{"metadata":{"creationTimestamp":null},"spec":{},"status":{"daemonEndpoints":{"kubeletEndpoint":{"Port":0}},"nodeInfo":{"machineID":"","systemUUID":"","bootID":"","kernelVersion":"","osImage":"","containerRuntimeVersion":"","kubeletVersion":"","kubeProxyVersion":"","operatingSystem":"","architecture":""}}}}}
E0921 23:35:31.674413 15574 reflector.go:205] sigs.k8s.io/controller-runtime/pkg/cache/internal/informers_map.go:126: Failed to list *v1.Node: the server could not find the requested resource
E0921 23:35:32.680154 15574 reflector.go:205] sigs.k8s.io/controller-runtime/pkg/cache/internal/informers_map.go:126: Failed to list *v1.Node: the server could not find the requested resource
As expected the ListWatcher fails to List the nodes because when the namespace is set it will form an incorrect request to list Nodes by adding the namespace in the REST path.
https://github.com/kubernetes-sigs/controller-runtime/pull/136/files#diff-fc36bd1640f2ed10bc9e4f4d24789edfR235
The behavior seems consistent with the need for namespacing the manager i.e you don't expect to Watch cluster-scoped resources if your permissions are restricted to a namespace.
I've updated the comment to document this behavior.
b976451
to
495a3d8
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
realized that we also should check if the resource is namespaced in the first place.
pkg/cache/internal/informers_map.go
Outdated
@@ -225,14 +232,14 @@ func createStructuredListWatch(gvk schema.GroupVersionKind, ip *specificInformer | |||
return &cache.ListWatch{ | |||
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) { | |||
res := listObj.DeepCopyObject() | |||
err := client.Get().Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec).Do().Into(res) | |||
err := client.Get().NamespaceIfScoped(ip.namespace, ip.namespace != "").Resource(mapping.Resource.Resource).VersionedParams(&opts, ip.paramCodec).Do().Into(res) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it occurs to me that there's one last wrinkle for this -- we need to also check if the resource is namespace-scoped to begin with using the rest mapping -- i.e. you should be able to watch nodes when watching pods in a specific namespace.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so this should be NamespaceIfScoped(ip.namespace, ip.namespace != "" && mapping.Scope == apimeta.RESTScopeNameRoot)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
and below (in the watch)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed. Minor nit being it's:
ip.namespace != "" && mapping.Scope != apimeta.RESTScopeNameRoot
pkg/cache/internal/informers_map.go
Outdated
@@ -252,12 +259,18 @@ func createUnstructuredListWatch(gvk schema.GroupVersionKind, ip *specificInform | |||
// Create a new ListWatch for the obj | |||
return &cache.ListWatch{ | |||
ListFunc: func(opts metav1.ListOptions) (runtime.Object, error) { | |||
if ip.namespace != "" { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ditto here on the "is this namespaced"
495a3d8
to
6e82d0f
Compare
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.
6e82d0f
to
ca13e86
Compare
@DirectXMan12 I've updated the ListWatchers to check if the resource is namespace scoped before making a namespaced request. I've updated the tests with the additional case of ensuring a namespaced cache will still let you List a cluster-scoped resource( |
/lgtm |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: DirectXMan12, hasbro17 The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
…rs-namespaced Option to restrict informer cache to a namespace
Use "dep" to import dependencies for generated project
Fixes #124
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.