Skip to content

Commit 9974557

Browse files
behzad-mirCopilot
andcommitted
refactor: extract sortInfraNICFirst and isInfraOrLegacyNICType helpers
Address PR review comments: - Extract sortInfraNICFirst() to encapsulate the sort logic that ensures InfraNIC is always processed before other NIC types - Extract isInfraOrLegacyNICType() helper to deduplicate the repeated NICType == InfraNIC || NICType == "" checks - Replace complex integration test with simple unit test for sortInfraNICFirst covering multiple NIC type orderings - Use the helper in both the sort and the Delete path Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 324112b commit 9974557

2 files changed

Lines changed: 43 additions & 108 deletions

File tree

cni/network/network.go

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -647,6 +647,18 @@ func (plugin *NetPlugin) Add(args *cniSkel.CmdArgs) error {
647647
// SecondaryEndpointClient then moves eth1 into the pod namespace, and the subsequent InfraNIC
648648
// iteration finds the network bound to that now-gone interface, causing
649649
// TransparentEndpointClient.AddEndpoints to fail with "no such network interface".
650+
sortInfraNICFirst(epInfos)
651+
652+
err = plugin.nm.EndpointCreate(cnsclient, epInfos)
653+
if err != nil {
654+
return errors.Wrap(err, "failed to create endpoint") // behavior can change if you don't assign to err prior to returning
655+
}
656+
return nil
657+
}
658+
659+
// sortInfraNICFirst sorts endpoint infos so that InfraNIC (or legacy empty NICType)
660+
// entries come before all other NIC types, preserving relative order among equals.
661+
func sortInfraNICFirst(epInfos []*network.EndpointInfo) {
650662
slices.SortStableFunc(epInfos, func(a, b *network.EndpointInfo) int {
651663
if isInfraOrLegacyNICType(a.NICType) && !isInfraOrLegacyNICType(b.NICType) {
652664
return -1
@@ -656,12 +668,6 @@ func (plugin *NetPlugin) Add(args *cniSkel.CmdArgs) error {
656668
}
657669
return 0
658670
})
659-
660-
err = plugin.nm.EndpointCreate(cnsclient, epInfos)
661-
if err != nil {
662-
return errors.Wrap(err, "failed to create endpoint") // behavior can change if you don't assign to err prior to returning
663-
}
664-
return nil
665671
}
666672

667673
// isInfraOrLegacyNICType returns true if the NIC type is InfraNIC or empty (legacy).

cni/network/network_linux_test.go

Lines changed: 31 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -795,113 +795,42 @@ func (m *mockInterfaceGetter) GetNetworkInterfaceAddrs(_ *net.Interface) ([]net.
795795
return nil, errNotImplemented
796796
}
797797

798-
func TestAddSortsInfraNICFirst(t *testing.T) {
799-
// Verify that plugin.Add() sorts InfraNIC first even when IPAM returns
800-
// several non-infra NICs before infra. The mock records the order of
801-
// endpoint creation to confirm InfraNIC is always processed first.
802-
resources := GetTestResources()
803-
mac1 := "60:45:bd:76:f6:44"
804-
mac2 := "60:45:bd:76:f6:55"
805-
mac3 := "60:45:bd:76:f6:66"
806-
parsedMAC1, _ := net.ParseMAC(mac1)
807-
parsedMAC2, _ := net.ParseMAC(mac2)
808-
parsedMAC3, _ := net.ParseMAC(mac3)
809-
810-
var creationOrder []cns.NICType
811-
mockEpClient := network.NewMockEndpointClient(func(epInfo *network.EndpointInfo) error {
812-
creationOrder = append(creationOrder, epInfo.NICType)
813-
return nil
814-
})
815-
816-
nwCfg := cni.NetworkConfig{
817-
CNIVersion: "0.3.0",
818-
Name: "net",
819-
MultiTenancy: false,
820-
IPAM: cni.IPAM{Type: "azure-cns"},
821-
DNS: types.DNS{
822-
Nameservers: []string{"ns1", "ns2"},
823-
Domain: "myDomain",
798+
func TestSortInfraNICFirst(t *testing.T) {
799+
tests := []struct {
800+
name string
801+
input []cns.NICType
802+
wantFirst cns.NICType
803+
}{
804+
{
805+
name: "infra already first",
806+
input: []cns.NICType{cns.InfraNIC, cns.NodeNetworkInterfaceFrontendNIC, cns.NodeNetworkInterfaceFrontendNIC},
807+
wantFirst: cns.InfraNIC,
824808
},
825-
}
826-
827-
plugin := &NetPlugin{
828-
Plugin: resources.Plugin,
829-
nm: network.NewMockNetworkmanager(mockEpClient),
830-
ipamInvoker: &MockIpamInvoker{
831-
add: func(opt IPAMAddConfig) (IPAMAddResult, error) {
832-
// Return several FrontendNICs and one InfraNIC to test sorting.
833-
// FrontendNIC is the secondary NIC type used in Linux SwiftV2.
834-
result := IPAMAddResult{interfaceInfo: map[string]network.InterfaceInfo{
835-
"frontend1": {
836-
MacAddress: parsedMAC1,
837-
IPConfigs: []*network.IPConfig{
838-
{Address: *getCIDRNotationForAddress("10.241.0.35/32")},
839-
},
840-
NICType: cns.NodeNetworkInterfaceFrontendNIC,
841-
},
842-
"frontend2": {
843-
MacAddress: parsedMAC2,
844-
IPConfigs: []*network.IPConfig{
845-
{Address: *getCIDRNotationForAddress("10.241.0.36/32")},
846-
},
847-
NICType: cns.NodeNetworkInterfaceFrontendNIC,
848-
},
849-
"frontend3": {
850-
MacAddress: parsedMAC3,
851-
IPConfigs: []*network.IPConfig{
852-
{Address: *getCIDRNotationForAddress("10.241.0.37/32")},
853-
},
854-
NICType: cns.NodeNetworkInterfaceFrontendNIC,
855-
},
856-
string(cns.InfraNIC): {
857-
IPConfigs: []*network.IPConfig{
858-
{Address: *getCIDRNotationForAddress("20.241.0.35/16")},
859-
},
860-
NICType: cns.InfraNIC,
861-
HostSubnetPrefix: *getCIDRNotationForAddress("10.224.0.0/16"),
862-
},
863-
}}
864-
return result, nil
865-
},
866-
ipMap: make(map[string]bool),
809+
{
810+
name: "infra last among several frontends",
811+
input: []cns.NICType{cns.NodeNetworkInterfaceFrontendNIC, cns.NodeNetworkInterfaceFrontendNIC, cns.NodeNetworkInterfaceFrontendNIC, cns.InfraNIC},
812+
wantFirst: cns.InfraNIC,
867813
},
868-
netClient: &InterfaceGetterMock{
869-
interfaces: []net.Interface{
870-
{Name: "eth1", HardwareAddr: parsedMAC1},
871-
{Name: "eth2", HardwareAddr: parsedMAC2},
872-
{Name: "eth3", HardwareAddr: parsedMAC3},
873-
{Name: "primary", HardwareAddr: net.HardwareAddr{}},
874-
},
875-
interfaceAddrs: map[string][]net.Addr{
876-
"primary": {getCIDRNotationForAddress("10.224.0.0/16")},
877-
},
814+
{
815+
name: "infra in the middle",
816+
input: []cns.NICType{cns.DelegatedVMNIC, cns.InfraNIC, cns.NodeNetworkInterfaceFrontendNIC},
817+
wantFirst: cns.InfraNIC,
878818
},
879-
}
880-
881-
// Stub resolveMasterInterface for all frontend NICs
882-
original := nlClient
883-
nlClient = &mockNetlinkClient{
884-
links: map[string]vishnetlink.Link{
885-
"eth1": &vishnetlink.Dummy{LinkAttrs: vishnetlink.LinkAttrs{Name: "eth1", MasterIndex: 0}},
886-
"eth2": &vishnetlink.Dummy{LinkAttrs: vishnetlink.LinkAttrs{Name: "eth2", MasterIndex: 0}},
887-
"eth3": &vishnetlink.Dummy{LinkAttrs: vishnetlink.LinkAttrs{Name: "eth3", MasterIndex: 0}},
819+
{
820+
name: "empty NICType treated as infra",
821+
input: []cns.NICType{cns.NodeNetworkInterfaceFrontendNIC, ""},
822+
wantFirst: "",
888823
},
889824
}
890-
t.Cleanup(func() { nlClient = original })
891-
892-
args := &cniSkel.CmdArgs{
893-
StdinData: nwCfg.Serialize(),
894-
ContainerID: "test-container",
895-
Netns: "bc526fae-4ba0-4e80-bc90-ad721e5850bf",
896-
Args: fmt.Sprintf("K8S_POD_NAME=%v;K8S_POD_NAMESPACE=%v", "test-pod", "test-pod-ns"),
897-
IfName: eth0IfName,
898-
}
899825

900-
err := plugin.Add(args)
901-
require.NoError(t, err)
902-
require.Len(t, creationOrder, 4, "expected 4 endpoints to be created")
903-
assert.Equal(t, cns.InfraNIC, creationOrder[0], "InfraNIC should be created first")
904-
for i := 1; i < len(creationOrder); i++ {
905-
assert.Equal(t, cns.NodeNetworkInterfaceFrontendNIC, creationOrder[i], "non-infra NICs should be created after InfraNIC")
826+
for _, tt := range tests {
827+
t.Run(tt.name, func(t *testing.T) {
828+
epInfos := make([]*network.EndpointInfo, len(tt.input))
829+
for i, nic := range tt.input {
830+
epInfos[i] = &network.EndpointInfo{NICType: nic}
831+
}
832+
sortInfraNICFirst(epInfos)
833+
assert.Equal(t, tt.wantFirst, epInfos[0].NICType, "infra/legacy NIC should be sorted first")
834+
})
906835
}
907836
}

0 commit comments

Comments
 (0)