-
Notifications
You must be signed in to change notification settings - Fork 368
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
Save secondary network interfaces in InterfaceStore #5998
Conversation
49655e7
to
9cfe293
Compare
// Not needed for a secondary network interface. | ||
if pc.routeClient != 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.
I understand why we reuse the interface store for secondary interfaces, but do we need to also reuse the same PodConfigurator code, given that it leads to all these conditionals?
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 you look at this func, only this block cannot be reused for secondary interface. I think it is just a tradeoff to make. I do not have a strong opinion. Let me know what you prefer.
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.
More generally, I was referring to other PodConfigurator
methods as well, in particular func (pc *podConfigurator) connectInterfaceToOVSCommon
.
To me it ends up being a bit more confusing that having a dedicated workflow for secondary network interfaces (with a shared interface store of course).
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, there are other funcs, which also means there is more code can be shared in this way. I feel it does not look bad to check some interfaces (routeClient, ofClient, podUpdateNotifier) are not nil before calling their funcs. But I can change to duplicating the required code for secondary interfaces if you want that.
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 chatted with Jianjun, and I'm fine with either approach, but we fell like we should check with @tnqn in case he has a preference.
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'm fine with the way the code is shared. However, I feel it looks a little obscure to use various nil check to determine the workflow. It's not very straightforward to understand and we need to elaborate instances of podConfigurator
to make sure it can work correctly for two cases.
Instead, could we make the code more explicit and perhaps more readable by adding a parameter to the functions, like isSecondaryInterface bool
, or calculate it from containerIFDev != "eth0"
in the beginning of the functions? Another benefit is that we wouldn't need to create two podConfigurator
instances and can remove NewSecondaryInterfaceConfigurator
.
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 still want a separate podConfigurator
as we need a separate OVS bridge client, and I want not to use the primary interface InterfaceStore for secondary interfaces.
I can add a new argument like isSecondaryInterface to the funcs.
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 thought over, and now feel better to add an attribute to podConfigurator
to indicate it is for secondary network. Will update the code.
// Not needed for a secondary network interface. | ||
if pc.routeClient != 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.
I'm fine with the way the code is shared. However, I feel it looks a little obscure to use various nil check to determine the workflow. It's not very straightforward to understand and we need to elaborate instances of podConfigurator
to make sure it can work correctly for two cases.
Instead, could we make the code more explicit and perhaps more readable by adding a parameter to the functions, like isSecondaryInterface bool
, or calculate it from containerIFDev != "eth0"
in the beginning of the functions? Another benefit is that we wouldn't need to create two podConfigurator
instances and can remove NewSecondaryInterfaceConfigurator
.
pkg/agent/cniserver/secondary.go
Outdated
klog.InfoS("Deleted SR-IOV interface", "Pod", | ||
klog.KRef(interfaceConfig.PodNamespace, interfaceConfig.PodName), |
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.
Perhaps better to have a key-value pair in the same line.
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.
Sure. Fixed.
5165589
to
191f26f
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.
Some nits about format.
podName, podNamespace, containerID, netNS string, | ||
hostIface, containerIface *current.Interface, | ||
ips []*current.IPConfig, vlanID uint16, | ||
containerAccess *containerAccessArbitrator) (*interfacestore.InterfaceConfig, error) { |
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, the original one argument per line is more common in golang. Normally it's either all parameters in one line for short parameter list or one parameter per line for long parameter list. For example:
https://github.com/golang/go/blob/b5a64ba62eafe5dee13562091ca03aef6cac87b6/src/go/format/internal.go#L94-L101
https://github.com/kubernetes/kubernetes/blob/d51e2da869350b2b20a33d060e17bd1d2165e02d/pkg/kubelet/kubelet.go#L338-L365
In addition to being neat, there will be less diff when adding/updating/deleting the parameters.
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 changed to breaking lines by types. Check if that works for you.
podName, podNamespace, containerID, netNS string, | ||
hostIface, containerIface *current.Interface, | ||
ips []*current.IPConfig, vlanID uint16, | ||
containerAccess *containerAccessArbitrator) (*interfacestore.InterfaceConfig, error) { |
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
pod *corev1.Pod, network *netdefv1.NetworkSelectionElement, | ||
podCNIInfo *podCNIInfo, networkConfig *SecondaryNetworkConfig) error { |
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
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.
some minor comments, otherwise LGTM
@@ -72,6 +75,8 @@ type podConfigurator struct { | |||
// podUpdateNotifier is used for notifying updates of local Pods to other components which may benefit from this | |||
// information, i.e. NetworkPolicyController, EgressController. | |||
podUpdateNotifier channel.Notifier | |||
// SecondaryInterfaceConfigurator |
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.
// SecondaryInterfaceConfigurator | |
// isSecondaryNetwork is true if this instance of podConfigurator is used to configure secondary Pod network interfaces. |
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.
Done
pkg/agent/agent_test.go
Outdated
interfacestore.NewContainerInterface("p1", uuid1, "pod1", "ns1", p1NetMAC, []net.IP{p1NetIP}, 0)))} | ||
interfacestore.NewContainerInterface("p1", uuid1, "pod1", "ns1", "eth0", | ||
p1NetMAC, []net.IP{p1NetIP}, 0)))} | ||
ovsPort2 := ovsconfig.OVSPortData{UUID: uuid2, Name: "p2", IFName: "p2", OFPort: 12, | ||
ExternalIDs: convertExternalIDMap(cniserver.BuildOVSPortExternalIDs( | ||
interfacestore.NewContainerInterface("p2", uuid2, "pod2", "ns2", p2NetMAC, []net.IP{p2NetIP}, 0)))} | ||
interfacestore.NewContainerInterface("p2", uuid2, "pod2", "ns2", "eth0", | ||
p2NetMAC, []net.IP{p2NetIP}, 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.
nit: based on Quan's earlier comment, these should probably be broken up differently. I usually go with:
ovsPort2 := ovsconfig.OVSPortData{UUID: uuid2, Name: "p2", IFName: "p2", OFPort: 12,
ExternalIDs: convertExternalIDMap(cniserver.BuildOVSPortExternalIDs(
interfacestore.NewContainerInterface("p2", uuid2, "pod2", "ns2", "eth0", p2NetMAC, []net.IP{p2NetIP}, 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.
Done
klog.InfoS("Configured container interface", "Pod", klog.KRef(podNamespace, podName), | ||
"container", containerID, "interface", containerIface.Name, "hostInterface", hostIface.Name) |
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: we also tend to stick to a single line for these
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 prefer a new line here, otherwise it is too long.
if err != nil { | ||
return fmt.Errorf("failed to get of_port of OVS port %s: %v", ovsPortName, err) | ||
} | ||
klog.V(2).Infof("Setting up Openflow entries for container %s", containerID) |
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.
maybe update to klog.InfoS
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.
Done
assert.NoError(t, pc.ConfigureVLANSecondaryInterface(podName, testPodNamespace, containerID, containerNS, "eth2", 1600, ipamResult)) | ||
assert.Equal(t, 2, ifaceStore.Len()) | ||
intfConfigs := ifaceStore.GetContainerInterfacesByPod(podName, testPodNamespace) | ||
assert.Equal(t, 2, len(intfConfigs)) |
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 is an assert.Len
assertion available for 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.
Done
mockOVSBridgeClient := ovsconfigtest.NewMockOVSBridgeClient(controller) | ||
ifaceStore := interfacestore.NewInterfaceStore() | ||
pc, err := NewSecondaryInterfaceConfigurator(mockOVSBridgeClient, ifaceStore) | ||
require.Nil(t, err, "No error expected in podConfigurator constructor") |
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.
require.NoError
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.
Done
klog.ErrorS(err, "Failed to delete container interface link", | ||
"Pod", klog.KRef(interfaceConfig.PodNamespace, interfaceConfig.PodName), | ||
"interface", interfaceConfig.IFDev) | ||
// No retry for interface deletion. |
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.
Is my understanding correct that if the Pod is deleted (i.e., the netns is deleted), there can be no leftover veth anyway?
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.
pkg/agent/cniserver/server_test.go
Outdated
} | ||
assert.True(t, existed, fmt.Sprintf("IP %s should exist in the restored InterfaceConfig", ip1.String())) |
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 don't think you need to call fmt.Sprintf
manually. assert.Truef
(or even assert.True
?) will do the formatting for you
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.
Done
pkg/agent/cniserver/server_test.go
Outdated
if !existed || parsedIFDev != "eth1" { | ||
t.Errorf("Failed to store interface name to external IDs") | ||
} |
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 stick to assert.True(t, existed && parsedIFDev == "eth1")
?
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.
Done
for _, containerInterface := range []*InterfaceConfig{containerInterface1, containerInterface2} { | ||
interfaceKey := util.GenerateContainerInterfaceKey(containerInterface.ContainerID, containerInterface.IFDev) | ||
storedIface, exists := store.GetInterface(interfaceKey) | ||
assert.True(t, exists) |
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.
feels like this would be better as a require.True
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.
Done
Change to using InterfaceStore to store secondary interfaces. This enables more code sharing of OVS interface configuration between primary and secondary interfaces, and faciliates interface configuration persistence and restoration after agent restarts. VLAN interface restoration will be implemented by a future commit. Signed-off-by: Jianjun Shen <[email protected]>
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
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 |
Change to using InterfaceStore to store secondary interfaces. This enables more code sharing of OVS interface configuration between primary and secondary interfaces, and faciliates interface configuration persistence and restoration after agent restarts.
VLAN interface restoration will be implemented by a future commit.
Closes: #5278