Skip to content
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

Merged
merged 1 commit into from
Mar 5, 2024

Conversation

jianjuns
Copy link
Contributor

@jianjuns jianjuns commented Feb 17, 2024

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

@jianjuns jianjuns added area/interface Issues or PRs related to network interfaces area/secondary-network Issues or PRs related to support for secondary networks in Antrea labels Feb 17, 2024
@jianjuns jianjuns marked this pull request as ready for review February 17, 2024 23:31
Comment on lines 241 to 242
// Not needed for a secondary network interface.
if pc.routeClient != nil {
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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).

Copy link
Contributor Author

@jianjuns jianjuns Feb 22, 2024

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.

Copy link
Contributor

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.

Copy link
Member

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

pkg/agent/cniserver/pod_configuration.go Show resolved Hide resolved
Comment on lines 241 to 242
// Not needed for a secondary network interface.
if pc.routeClient != nil {
Copy link
Member

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/pod_configuration.go Outdated Show resolved Hide resolved
Comment on lines 67 to 68
klog.InfoS("Deleted SR-IOV interface", "Pod",
klog.KRef(interfaceConfig.PodNamespace, interfaceConfig.PodName),
Copy link
Member

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. Fixed.

@jianjuns jianjuns force-pushed the secnet-store branch 4 times, most recently from 5165589 to 191f26f Compare March 1, 2024 06:48
Copy link
Member

@tnqn tnqn left a 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.

pkg/agent/cniserver/pod_configuration.go Outdated Show resolved Hide resolved
pkg/agent/cniserver/pod_configuration.go Outdated Show resolved Hide resolved
Comment on lines 30 to 34
podName, podNamespace, containerID, netNS string,
hostIface, containerIface *current.Interface,
ips []*current.IPConfig, vlanID uint16,
containerAccess *containerAccessArbitrator) (*interfacestore.InterfaceConfig, error) {
Copy link
Member

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.

Copy link
Contributor Author

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.

Comment on lines 67 to 71
podName, podNamespace, containerID, netNS string,
hostIface, containerIface *current.Interface,
ips []*current.IPConfig, vlanID uint16,
containerAccess *containerAccessArbitrator) (*interfacestore.InterfaceConfig, error) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Comment on lines 327 to 328
pod *corev1.Pod, network *netdefv1.NetworkSelectionElement,
podCNIInfo *podCNIInfo, networkConfig *SecondaryNetworkConfig) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto

Copy link
Contributor

@antoninbas antoninbas left a 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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
// SecondaryInterfaceConfigurator
// isSecondaryNetwork is true if this instance of podConfigurator is used to configure secondary Pod network interfaces.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 94 to 99
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)))}
Copy link
Contributor

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),
	)),
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines +259 to +261
klog.InfoS("Configured container interface", "Pod", klog.KRef(podNamespace, podName),
"container", containerID, "interface", containerIface.Name, "hostInterface", hostIface.Name)
Copy link
Contributor

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

Copy link
Contributor Author

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)
Copy link
Contributor

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

Copy link
Contributor Author

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))
Copy link
Contributor

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

Copy link
Contributor Author

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")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

require.NoError

Copy link
Contributor Author

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.
Copy link
Contributor

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?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes.

}
assert.True(t, existed, fmt.Sprintf("IP %s should exist in the restored InterfaceConfig", ip1.String()))
Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

Comment on lines 733 to 735
if !existed || parsedIFDev != "eth1" {
t.Errorf("Failed to store interface name to external IDs")
}
Copy link
Contributor

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")?

Copy link
Contributor Author

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)
Copy link
Contributor

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

Copy link
Contributor Author

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]>
Copy link
Contributor

@antoninbas antoninbas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Member

@tnqn tnqn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jianjuns
Copy link
Contributor Author

jianjuns commented Mar 5, 2024

/test-all

@jianjuns jianjuns merged commit 55d46ec into antrea-io:main Mar 5, 2024
50 of 55 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
area/interface Issues or PRs related to network interfaces area/secondary-network Issues or PRs related to support for secondary networks in Antrea
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support bridging and VLAN network for Pod secondary interfaces
4 participants