-
Notifications
You must be signed in to change notification settings - Fork 387
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
Update Pod annotations for Service type change #1936
Conversation
Codecov Report
@@ Coverage Diff @@
## main #1936 +/- ##
==========================================
+ Coverage 61.57% 62.06% +0.48%
==========================================
Files 265 265
Lines 19770 19774 +4
==========================================
+ Hits 12174 12272 +98
+ Misses 6333 6233 -100
- Partials 1263 1269 +6
Flags with carried forward coverage won't be shown. Click here to find out more.
|
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.
out-of-curiosity, do you think it would make sense to log a warning in the Agent's NPL controller when processing a NodePort Service which has the NPL enabled annotation?
podKeys = oldPodSet.Difference(newPodSet).Union(newPodSet.Difference(oldPodSet)) | ||
oldPodSet = sets.NewString(c.getPodsFromService(oldSvc)...) | ||
newPodSet = sets.NewString(c.getPodsFromService(newSvc)...) | ||
podKeys = podKeys.Union(oldPodSet.Difference(newPodSet).Union(newPodSet.Difference(oldPodSet))) |
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.
could we write our own SymmetricDifference
method instead of doing oldPodSet.Difference(newPodSet).Union(newPodSet.Difference(oldPodSet))
? It will be more readable and it may also be more efficient since we would not be building intermediate sets.
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, maybe the difference is minor here, but I found using Union when calculating the union of many sets affected the performance a lot (in particular, the TestInitXLargeScaleWithOneNamespace test) as it always makes a new set and copies all items when merging each set. I added a function to improve it in #1938. Maybe we could have a separate PR to add SymmetricDifference
in same place and replace all such usage.
var oldPodSet, newPodSet sets.String | ||
if oldSvc.Spec.Type != newSvc.Spec.Type { | ||
newPodSet = sets.NewString(c.getPodsFromService(newSvc)...) | ||
podKeys = podKeys.Union(newPodSet) | ||
} | ||
|
||
if !reflect.DeepEqual(oldSvc.Spec.Selector, newSvc.Spec.Selector) { | ||
// Disjunctive union of Pods from both Service sets. | ||
oldPodSet := sets.NewString(c.getPodsFromService(oldSvc)...) | ||
newPodSet := sets.NewString(c.getPodsFromService(newSvc)...) | ||
podKeys = oldPodSet.Difference(newPodSet).Union(newPodSet.Difference(oldPodSet)) | ||
oldPodSet = sets.NewString(c.getPodsFromService(oldSvc)...) | ||
newPodSet = sets.NewString(c.getPodsFromService(newSvc)...) | ||
podKeys = podKeys.Union(oldPodSet.Difference(newPodSet).Union(newPodSet.Difference(oldPodSet))) |
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 seems that this code does not really need to execute in case of an annotation change (oldSvcAnnotation != newSvcAnnotation
), should it be in an else
block?
If I look at this edge case:
oldSvcAnnotation
is truenewSvcAnnotation
is falseoldSvc.Spec.Type != newSvc.Spec.Type
oldSvc.Spec.Selector != newSvc.Spec.Selector
You actually end up enqueuing both old Pods and new Pods, when you only need to enqueue old Pods.
|
||
var oldPodSet, newPodSet sets.String | ||
if oldSvc.Spec.Type != newSvc.Spec.Type { | ||
newPodSet = sets.NewString(c.getPodsFromService(newSvc)...) |
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 Service types we care about? In all the Service type change case, we always use the newPodSet?
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.
If nplAnnotation changes from false to true, the newPortSet is already counted in, and we can avoid getting the set twice?
if oldSvc.Spec.Type != newSvc.Spec.Type { | ||
newPodSet = sets.NewString(c.getPodsFromService(newSvc)...) | ||
if newPodSet != 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.
Again, what Service types we care about? In all the Service type change case, we should always process the newPodSet?
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.
We essentially care about transitions from non-nodeport -> nodeport
and vice-versa.
In cases of Service Type updates, I anticipate no change in the set of Pods (considering selectors to be identical at this moment), so we can just check the newPodSet for this, since old one must be 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.
In cases of Service Type updates, I anticipate no change in the set of Pods (considering selectors to be identical at this moment), so we can just check the newPodSet for this, since old one must be the same.
Can you clarify this? I don't think there is any guarantee that the Service Type update will be the only change in the Service spec. The selector may change as well. Even if the user first updated the Service Type, then updated the Selector separately, these 2 updates may be combined and the update handler may be called a single time.
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.
that is correct, the selector may change as well in the same update, in which case it should come in the following conditional, where it compares the selectors in the spec.
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 believe the logic is still not correct. With the latest code it seems that if only the annotation changes and nothing else, we do not process any Pod, which doesn't seem right.
I suggest the following logic, which I find easier to read and hopefully should be correct:
if newSvcAnnotation == "true" && newSvc.Spec.Type == corev1.ServiceTypeNodePort {
klog.Warningf("Service %s is of type NodePort and cannot be used for NodePortLocal, the '%s' annotation will have no effect", newSvc.Name, NPLEnabledAnnotationKey)
}
var oldPodSet, newPodSet sets.String
sameSelector := reflect.DeepEqual(oldSvc.Spec.Selector, newSvc.Spec.Selector)
oldPodSet = sets.NewString(c.getPodsFromService(oldSvc)...)
if sameSelector {
newPodSet = oldPodSet
} else {
newPodSet = sets.NewString(c.getPodsFromService(newSvc)...)
}
podKeys := sets.String{}
oldNPLEnabled = oldSvcAnnotation == "true" && oldSvc.Spec.Type != corev1.ServiceTypeNodePort
newNPLEnabled = newSvcAnnotation == "true" && newSvc.Spec.Type != corev1.ServiceTypeNodePort
switch {
case oldNPLEnabled && newNPLEnabled && !sameSelector:
podKeys = utilsets.SymmetricDifference(oldPodSet, newPodSet)
case oldNPLEnabled && !newNPLEnabled:
podKeys = oldPodSet
case newNPLEnabled && !oldNPLEnabled:
podKeys = newPodSet
}
What do you think? It should take care of Jianjun's comment as well.
Also please rebase the PR and resolve conflicts.
@antoninbas : but could we avoid calling getPodsFromService() when not needed, as it is expensive? |
sure, that can be moved to the individual case statements as needed |
71d7068
to
f894784
Compare
Missed the podKeys initialization while checking for annotation changes, the latest commit takes care of that. We can look at refactoring the function, if it looks unreadable right now, but I feel it has a logical sequencing to it that separates out various Service key/value comparisons. |
} else if !reflect.DeepEqual(oldSvc.Spec.Selector, newSvc.Spec.Selector) { | ||
} | ||
|
||
if oldSvc.Spec.Type != newSvc.Spec.Type { |
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.
Should we check one of old or new type is NodePort here, if we just care about changing to NodePort or changing from NodePort?
I think in cases where Service annotation or Service type changes, the Pod set is not affected, so maybe we can just rely on the Pods from the new Service. The Pod sets will only differ when the selector changes. |
Also maybe this
can be simplified further to
Which essentially means that This is not part of the PR yet, I can add it in the next commit, if this sounds right to you all. EDIT: I think symmetricDifference is still required |
The proposal looks good to me. I am fine not to optimize Service type change further, as it is rather uncommon. |
if podKeys == nil { | ||
podKeys = sets.NewString(c.getPodsFromService(newSvc)...) | ||
} | ||
podKeys = utilsets.SymmetricDifference(oldPodSet, podKeys) |
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.
If annotation/Type is updated as well as Selector, don't it need to process the pods in the intersection?
For example: update a service's annotation from true to false and its selector from app=foo to tier=frontend, shouldn't all pods matching app=foo be processed?
@chauhanshubham could we try to merge this for the v1.0 release (Wednesday)? |
includes a UT for the same. Fixes antrea-io#1910
f5c8575
to
347cfa1
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.
LGTM. Thanks for addressing the comments!
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
/test-all |
includes a UT for the same.
Fixes #1910