-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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
Multiple auto-egress-IP fixes #18808
Multiple auto-egress-IP fixes #18808
Conversation
This makes the tests a little bit more self-documenting, and makes it simpler to add new tests. Also, change it to only look at OVS flows in the egress table, so that when there is a mismatch, the error message is of a manageable length.
Split out one test case, add some new (already-passing) test cases, add some comments, and remove some double-checking-of-internal-state that will break with upcoming rewrites.
(We handled delete+recreate correctly, but not update.)
25a0ddd
to
b23627e
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
/assign @dcbw |
pkg/network/node/egressip.go
Outdated
ns.assignedIP = "" | ||
ns.nodeIP = "" | ||
if ns.requestedIP != "" { | ||
eg := eip.egressIPs[ns.requestedIP] |
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.
Are we sure eg isn't going to be nil here?
pkg/network/node/egressip.go
Outdated
if oldNode := eip.nodesByEgressIP[ip]; oldNode != nil { | ||
utilruntime.HandleError(fmt.Errorf("Multiple nodes claiming EgressIP %q (nodes %q, %q)", ip, node.nodeIP, oldNode.nodeIP)) | ||
continue | ||
eg := eip.egressIPs[ip] |
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 almost want:
eg := eip.ensureEgressIPInfo(ip)
if eg == nil {
return
}
...
}
func (eip *egressIPWatcher) ensureEgressIPInfo(ip string) *egressIPInfo {
if ip == "" {
return nil
}
eg := eip.egressIPs[ip]
if eg == nil {
eg = &egressIPInfo{ip: ip}
eip.egressIPs[ip] = eg
}
return eg
}
which you'd also use down below.
pkg/network/node/egressip.go
Outdated
} | ||
|
||
eip.nodesByEgressIP[ip] = node | ||
eip.maybeAddEgressIP(ip) | ||
if len(eg.nodes) != 0 { |
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.
Also kinda want:
func (eg *egressIPInfo) addNode(node *nodeEgress, ip string) {
if len(eg.nodes) != 0 {
utilruntime.HandleError(fmt.Errorf("Multiple nodes claiming EgressIP %q (nodes %q, %q)", ip, node.nodeIP, eg.nodes[0].nodeIP))
}
eg.nodes = append(eg.nodes, node)
}
func (eg *egressIPInfo) delNode(node *nodeEgress) {
for i := range eg.nodes {
if eg.nodes[i] == node {
eg.nodes = append(eg.nodes[:i], eg.nodes[i+1:]...)
return
}
}
}
and the corresponding functions for namespaces.
pkg/network/node/egressip.go
Outdated
// The egressIPInfo should have an assigned node IP if and only if the | ||
// egress IP is active (ie, it is assigned to exactly 1 node and exactly | ||
// 1 namespace). | ||
egressIPActive := (len(eg.nodes) == 1 && len(eg.namespaces) == 1) |
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 if you do choose to go with struct egressIPInfo functions, some of the stuff here could be done as functions too. Obviously it can go too far, but maybe a bit cleaner. Your call.
maybe even
changed := eg.syncNodeIP(eip.masqueradeBit, eip.localIP, eip.assignEgressIP, eip.releaseEgressIP)
func (eg *egressIPInfo) syncNodeIP(masqBit uint32, localIP string, assign func(string, string) error, func(string, string) error) bool {
...
}
b23627e
to
7e5a3c0
Compare
I took most of your suggestions. I didn't change syncEgressIP much other than to split it into two subfunctions, one for iptables stuff and one for OVS stuff. The OVS stuff doesn't really split further very well because the blockedVNIDs state has to flow through it so they'd just have to be passed around if we did split it up further |
There should never be multiple HostSubnets or multiple NetNamespaces claiming the same egress IP, but if there are, we need to track them carefully so we don't get out sync with reality after things are fixed.
7e5a3c0
to
a21509b
Compare
The changes LGTM too. @dcbw? |
/retest |
/lgtm |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: danwinship, dcbw 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 |
/retest |
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.
Added some comments, rest of the changes LGTM
ns.requestedIP = egressIP | ||
eip.namespacesByEgressIP[egressIP] = ns | ||
eip.maybeAddEgressIP(egressIP) | ||
if egressIP == "" { |
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 think this is to handle delete case where ns is no longer referenced in eip.namespacesByVNID?
To be clear, may be we could assign ns.requestedIP = egressip
after this check
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, you're right, the ns.requestedIP assignment is irrelevant (but harmless) in this case
@@ -266,44 +233,120 @@ func (eip *egressIPWatcher) updateNamespaceEgress(vnid uint32, egressIP string) | |||
|
|||
ns := eip.namespacesByVNID[vnid] |
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.
Need to consider join and isolate project operations otherwise there will be some problems.
Example:
- ns1 with vnid v1 requested egrees ip e1
- ns2 with vnid v2 requested egress ip e2
- ns2 joined with ns1, now ns2 has vnid v1
- eip.namespacesByVNID[v1] will now have requestedIP=e2
- ns2 got isolated
Now, v1 has only one namespace ns1 but the requested ip will be set as ep2 instead of ep1 which is incorrect.
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.
filed #18852
|
||
func (eg *egressIPInfo) addNode(node *nodeEgress) { | ||
if len(eg.nodes) != 0 { | ||
utilruntime.HandleError(fmt.Errorf("Multiple nodes claiming EgressIP %q (nodes %q, %q)", eg.ip, node.nodeIP, eg.nodes[0].nodeIP)) |
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.
Can we clarify that only the first node that claimed the egress IP will have egress rules?
(syncEgressIPTablesState() checks eg.nodes[0].nodeIP)
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 none of them will; syncEgressIPTablesState() only looks at eg.nodes[0] when len(eg.nodes)==1
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, that's right. Adding a comment like https://github.com/openshift/origin/pull/18808/files#diff-77d23d75ed1c9b57573523db2f13c52aR308 somewhere would be helpful.
// 1 namespace). | ||
egressIPActive := (len(eg.nodes) == 1 && len(eg.namespaces) == 1) | ||
assignedNodeIPChanged := false | ||
if egressIPActive && eg.assignedNodeIP != eg.nodes[0].nodeIP { |
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.
There could be very rare corner case when F5 is involved.
Scenario:
- F5 node n1(no real backing node only hostSubnet object) requested egress ip e1
- node n2 requested same egress ip e1
- eg.nodes == [n1, n2], n1 claims the egress ip e1
- SDN controller calls delete + create on F5 node n1 (removes AssignHostSubnetAnnoation and assigns vnid)
- eg.nodes == [n2, n1] now n2 claims the egress ip e1 (side effect of how we handle F5 case)
This is theoretical case but may never happen in practice. I don't think we need to fix this 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.
as above, there's no actual corner case here because egressIPActive would be false at both step 3 and step 5
/retest |
|
||
func (eg *egressIPInfo) addNode(node *nodeEgress) { | ||
if len(eg.nodes) != 0 { | ||
utilruntime.HandleError(fmt.Errorf("Multiple nodes claiming EgressIP %q (nodes %q, %q)", eg.ip, node.nodeIP, eg.nodes[0].nodeIP)) |
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, that's right. Adding a comment like https://github.com/openshift/origin/pull/18808/files#diff-77d23d75ed1c9b57573523db2f13c52aR308 somewhere would be helpful.
/test end_to_end |
/retest |
3 similar comments
/retest |
/retest |
/retest |
/retest |
1 similar comment
/retest |
/hold |
/hold cancel |
/retest |
Automatic merge from submit-queue (batch tested with PRs 18780, 18802, 18391, 18832, 18808). |
OK, 3 "setup" commits followed by 2 commits with fixes:
egressip_test.go
, so clearly don't break the actual functioning of auto-egress-IPegressip.go
and then a larger update toegressip_test.go
to show that the fix works. Specifically: if theEgressIPs
field on a NetNamespace was changed while that egress IP was active, then we did not clean up all of the state associated with the old egress IP, because we were accidentally cleaning up the state associated with the new egress IP instead.Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1551028
Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1547899
Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1543786
Fixes https://bugzilla.redhat.com/show_bug.cgi?id=1520363
@openshift/sig-networking PTAL