diff --git a/pkg/agent/cniserver/server_linux_test.go b/pkg/agent/cniserver/server_linux_test.go index 261116fafe9..b8c87dd3d6e 100644 --- a/pkg/agent/cniserver/server_linux_test.go +++ b/pkg/agent/cniserver/server_linux_test.go @@ -20,6 +20,7 @@ import ( "fmt" "net" "testing" + "time" cnitypes "github.com/containernetworking/cni/pkg/types" current "github.com/containernetworking/cni/pkg/types/100" @@ -45,6 +46,7 @@ import ( "antrea.io/antrea/pkg/ovs/ovsconfig" ovsconfigtest "antrea.io/antrea/pkg/ovs/ovsconfig/testing" "antrea.io/antrea/pkg/util/channel" + utilip "antrea.io/antrea/pkg/util/ip" ) func TestValidatePrevResult(t *testing.T) { @@ -652,59 +654,67 @@ func TestReconcile(t *testing.T) { }, }, } - containerIfaces := map[string]*interfacestore.InterfaceConfig{ - "iface1": { - InterfaceName: "iface1", - Type: interfacestore.ContainerInterface, - OVSPortConfig: &interfacestore.OVSPortConfig{ - PortUUID: generateUUID(t), - OFPort: int32(3), - }, - ContainerInterfaceConfig: &interfacestore.ContainerInterfaceConfig{ - PodName: "p1", - PodNamespace: testPodNamespace, - ContainerID: generateUUID(t), - }, + normalInterface := &interfacestore.InterfaceConfig{ + InterfaceName: "iface1", + Type: interfacestore.ContainerInterface, + IPs: []net.IP{net.ParseIP("1.1.1.1")}, + MAC: utilip.MustParseMAC("00:11:22:33:44:01"), + OVSPortConfig: &interfacestore.OVSPortConfig{ + PortUUID: generateUUID(t), + OFPort: int32(3), }, - "iface3": { - InterfaceName: "iface3", - Type: interfacestore.ContainerInterface, - OVSPortConfig: &interfacestore.OVSPortConfig{ - PortUUID: generateUUID(t), - OFPort: int32(4), - }, - ContainerInterfaceConfig: &interfacestore.ContainerInterfaceConfig{ - PodName: "p3", - PodNamespace: testPodNamespace, - ContainerID: generateUUID(t), - }, + ContainerInterfaceConfig: &interfacestore.ContainerInterfaceConfig{ + PodName: "p1", + PodNamespace: testPodNamespace, + ContainerID: generateUUID(t), }, - "iface4": { - InterfaceName: "iface4", - Type: interfacestore.ContainerInterface, - OVSPortConfig: &interfacestore.OVSPortConfig{ - PortUUID: generateUUID(t), - OFPort: int32(-1), - }, - ContainerInterfaceConfig: &interfacestore.ContainerInterfaceConfig{ - PodName: "p4", - PodNamespace: testPodNamespace, - ContainerID: generateUUID(t), - }, + } + staleInterface := &interfacestore.InterfaceConfig{ + InterfaceName: "iface3", + Type: interfacestore.ContainerInterface, + OVSPortConfig: &interfacestore.OVSPortConfig{ + PortUUID: generateUUID(t), + OFPort: int32(4), + }, + ContainerInterfaceConfig: &interfacestore.ContainerInterfaceConfig{ + PodName: "p3", + PodNamespace: testPodNamespace, + ContainerID: generateUUID(t), + }, + } + unconnectedInterface := &interfacestore.InterfaceConfig{ + InterfaceName: "iface4", + Type: interfacestore.ContainerInterface, + OVSPortConfig: &interfacestore.OVSPortConfig{ + PortUUID: generateUUID(t), + OFPort: int32(-1), + }, + ContainerInterfaceConfig: &interfacestore.ContainerInterfaceConfig{ + PodName: "p4", + PodNamespace: testPodNamespace, + ContainerID: generateUUID(t), }, } kubeClient := fakeclientset.NewSimpleClientset(pods...) cniServer.kubeClient = kubeClient - for _, containerIface := range containerIfaces { + for _, containerIface := range []*interfacestore.InterfaceConfig{normalInterface, staleInterface, unconnectedInterface} { ifaceStore.AddInterface(containerIface) } - mockOFClient.EXPECT().InstallPodFlows("iface1", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) - iface := containerIfaces["iface3"] - mockOFClient.EXPECT().UninstallPodFlows("iface3").Return(nil).Times(1) - mockOVSBridgeClient.EXPECT().DeletePort(iface.PortUUID).Return(nil).Times(1) + podFlowsInstalled := make(chan struct{}) + mockOFClient.EXPECT().InstallPodFlows(normalInterface.InterfaceName, normalInterface.IPs, normalInterface.MAC, uint32(normalInterface.OFPort), uint16(0), nil). + Do(func(_ string, _ []net.IP, _ net.HardwareAddr, _ uint32, _ uint16, _ *uint32) { + close(podFlowsInstalled) + }).Times(1) + mockOFClient.EXPECT().UninstallPodFlows(staleInterface.InterfaceName).Return(nil).Times(1) + mockOVSBridgeClient.EXPECT().DeletePort(staleInterface.PortUUID).Return(nil).Times(1) mockRoute.EXPECT().DeleteLocalAntreaFlexibleIPAMPodRule(gomock.Any()).Return(nil).Times(1) err := cniServer.reconcile() assert.NoError(t, err) - _, exists := ifaceStore.GetInterfaceByName("iface3") + _, exists := ifaceStore.GetInterfaceByName(staleInterface.InterfaceName) assert.False(t, exists) + select { + case <-podFlowsInstalled: + case <-time.After(500 * time.Millisecond): + t.Errorf("InstallPodFlows for %s should be called but was not", normalInterface.InterfaceName) + } } diff --git a/pkg/agent/cniserver/server_windows_test.go b/pkg/agent/cniserver/server_windows_test.go index 0a2d69399ca..ae33926ffce 100644 --- a/pkg/agent/cniserver/server_windows_test.go +++ b/pkg/agent/cniserver/server_windows_test.go @@ -47,6 +47,7 @@ import ( cnipb "antrea.io/antrea/pkg/apis/cni/v1beta1" ovsconfigtest "antrea.io/antrea/pkg/ovs/ovsconfig/testing" "antrea.io/antrea/pkg/util/channel" + utilip "antrea.io/antrea/pkg/util/ip" ) var ( @@ -897,72 +898,86 @@ func TestReconcile(t *testing.T) { } kubeClient := fakeclientset.NewSimpleClientset(pods...) cniServer.kubeClient = kubeClient - containerIfaces := map[string]*interfacestore.InterfaceConfig{ - "iface1": { - InterfaceName: "iface1", - Type: interfacestore.ContainerInterface, - OVSPortConfig: &interfacestore.OVSPortConfig{ - PortUUID: generateUUID(t), - OFPort: int32(3), - }, - ContainerInterfaceConfig: &interfacestore.ContainerInterfaceConfig{ - PodName: "p1", - PodNamespace: testPodNamespace, - ContainerID: generateUUID(t), - }, + normalInterface := &interfacestore.InterfaceConfig{ + InterfaceName: "iface1", + Type: interfacestore.ContainerInterface, + IPs: []net.IP{net.ParseIP("1.1.1.1")}, + MAC: utilip.MustParseMAC("00:11:22:33:44:01"), + OVSPortConfig: &interfacestore.OVSPortConfig{ + PortUUID: generateUUID(t), + OFPort: int32(3), }, - "iface3": { - InterfaceName: "iface3", - Type: interfacestore.ContainerInterface, - OVSPortConfig: &interfacestore.OVSPortConfig{ - PortUUID: generateUUID(t), - OFPort: int32(4), - }, - ContainerInterfaceConfig: &interfacestore.ContainerInterfaceConfig{ - PodName: "p3", - PodNamespace: testPodNamespace, - ContainerID: generateUUID(t), - }, + ContainerInterfaceConfig: &interfacestore.ContainerInterfaceConfig{ + PodName: "p1", + PodNamespace: testPodNamespace, + ContainerID: generateUUID(t), }, - "iface4": { - InterfaceName: "iface4", - Type: interfacestore.ContainerInterface, - OVSPortConfig: &interfacestore.OVSPortConfig{ - PortUUID: generateUUID(t), - OFPort: int32(-1), - }, - ContainerInterfaceConfig: &interfacestore.ContainerInterfaceConfig{ - PodName: "p4", - PodNamespace: testPodNamespace, - ContainerID: generateUUID(t), - }, + } + staleInterface := &interfacestore.InterfaceConfig{ + InterfaceName: "iface3", + Type: interfacestore.ContainerInterface, + OVSPortConfig: &interfacestore.OVSPortConfig{ + PortUUID: generateUUID(t), + OFPort: int32(4), + }, + ContainerInterfaceConfig: &interfacestore.ContainerInterfaceConfig{ + PodName: "p3", + PodNamespace: testPodNamespace, + ContainerID: generateUUID(t), + }, + } + unconnectedInterface := &interfacestore.InterfaceConfig{ + InterfaceName: "iface4", + Type: interfacestore.ContainerInterface, + IPs: []net.IP{net.ParseIP("1.1.1.2")}, + MAC: utilip.MustParseMAC("00:11:22:33:44:02"), + OVSPortConfig: &interfacestore.OVSPortConfig{ + PortUUID: generateUUID(t), + OFPort: int32(-1), + }, + ContainerInterfaceConfig: &interfacestore.ContainerInterfaceConfig{ + PodName: "p4", + PodNamespace: testPodNamespace, + ContainerID: generateUUID(t), }, } - for _, containerIface := range containerIfaces { + for _, containerIface := range []*interfacestore.InterfaceConfig{normalInterface, staleInterface, unconnectedInterface} { ifaceStore.AddInterface(containerIface) } - pod4IfaceName := "iface4" - pod4Iface := containerIfaces["iface4"] - waiter := newAsyncWaiter(pod4Iface.PodName, pod4Iface.ContainerID) + waiter := newAsyncWaiter(unconnectedInterface.PodName, unconnectedInterface.ContainerID) cniServer.podConfigurator, _ = newPodConfigurator(mockOVSBridgeClient, mockOFClient, mockRoute, ifaceStore, gwMAC, "system", false, false, waiter.notifier) cniServer.nodeConfig = &config.NodeConfig{Name: nodeName} // Re-install Pod1 flows - mockOFClient.EXPECT().InstallPodFlows("iface1", gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) + podFlowsInstalled := make(chan string, 2) + mockOFClient.EXPECT().InstallPodFlows(normalInterface.InterfaceName, normalInterface.IPs, normalInterface.MAC, uint32(normalInterface.OFPort), uint16(0), nil). + Do(func(interfaceName string, _ []net.IP, _ net.HardwareAddr, _ uint32, _ uint16, _ *uint32) { + podFlowsInstalled <- interfaceName + }).Times(1) // Uninstall Pod3 flows which is deleted. - iface := containerIfaces["iface3"] - mockOFClient.EXPECT().UninstallPodFlows("iface3").Return(nil).Times(1) - mockOVSBridgeClient.EXPECT().DeletePort(iface.PortUUID).Return(nil).Times(1) + mockOFClient.EXPECT().UninstallPodFlows(staleInterface.InterfaceName).Return(nil).Times(1) + mockOVSBridgeClient.EXPECT().DeletePort(staleInterface.PortUUID).Return(nil).Times(1) mockRoute.EXPECT().DeleteLocalAntreaFlexibleIPAMPodRule(gomock.Any()).Return(nil).Times(1) // Re-connect to Pod4 - hostIfaces.Store(fmt.Sprintf("vEthernet (%s)", pod4IfaceName), true) - mockOVSBridgeClient.EXPECT().SetInterfaceType(pod4IfaceName, "internal").Return(nil).Times(1) - mockOVSBridgeClient.EXPECT().GetOFPort(pod4IfaceName, true).Return(int32(5), nil).Times(1) - mockOFClient.EXPECT().InstallPodFlows(pod4IfaceName, gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(1) + hostIfaces.Store(fmt.Sprintf("vEthernet (%s)", unconnectedInterface.InterfaceName), true) + mockOVSBridgeClient.EXPECT().SetInterfaceType(unconnectedInterface.InterfaceName, "internal").Return(nil).Times(1) + mockOVSBridgeClient.EXPECT().GetOFPort(unconnectedInterface.InterfaceName, true).Return(int32(5), nil).Times(1) + mockOFClient.EXPECT().InstallPodFlows(unconnectedInterface.InterfaceName, unconnectedInterface.IPs, unconnectedInterface.MAC, uint32(5), uint16(0), nil). + Do(func(interfaceName string, _ []net.IP, _ net.HardwareAddr, _ uint32, _ uint16, _ *uint32) { + podFlowsInstalled <- interfaceName + }).Times(1) err := cniServer.reconcile() assert.NoError(t, err) _, exists := ifaceStore.GetInterfaceByName("iface3") assert.False(t, exists) + for i := 0; i < 2; i++ { + select { + case <-podFlowsInstalled: + case <-time.After(500 * time.Millisecond): + t.Errorf("InstallPodFlows should be called 2 times but was only called %d times", i) + break + } + } waiter.wait() waiter.close() } diff --git a/pkg/util/ip/ip.go b/pkg/util/ip/ip.go index bd82ea18989..58563e2274c 100644 --- a/pkg/util/ip/ip.go +++ b/pkg/util/ip/ip.go @@ -195,6 +195,14 @@ func MustParseCIDR(cidr string) *net.IPNet { return ipNet } +func MustParseMAC(mac string) net.HardwareAddr { + addr, err := net.ParseMAC(mac) + if err != nil { + panic(fmt.Errorf("cannot parse '%v': %v", mac, err)) + } + return addr +} + // IPNetEqual returns if the provided IPNets are the same subnet. func IPNetEqual(ipNet1, ipNet2 *net.IPNet) bool { if ipNet1 == nil && ipNet2 == nil {