-
Notifications
You must be signed in to change notification settings - Fork 7.6k
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
Support crd discovery selector #36639
Conversation
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.
This is a breaking change. The API explicitly only applies to discovery, so making it do more will be incompatible.
It would be good to revisit the design doc. I recall we explicitly decided to make it only discovery. If we are changing our minds there we ought to understand why our decisions have changed.
The api does not explicitly say it does not limit CR, and also it does not explicitly say it works on it. Will search the design |
Unfortunately, there is even no mention of crd in the design https://docs.google.com/document/d/1S3GPCUBmTmvh09NmpuMCG3vwDTNkVeRhGT3lbLyA4qs/edit?resourcekey=0-a8iMutRwjQElYN5dlgEDdw#heading=h.h3lxcxfhqndp SO I think there is no compatibility issue, this can be treated as a feature request |
Regardless of if it's in the doc dramatically changing the functionality of an existing API is incompatible. Let's take a step back here. I haven't seen any discussion of the motivations for this change. We already have a lot of 'scoping' mechanisms. What problems are we trying to solve here? |
@howardjohn @ramaraochavali What do i need to promote this feature? |
I still don't understand the use case. The issue you linked does not explain why they want this. |
An special case i can think of is in multi tenant case, multi istio can be deployed in a single cluster. We donot want the crd{CR} to interference each other. |
Doesn't Sidecar scoping handle this? In multitenant situations I would imagine its typical to not actually have permission to read from all namespaces? |
I think this is a pretty important topic and should probably have a proper design. At the very least, we cannot change the behavior of the existing field or there will be incompatible behavior change when users of discoverySelector upgrade to this commit |
I donot think there are many users of discoverySelector who have CR created in namespaces that are not selected. For sake, I am fine to add a knob for this. We can also propose a discussion about it. |
All the CRs it does not scope are already scoped by namespace. For example, if you have a PeerAuthentication if |
For us (non-vanilla) this might be a convenient way to avoid configuration leakage between multiple tenants/istiod control planes. Tenant boundaries could be defined in terms of immutable For vanilla users, this might be useful to include or exclude namespaces for configuration discovery, for example to ignore "less trusted" namespaces when operating in a shared environment. |
@howardjohn sidecar scoping solves part of the issue, but what about the gateways? |
@kfaseela Gateways have namespace scoping options already: PILOT_SCOPE_GATEWAY_TO_NAMESPACE or use new Kubernetes Gateway API which has this as default |
Would try this end to end along with sidecar scoping and get back. But wouldn't it simplify the configuration a lot when we have this supported out of the box, while using discovery selectors? Ideally we don't want to configure additional sidecar config to limit the CRD scope. |
This might also be useful as an option to lock down certain CR discovery settings that cannot be overridden by the user (default mesh-wide sidecar w/o workload selector in root namespace is easily overridden by another sidecar if I understand correctly) |
2387605
to
1819ba4
Compare
We have implemented this feature in production but by little dirty way. We can deploy multiple istiod instances in one K8s cluster for tenant case. These istiod instances can not only locate at individual namespaces, but also locate at the same namespace isolated by labels of CR. Part of code changes like below. Server.
KubeClient.
|
return i.Lister() | ||
} | ||
return i.Lister().ByNamespace(namespace) | ||
informer: informer.NewFilteredSharedIndexInformer(cl.namespacesFilter, i.Informer()), | ||
} |
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.
lister: cache.NewGenericLister(h.informer.GetIndexer(), schema.Resource().GroupVersionResource().GroupResource())
can we do this?
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.
Definitely we can implement it in theory, but I think currently it is enough to use
NewGenericLister does not provide much benefit to our case
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.
Why not? It makes the code much simpler as we do not need to do the weird indexer operations we have replaced it with, and is standard k8s practice
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.
As you can see with generic lister for clusterscoped resource, we need to handle differently. however with underlying indexer, we do not need to care about it, that is why I believe this way is more simple
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.
Actually, all the indexer related code are in pkg/kube, the fiteredInformer does only provide Get List interface.
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 am not sure how it's simpler. The lister requires 1 single if statement here - the indexer approach has like 50 lines of added complexity throughout the PR of more complex error handling, type casting, and still has the if for namespaces but at each List call instead of in one single place
Listers are the Kubernetes standard for his to list objects, I don't see a compelling reason to move away from that. It also seems unrelated to the rest of the PR.
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.
GenericLister returns runtime.Object, which also need type cast, error handling. I do not have strong preference though
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.
yes but we don't need this: https://github.com/istio/istio/pull/36639/files#diff-eff3d408ac506e9395b51f480c999a588f9fa408e392ce77506f25ceae00d6a1R328
and we now have to do:
item, _, err := h.informer.GetIndexer().GetByKey(KeyFunc(namespace, name))
if item == nil || err != nil {
instead of just
obj, err := h.lister(namespace).Get(name)
if err != nil {
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.
overall it's looking right but it's changing a lot of unrelated things which I think add complexity
ingressLister: ingressInformer.Lister(), | ||
classes: classes, | ||
serviceInformer: serviceInformer.Informer(), | ||
serviceLister: serviceInformer.Lister(), | ||
} | ||
|
||
if options.DiscoveryNamespacesFilter != nil { | ||
c.filteredIngressInformer = informer.NewFilteredSharedIndexInformer(options.DiscoveryNamespacesFilter.Filter, ingressInformer.Informer()) |
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.
nit: no need for if, the result is the same
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.
still applicable but not blocking
pkg/kube/namespace/filter.go
Outdated
@@ -74,6 +74,10 @@ func (d *discoveryNamespacesFilter) Filter(obj any) bool { | |||
return true | |||
} | |||
|
|||
if ns, ok := obj.(string); ok { |
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.
what is this for?
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.
This is to filter out namespaces, which is useful for namespace controller
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.
Its not used in this PR, right? Just the future?
In the namespace controllre PR we discussed making a FilterObject(obj Object)
and FilterNamespace(namespace string)
. Instead of doing all the type casting, etc. This is more explicit API
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.
removed
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.
lgtm, we can followup on the rest
ingressLister: ingressInformer.Lister(), | ||
classes: classes, | ||
serviceInformer: serviceInformer.Informer(), | ||
serviceLister: serviceInformer.Lister(), | ||
} | ||
|
||
if options.DiscoveryNamespacesFilter != nil { | ||
c.filteredIngressInformer = informer.NewFilteredSharedIndexInformer(options.DiscoveryNamespacesFilter.Filter, ingressInformer.Informer()) |
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.
still applicable but not blocking
@@ -802,7 +803,7 @@ func (c *Controller) syncNodes() error { | |||
|
|||
func (c *Controller) syncServices() error { | |||
var err *multierror.Error | |||
services := c.serviceInformer.GetIndexer().List() | |||
services, _ := c.serviceInformer.List("") |
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.
nit: use metav1.NamespaceAll
if c.opts.DiscoveryNamespacesFilter != nil { | ||
ec.filteredInformer = informer.NewFilteredSharedIndexInformer(c.opts.DiscoveryNamespacesFilter.Filter, dInformer.Informer()) | ||
} else { | ||
ec.filteredInformer = informer.NewFilteredSharedIndexInformer(nil, dInformer.Informer()) |
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.
same comment about reducing if
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.
c.opts.DiscoveryNamespacesFilter can be nil, this can be optimized later, because we need to filter namespace string as well
nsInformer := kubeclientset.KubeInformer().Core().V1().Namespaces().Informer() | ||
_ = nsInformer.SetTransform(kube.StripUnusedFields) | ||
nsLister := kubeclientset.KubeInformer().Core().V1().Namespaces().Lister() | ||
controller.DiscoveryNamespacesFilter = filter.NewDiscoveryNamespacesFilter(nsLister, meshWatcher.Mesh().GetDiscoverySelectors()) |
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.
do we want to protect this with the feature flag?
Address some followup comments
Please provide a description of this PR:
Fix #36627