Skip to content
Merged
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/network/cnsclient.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,5 @@ import (
type cnsclient interface {
RequestIPAddress(ctx context.Context, ipconfig cns.IPConfigRequest) (*cns.IPConfigResponse, error)
ReleaseIPAddress(ctx context.Context, ipconfig cns.IPConfigRequest) error
GetNetworkConfiguration(ctx context.Context, orchestratorContext []byte) (*cns.GetNetworkContainerResponse, error)
}
64 changes: 33 additions & 31 deletions cni/network/invoker_azure_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,19 +71,21 @@ func (m *mockDelegatePlugin) Errorf(format string, args ...interface{}) *cniType
}
}

func getCIDRNotationForAddress(t *testing.T, ipaddresswithcidr string) *net.IPNet {
func getCIDRNotationForAddress(ipaddresswithcidr string) *net.IPNet {
ip, ipnet, err := net.ParseCIDR(ipaddresswithcidr)
require.NoError(t, err)
if err != nil {
panic(fmt.Sprintf("failed to parse cidr with err: %v", err))
}
ipnet.IP = ip
return ipnet
}

func getResult(t *testing.T, ip string) []*cniTypesCurr.Result {
func getResult(ip string) []*cniTypesCurr.Result {
res := []*cniTypesCurr.Result{
{
IPs: []*cniTypesCurr.IPConfig{
{
Address: *getCIDRNotationForAddress(t, ip),
Address: *getCIDRNotationForAddress(ip),
},
},
},
Expand All @@ -101,16 +103,16 @@ type ipamStruct struct { //nolint:unused
QueryInterval string `json:"queryInterval,omitempty"`
}

func getNwInfo(t *testing.T, subnetv4, subnetv6 string) *network.NetworkInfo {
func getNwInfo(subnetv4, subnetv6 string) *network.NetworkInfo {
nwinfo := &network.NetworkInfo{}
if subnetv4 != "" {
nwinfo.Subnets = append(nwinfo.Subnets, network.SubnetInfo{
Prefix: *getCIDRNotationForAddress(t, subnetv4),
Prefix: *getCIDRNotationForAddress(subnetv4),
})
}
if subnetv6 != "" {
nwinfo.Subnets = append(nwinfo.Subnets, network.SubnetInfo{
Prefix: *getCIDRNotationForAddress(t, subnetv6),
Prefix: *getCIDRNotationForAddress(subnetv6),
})
}
return nwinfo
Expand Down Expand Up @@ -141,38 +143,38 @@ func TestAzureIPAMInvoker_Add(t *testing.T) {
fields: fields{
plugin: &mockDelegatePlugin{
add: add{
resultsIPv4: getResult(t, "10.0.0.1/24"),
resultsIPv4: getResult("10.0.0.1/24"),
},
del: del{},
},
nwInfo: getNwInfo(t, "10.0.0.0/24", ""),
nwInfo: getNwInfo("10.0.0.0/24", ""),
},
args: args{
nwCfg: &cni.NetworkConfig{},
subnetPrefix: getCIDRNotationForAddress(t, "10.0.0.0/24"),
subnetPrefix: getCIDRNotationForAddress("10.0.0.0/24"),
},
want: getResult(t, "10.0.0.1/24")[0],
want: getResult("10.0.0.1/24")[0],
wantErr: false,
},
{
name: "happy add ipv4+ipv6",
fields: fields{
plugin: &mockDelegatePlugin{
add: add{
resultsIPv4: getResult(t, "10.0.0.1/24"),
resultsIPv6: getResult(t, "2001:0db8:abcd:0015::0/64"),
resultsIPv4: getResult("10.0.0.1/24"),
resultsIPv6: getResult("2001:0db8:abcd:0015::0/64"),
},
},
nwInfo: getNwInfo(t, "10.0.0.0/24", "2001:db8:abcd:0012::0/64"),
nwInfo: getNwInfo("10.0.0.0/24", "2001:db8:abcd:0012::0/64"),
},
args: args{
nwCfg: &cni.NetworkConfig{
IPV6Mode: network.IPV6Nat,
},
subnetPrefix: getCIDRNotationForAddress(t, "10.0.0.0/24"),
subnetPrefix: getCIDRNotationForAddress("10.0.0.0/24"),
},
want: getResult(t, "10.0.0.1/24")[0],
want1: getResult(t, "2001:0db8:abcd:0015::0/64")[0],
want: getResult("10.0.0.1/24")[0],
want1: getResult("2001:0db8:abcd:0015::0/64")[0],
wantErr: false,
},
{
Expand All @@ -183,7 +185,7 @@ func TestAzureIPAMInvoker_Add(t *testing.T) {
errv4: errors.New("test error"), //nolint:goerr113
},
},
nwInfo: getNwInfo(t, "10.0.0.0/24", ""),
nwInfo: getNwInfo("10.0.0.0/24", ""),
},
args: args{
nwCfg: &cni.NetworkConfig{},
Expand All @@ -197,19 +199,19 @@ func TestAzureIPAMInvoker_Add(t *testing.T) {
fields: fields{
plugin: &mockDelegatePlugin{
add: add{
resultsIPv4: getResult(t, "10.0.0.1/24"),
resultsIPv4: getResult("10.0.0.1/24"),
errv6: errors.New("test v6 error"), //nolint:goerr113
},
},
nwInfo: getNwInfo(t, "10.0.0.0/24", ""),
nwInfo: getNwInfo("10.0.0.0/24", ""),
},
args: args{
nwCfg: &cni.NetworkConfig{
IPV6Mode: network.IPV6Nat,
},
subnetPrefix: getCIDRNotationForAddress(t, "10.0.0.0/24"),
subnetPrefix: getCIDRNotationForAddress("10.0.0.0/24"),
},
want: getResult(t, "10.0.0.1/24")[0],
want: getResult("10.0.0.1/24")[0],
want1: nil,
wantErr: true,
},
Expand Down Expand Up @@ -258,10 +260,10 @@ func TestAzureIPAMInvoker_Delete(t *testing.T) {
plugin: &mockDelegatePlugin{
del: del{},
},
nwInfo: getNwInfo(t, "10.0.0.0/24", ""),
nwInfo: getNwInfo("10.0.0.0/24", ""),
},
args: args{
address: getCIDRNotationForAddress(t, "10.0.0.4/24"),
address: getCIDRNotationForAddress("10.0.0.4/24"),
nwCfg: &cni.NetworkConfig{
Ipam: ipamStruct{
Address: "10.0.0.4",
Expand All @@ -275,10 +277,10 @@ func TestAzureIPAMInvoker_Delete(t *testing.T) {
plugin: &mockDelegatePlugin{
del: del{},
},
nwInfo: getNwInfo(t, "10.0.0.0/24", "2001:db8:abcd:0012::0/64"),
nwInfo: getNwInfo("10.0.0.0/24", "2001:db8:abcd:0012::0/64"),
},
args: args{
address: getCIDRNotationForAddress(t, "2001:db8:abcd:0015::0/64"),
address: getCIDRNotationForAddress("2001:db8:abcd:0015::0/64"),
nwCfg: &cni.NetworkConfig{
Ipam: ipamStruct{
Address: "2001:db8:abcd:0015::0/64",
Expand All @@ -294,7 +296,7 @@ func TestAzureIPAMInvoker_Delete(t *testing.T) {
err: errors.New("error when address is nil"), //nolint:goerr113
},
},
nwInfo: getNwInfo(t, "", "2001:db8:abcd:0012::0/64"),
nwInfo: getNwInfo("", "2001:db8:abcd:0012::0/64"),
},
args: args{
address: nil,
Expand All @@ -314,10 +316,10 @@ func TestAzureIPAMInvoker_Delete(t *testing.T) {
err: errors.New("error on v4 delete"), //nolint:goerr113
},
},
nwInfo: getNwInfo(t, "10.0.0.0/24", ""),
nwInfo: getNwInfo("10.0.0.0/24", ""),
},
args: args{
address: getCIDRNotationForAddress(t, "10.0.0.4/24"),
address: getCIDRNotationForAddress("10.0.0.4/24"),
nwCfg: &cni.NetworkConfig{
Ipam: ipamStruct{
Address: "10.0.0.4/24",
Expand All @@ -334,10 +336,10 @@ func TestAzureIPAMInvoker_Delete(t *testing.T) {
err: errors.New("error on v6 delete"), //nolint:goerr113
},
},
nwInfo: getNwInfo(t, "10.0.0.0/24", "2001:db8:abcd:0012::0/64"),
nwInfo: getNwInfo("10.0.0.0/24", "2001:db8:abcd:0012::0/64"),
},
args: args{
address: getCIDRNotationForAddress(t, "2001:db8:abcd:0015::0/64"),
address: getCIDRNotationForAddress("2001:db8:abcd:0015::0/64"),
nwCfg: &cni.NetworkConfig{
Ipam: ipamStruct{
Address: "10.0.0.4/24",
Expand Down
48 changes: 5 additions & 43 deletions cni/network/invoker_cns_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package network

import (
"context"
"encoding/json"
"errors"
"net"
"testing"
Expand All @@ -17,42 +15,6 @@ import (
"github.com/stretchr/testify/require"
)

// Handler structs
type requestIPAddressHandler struct {
// arguments
ipconfigArgument cns.IPConfigRequest

// results
result *cns.IPConfigResponse
err error
}

type releaseIPAddressHandler struct {
ipconfigArgument cns.IPConfigRequest
err error
}

type MockCNSClient struct {
require *require.Assertions
request requestIPAddressHandler
release releaseIPAddressHandler
}

func (c *MockCNSClient) RequestIPAddress(_ context.Context, ipconfig cns.IPConfigRequest) (*cns.IPConfigResponse, error) {
c.require.Exactly(c.request.ipconfigArgument, ipconfig)
return c.request.result, c.request.err
}

func (c *MockCNSClient) ReleaseIPAddress(_ context.Context, ipconfig cns.IPConfigRequest) error {
c.require.Exactly(c.release.ipconfigArgument, ipconfig)
return c.release.err
}

func marshallPodInfo(podInfo cns.KubernetesPodInfo) []byte {
orchestratorContext, _ := json.Marshal(podInfo)
return orchestratorContext
}

var testPodInfo cns.KubernetesPodInfo

func getTestIPConfigRequest() cns.IPConfigRequest {
Expand Down Expand Up @@ -129,14 +91,14 @@ func TestCNSIPAMInvoker_Add(t *testing.T) {
Netns: "testnetns",
IfName: "testifname",
},
hostSubnetPrefix: getCIDRNotationForAddress(t, "10.0.0.1/24"),
hostSubnetPrefix: getCIDRNotationForAddress("10.0.0.1/24"),
options: map[string]interface{}{},
},
want: &cniTypesCurr.Result{
IPs: []*cniTypesCurr.IPConfig{
{
Version: "4",
Address: *getCIDRNotationForAddress(t, "10.0.1.10/24"),
Address: *getCIDRNotationForAddress("10.0.1.10/24"),
Gateway: net.ParseIP("10.0.0.1"),
},
},
Expand Down Expand Up @@ -279,8 +241,8 @@ func Test_setHostOptions(t *testing.T) {
{
name: "test happy path",
args: args{
hostSubnetPrefix: getCIDRNotationForAddress(t, "10.0.1.0/24"),
ncSubnetPrefix: getCIDRNotationForAddress(t, "10.0.1.0/24"),
hostSubnetPrefix: getCIDRNotationForAddress("10.0.1.0/24"),
ncSubnetPrefix: getCIDRNotationForAddress("10.0.1.0/24"),
options: map[string]interface{}{},
info: &IPv4ResultInfo{
podIPAddress: "10.0.1.10",
Expand Down Expand Up @@ -313,7 +275,7 @@ func Test_setHostOptions(t *testing.T) {
},
network.RoutesKey: []network.RouteInfo{
{
Dst: *getCIDRNotationForAddress(t, "10.0.1.0/24"),
Dst: *getCIDRNotationForAddress("10.0.1.0/24"),
Gw: net.ParseIP("10.0.0.1"),
},
},
Expand Down
48 changes: 35 additions & 13 deletions cni/network/multitenancy.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import (

"github.com/Azure/azure-container-networking/cni"
"github.com/Azure/azure-container-networking/cns"
cnscli "github.com/Azure/azure-container-networking/cns/client"
"github.com/Azure/azure-container-networking/common"
"github.com/Azure/azure-container-networking/log"
"github.com/Azure/azure-container-networking/network"
Expand Down Expand Up @@ -46,12 +45,35 @@ type MultitenancyClient interface {
podName string,
podNamespace string,
ifName string) (*cniTypesCurr.Result, *cns.GetNetworkContainerResponse, net.IPNet, error)

Init(cnsclient cnsclient, netioshim netioshim)
}

type Multitenancy struct {
// cnsclient is used to communicate with CNS
cnsclient cnsclient

// netioshim is used to interact with networking syscalls
netioshim netioshim
}

type Multitenancy struct{}
type netioshim interface {
GetInterfaceSubnetWithSpecificIP(ipAddr string) *net.IPNet
}

type AzureNetIOShim struct{}

func (a AzureNetIOShim) GetInterfaceSubnetWithSpecificIP(ipAddr string) *net.IPNet {
return common.GetInterfaceSubnetWithSpecificIP(ipAddr)
}

var errNmaResponse = errors.New("nmagent request status code")

func (m *Multitenancy) Init(cnsclient cnsclient, netioshim netioshim) {
m.cnsclient = cnsclient
m.netioshim = netioshim
}

// DetermineSnatFeatureOnHost - Temporary function to determine whether we need to disable SNAT due to NMAgent support
func (m *Multitenancy) DetermineSnatFeatureOnHost(snatFile, nmAgentSupportedApisURL string) (snatForDNS, snatOnHost bool, err error) {
var (
Expand Down Expand Up @@ -171,36 +193,32 @@ func (m *Multitenancy) GetContainerNetworkConfiguration(
}

log.Printf("Podname without suffix %v", podNameWithoutSuffix)
return getContainerNetworkConfigurationInternal(ctx, nwCfg.CNSUrl, podNamespace, podNameWithoutSuffix, ifName)
return m.getContainerNetworkConfigurationInternal(ctx, nwCfg.CNSUrl, podNamespace, podNameWithoutSuffix, ifName)
}

func getContainerNetworkConfigurationInternal(
func (m *Multitenancy) getContainerNetworkConfigurationInternal(
ctx context.Context, cnsURL string, namespace string, podName string, ifName string) (*cniTypesCurr.Result, *cns.GetNetworkContainerResponse, net.IPNet, error) {
client, err := cnscli.New(cnsURL, cnscli.DefaultTimeout)
if err != nil {
log.Printf("Failed to get CNS client. Error: %v", err)
return nil, nil, net.IPNet{}, err
}

podInfo := cns.KubernetesPodInfo{
PodName: podName,
PodNamespace: namespace,
}

orchestratorContext, err := json.Marshal(podInfo)
if err != nil {
log.Printf("Marshalling KubernetesPodInfo failed with %v", err)
return nil, nil, net.IPNet{}, err
}

networkConfig, err := client.GetNetworkConfiguration(ctx, orchestratorContext)
networkConfig, err := m.cnsclient.GetNetworkConfiguration(ctx, orchestratorContext)
if err != nil {
log.Printf("GetNetworkConfiguration failed with %v", err)
return nil, nil, net.IPNet{}, err
}

log.Printf("Network config received from cns %+v", networkConfig)

subnetPrefix := common.GetInterfaceSubnetWithSpecificIp(networkConfig.PrimaryInterfaceIdentifier)
subnetPrefix := m.netioshim.GetInterfaceSubnetWithSpecificIP(networkConfig.PrimaryInterfaceIdentifier)
if subnetPrefix == nil {
errBuf := fmt.Sprintf("Interface not found for this ip %v", networkConfig.PrimaryInterfaceIdentifier)
log.Printf(errBuf)
Expand Down Expand Up @@ -265,7 +283,9 @@ func getInfraVnetIP(
nwCfg.Ipam.Subnet = ipNet.String()

log.Printf("call ipam to allocate ip from subnet %v", nwCfg.Ipam.Subnet)
azIpamResult, err := plugin.DelegateAdd(nwCfg.Ipam.Type, nwCfg)
subnetPrefix := &net.IPNet{}
options := make(map[string]interface{})
azIpamResult, _, err := plugin.ipamInvoker.Add(nwCfg, nil, subnetPrefix, options)
if err != nil {
err = plugin.Errorf("Failed to allocate address: %v", err)
return nil, err
Expand All @@ -289,7 +309,9 @@ func cleanupInfraVnetIP(
_, ipNet, _ := net.ParseCIDR(infraIPNet.String())
nwCfg.Ipam.Subnet = ipNet.String()
nwCfg.Ipam.Address = infraIPNet.IP.String()
plugin.DelegateDel(nwCfg.Ipam.Type, nwCfg)
if err := plugin.DelegateDel(nwCfg.Ipam.Type, nwCfg); err != nil {
log.Errorf("failed to cleanup infravnet ip with err %w", err)
}
}
}

Expand Down
Loading