Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cni/netconfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ type NetworkConfig struct {
EnableExactMatchForPodName bool `json:"enableExactMatchForPodName,omitempty"`
DisableHairpinOnHostInterface bool `json:"disableHairpinOnHostInterface,omitempty"`
DisableIPTableLock bool `json:"disableIPTableLock,omitempty"`
DisableAsyncDelete bool `json:"disableAsyncDelete,omitempty"`
CNSUrl string `json:"cnsurl,omitempty"`
ExecutionMode string `json:"executionMode,omitempty"`
IPAM IPAM `json:"ipam,omitempty"`
Expand Down
33 changes: 32 additions & 1 deletion cni/network/invoker_cns.go
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ func (invoker *CNSIPAMInvoker) Add(addConfig IPAMAddConfig) (IPAMAddResult, erro
}

logger.Info("Received info for pod",
zap.Any("ipInfo", info),
zap.Any("ipInfo", response.PodIPInfo[i]),
zap.Any("podInfo", podInfo))

//nolint:exhaustive // ignore exhaustive types check
Expand All @@ -192,6 +192,11 @@ func (invoker *CNSIPAMInvoker) Add(addConfig IPAMAddConfig) (IPAMAddResult, erro
if err := addBackendNICToResult(&info, &addResult, key); err != nil {
return IPAMAddResult{}, err
}
case cns.ApipaNIC:
if err := configureApipaAddResult(&addResult, &response.PodIPInfo[i], key); err != nil {
return IPAMAddResult{}, err
}

case cns.InfraNIC, "":
// if we change from legacy cns, the nicType will be empty, so we assume it is infra nic
info.nicType = cns.InfraNIC
Expand Down Expand Up @@ -508,6 +513,32 @@ func configureSecondaryAddResult(info *IPResultInfo, addResult *IPAMAddResult, p
return nil
}

func configureApipaAddResult(addResult *IPAMAddResult, info *cns.PodIpInfo, key string) error {
ip, ipnet, err := info.PodIPConfig.GetIPNet()
if ip == nil {
return errors.Wrap(err, "GetIPNet failed while configuring apipa AddResult")
}

addResult.interfaceInfo[key] = network.InterfaceInfo{
IPConfigs: []*network.IPConfig{
{
Address: net.IPNet{
IP: ip,
Mask: ipnet.Mask,
},
Gateway: net.ParseIP(info.NetworkContainerPrimaryIPConfig.GatewayIPAddress),
},
},
NICType: info.NICType,
SkipDefaultRoutes: true,
NetworkContainerID: info.NetworkContainerID,
AllowHostToNCCommunication: info.AllowHostToNCCommunication,
AllowNCToHostCommunication: info.AllowNCToHostCommunication,
}

return nil
}

func addBackendNICToResult(info *IPResultInfo, addResult *IPAMAddResult, key string) error {
macAddress, err := net.ParseMAC(info.macAddress)
if err != nil {
Expand Down
312 changes: 312 additions & 0 deletions cni/network/invoker_cns_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2298,3 +2298,315 @@ func TestMultipleIBNICsToResult(t *testing.T) {
})
}
}

func TestCNSIPAMInvoker_Add_ApipaNIC(t *testing.T) {
require := require.New(t)

type fields struct {
podName string
podNamespace string
cnsClient cnsclient
ipamMode util.IpamMode
}
type args struct {
nwCfg *cni.NetworkConfig
args *cniSkel.CmdArgs
options map[string]interface{}
}

tests := []struct {
name string
fields fields
args args
wantApipaResult network.InterfaceInfo
wantErr bool
wantErrMsg string
}{
{
name: "Test CNI Add with InfraNIC + ApipaNIC",
fields: fields{
podName: testPodInfo.PodName,
podNamespace: testPodInfo.PodNamespace,
cnsClient: &MockCNSClient{
require: require,
requestIPs: requestIPsHandler{
ipconfigArgument: cns.IPConfigsRequest{
PodInterfaceID: "testcont-testifname",
InfraContainerID: "testcontainerid",
OrchestratorContext: marshallPodInfo(testPodInfo),
},
result: &cns.IPConfigsResponse{
PodIPInfo: []cns.PodIpInfo{
{
PodIPConfig: cns.IPSubnet{
IPAddress: "10.0.1.10",
PrefixLength: 24,
},
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
IPSubnet: cns.IPSubnet{
IPAddress: "10.0.1.0",
PrefixLength: 24,
},
GatewayIPAddress: "10.0.1.1",
},
HostPrimaryIPInfo: cns.HostIPInfo{
Gateway: "10.0.0.1",
PrimaryIP: "10.0.0.1",
Subnet: "10.0.0.0/24",
},
NICType: cns.InfraNIC,
},
{
PodIPConfig: cns.IPSubnet{
IPAddress: "169.254.128.10",
PrefixLength: 17,
},
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
GatewayIPAddress: "169.254.128.1",
},
NICType: cns.ApipaNIC,
NetworkContainerID: "test-nc-id",
AllowHostToNCCommunication: true,
AllowNCToHostCommunication: false,
},
},
Response: cns.Response{
ReturnCode: 0,
Message: "",
},
},
err: nil,
},
},
},
args: args{
nwCfg: &cni.NetworkConfig{},
args: &cniSkel.CmdArgs{
ContainerID: "testcontainerid",
Netns: "testnetns",
IfName: "testifname",
},
options: map[string]interface{}{},
},
wantApipaResult: network.InterfaceInfo{
IPConfigs: []*network.IPConfig{
{
Address: net.IPNet{
IP: net.ParseIP("169.254.128.10"),
Mask: net.CIDRMask(17, 32),
},
Gateway: net.ParseIP("169.254.128.1"),
},
},
NICType: cns.ApipaNIC,
SkipDefaultRoutes: true,
NetworkContainerID: "test-nc-id",
AllowHostToNCCommunication: true,
AllowNCToHostCommunication: false,
},
wantErr: false,
},
{
name: "Test CNI add with Frontend Nic + ApipaNIC",
fields: fields{
podName: testPodInfo.PodName,
podNamespace: testPodInfo.PodNamespace,
cnsClient: &MockCNSClient{
require: require,
requestIPs: requestIPsHandler{
ipconfigArgument: cns.IPConfigsRequest{
PodInterfaceID: "testcont-testifname",
InfraContainerID: "testcontainerid",
OrchestratorContext: marshallPodInfo(testPodInfo),
},
result: &cns.IPConfigsResponse{
PodIPInfo: []cns.PodIpInfo{
{
PodIPConfig: cns.IPSubnet{
IPAddress: "10.0.1.10",
PrefixLength: 24,
},
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
IPSubnet: cns.IPSubnet{
IPAddress: "10.0.1.0",
PrefixLength: 24,
},
GatewayIPAddress: "10.0.1.1",
},
HostPrimaryIPInfo: cns.HostIPInfo{
Gateway: "10.0.0.1",
PrimaryIP: "10.0.0.1",
Subnet: "10.0.0.0/24",
},
MacAddress: "bc:9a:78:56:34:12",
NICType: cns.NodeNetworkInterfaceFrontendNIC,
},
{
PodIPConfig: cns.IPSubnet{
IPAddress: "169.254.5.50",
PrefixLength: 16,
},
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
GatewayIPAddress: "169.254.5.1",
},
NICType: cns.ApipaNIC,
NetworkContainerID: "mixed-nc-id",
AllowHostToNCCommunication: true,
AllowNCToHostCommunication: false,
},
},
Response: cns.Response{
ReturnCode: 0,
Message: "",
},
},
err: nil,
},
},
},
args: args{
nwCfg: &cni.NetworkConfig{},
args: &cniSkel.CmdArgs{
ContainerID: "testcontainerid",
Netns: "testnetns",
IfName: "testifname",
},
options: map[string]interface{}{},
},
wantApipaResult: network.InterfaceInfo{
IPConfigs: []*network.IPConfig{
{
Address: net.IPNet{
IP: net.ParseIP("169.254.5.50"),
Mask: net.CIDRMask(16, 32),
},
Gateway: net.ParseIP("169.254.5.1"),
},
},
NICType: cns.ApipaNIC,
SkipDefaultRoutes: true,
NetworkContainerID: "mixed-nc-id",
AllowHostToNCCommunication: true,
AllowNCToHostCommunication: false,
},
wantErr: false,
},
{
name: "Test CNI add with ApipaNIC fails when GetIPNet fails",
fields: fields{
podName: testPodInfo.PodName,
podNamespace: testPodInfo.PodNamespace,
cnsClient: &MockCNSClient{
require: require,
requestIPs: requestIPsHandler{
ipconfigArgument: cns.IPConfigsRequest{
PodInterfaceID: "testcont-testifname",
InfraContainerID: "testcontainerid",
OrchestratorContext: marshallPodInfo(testPodInfo),
},
result: &cns.IPConfigsResponse{
PodIPInfo: []cns.PodIpInfo{
{
PodIPConfig: cns.IPSubnet{
IPAddress: "invalid-ip-address",
PrefixLength: 16,
},
NetworkContainerPrimaryIPConfig: cns.IPConfiguration{
GatewayIPAddress: "169.254.1.1",
},
NICType: cns.ApipaNIC,
NetworkContainerID: "failed-nc-id",
AllowHostToNCCommunication: false,
AllowNCToHostCommunication: false,
},
},
Response: cns.Response{
ReturnCode: 0,
Message: "",
},
},
err: nil,
},
},
},
args: args{
nwCfg: &cni.NetworkConfig{},
args: &cniSkel.CmdArgs{
ContainerID: "testcontainerid",
Netns: "testnetns",
IfName: "testifname",
},
options: map[string]interface{}{},
},
wantErr: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(_ *testing.T) {
invoker := &CNSIPAMInvoker{
podName: tt.fields.podName,
podNamespace: tt.fields.podNamespace,
cnsClient: tt.fields.cnsClient,
}
if tt.fields.ipamMode != "" {
invoker.ipamMode = tt.fields.ipamMode
}

ipamAddResult, err := invoker.Add(IPAMAddConfig{
nwCfg: tt.args.nwCfg,
args: tt.args.args,
options: tt.args.options,
})

if tt.wantErr {
require.Error(err)
return
}

require.NoError(err)

// Find the ApipaNIC interface in the result
var apipaInterfaceFound bool
var actualApipaResult network.InterfaceInfo

for _, ifInfo := range ipamAddResult.interfaceInfo {
if ifInfo.NICType == cns.ApipaNIC {
apipaInterfaceFound = true
actualApipaResult = ifInfo
break
}
}

require.True(apipaInterfaceFound, "ApipaNIC interface should be found in the result")

// Verify the ApipaNIC interface info
// Lines around 2586-2590 should be:
require.Equal(string(tt.wantApipaResult.NICType), string(actualApipaResult.NICType), "NICType should match expected value")
require.Equal(tt.wantApipaResult.SkipDefaultRoutes, actualApipaResult.SkipDefaultRoutes)
require.Equal(tt.wantApipaResult.NetworkContainerID, actualApipaResult.NetworkContainerID)
require.Equal(tt.wantApipaResult.AllowHostToNCCommunication, actualApipaResult.AllowHostToNCCommunication)
require.Equal(tt.wantApipaResult.AllowNCToHostCommunication, actualApipaResult.AllowNCToHostCommunication)

// Verify IP configs
require.Len(actualApipaResult.IPConfigs, 1, "Should have exactly one IP config for ApipaNIC")
actualIPConfig := actualApipaResult.IPConfigs[0]
expectedIPConfig := tt.wantApipaResult.IPConfigs[0]

require.True(actualIPConfig.Address.IP.Equal(expectedIPConfig.Address.IP),
"IP addresses should match: expected %s, got %s",
expectedIPConfig.Address.IP, actualIPConfig.Address.IP)
require.Equal(expectedIPConfig.Address.Mask, actualIPConfig.Address.Mask,
"IP masks should match")

if expectedIPConfig.Gateway != nil {
require.NotNil(actualIPConfig.Gateway, "Gateway should not be nil")
require.True(actualIPConfig.Gateway.Equal(expectedIPConfig.Gateway),
"Gateway IPs should match: expected %s, got %s",
expectedIPConfig.Gateway, actualIPConfig.Gateway)
} else {
require.Nil(actualIPConfig.Gateway, "Gateway should be nil")
}
})
}
}
Loading
Loading