diff --git a/azure-ipam/ipam.go b/azure-ipam/ipam.go index e71782ebd8..0f83414b98 100644 --- a/azure-ipam/ipam.go +++ b/azure-ipam/ipam.go @@ -115,7 +115,7 @@ func (p *IPAMPlugin) CmdAdd(args *cniSkel.CmdArgs) error { p.logger.Debug("Received CNS IP config response", zap.Any("response", resp)) // Get Pod IP and gateway IP from ip config response - podIPNet, err := ipconfig.ProcessIPConfigsResp(resp) + podIPNet, gatewayIP, err := ipconfig.ProcessIPConfigsResp(resp) if err != nil { p.logger.Error("Failed to interpret CNS IPConfigResponse", zap.Error(err), zap.Any("response", resp)) return cniTypes.NewError(ErrProcessIPConfigResponse, err.Error(), "failed to interpret CNS IPConfigResponse") @@ -136,9 +136,33 @@ func (p *IPAMPlugin) CmdAdd(args *cniSkel.CmdArgs) error { Mask: net.CIDRMask(ipNet.Bits(), 128), // nolint } } + ipConfig.Gateway = (*gatewayIP)[i] cniResult.IPs[i] = ipConfig } + cniResult.Interfaces = []*types100.Interface{} + seenInterfaces := map[string]bool{} + + for _, podIPInfo := range resp.PodIPInfo { + // Skip if interface already seen + // This is to avoid duplicate interfaces in the result + // Deduplication is necessary because there is one podIPInfo entry for each IP family(IPv4 and IPv6), and both may point to the same interface or if multiple interfaces are assigned to the same pod + if podIPInfo.MacAddress == "" || seenInterfaces[podIPInfo.MacAddress] { + continue + } + + infMac, err := net.ParseMAC(podIPInfo.MacAddress) + if err != nil { + p.logger.Error("Failed to parse interface MAC address", zap.Error(err), zap.String("macAddress", podIPInfo.MacAddress)) + return cniTypes.NewError(cniTypes.ErrUnsupportedField, err.Error(), "failed to parse interface MAC address") + } + + cniResult.Interfaces = append(cniResult.Interfaces, &types100.Interface{ + Mac: infMac.String(), + }) + seenInterfaces[podIPInfo.MacAddress] = true + } + // Get versioned result versionedCniResult, err := cniResult.GetAsVersion(nwCfg.CNIVersion) if err != nil { diff --git a/azure-ipam/ipconfig/ipconfig.go b/azure-ipam/ipconfig/ipconfig.go index 9b2ecc97f4..b41d0cd408 100644 --- a/azure-ipam/ipconfig/ipconfig.go +++ b/azure-ipam/ipconfig/ipconfig.go @@ -3,9 +3,11 @@ package ipconfig import ( "encoding/json" "fmt" + "net" "net/netip" "github.com/Azure/azure-container-networking/cns" + "github.com/Azure/azure-container-networking/cns/logger" cniSkel "github.com/containernetworking/cni/pkg/skel" cniTypes "github.com/containernetworking/cni/pkg/types" "github.com/pkg/errors" @@ -63,10 +65,13 @@ func CreateIPConfigsReq(args *cniSkel.CmdArgs) (cns.IPConfigsRequest, error) { return req, nil } -func ProcessIPConfigsResp(resp *cns.IPConfigsResponse) (*[]netip.Prefix, error) { +func ProcessIPConfigsResp(resp *cns.IPConfigsResponse) (*[]netip.Prefix, *[]net.IP, error) { podIPNets := make([]netip.Prefix, len(resp.PodIPInfo)) + gatewaysIPs := make([]net.IP, len(resp.PodIPInfo)) for i := range resp.PodIPInfo { + var gatewayIP net.IP + podCIDR := fmt.Sprintf( "%s/%d", resp.PodIPInfo[i].PodIPConfig.IPAddress, @@ -74,12 +79,24 @@ func ProcessIPConfigsResp(resp *cns.IPConfigsResponse) (*[]netip.Prefix, error) ) podIPNet, err := netip.ParsePrefix(podCIDR) if err != nil { - return nil, errors.Wrapf(err, "cns returned invalid pod CIDR %q", podCIDR) + return nil, nil, errors.Wrapf(err, "cns returned invalid pod CIDR %q", podCIDR) } podIPNets[i] = podIPNet + + if podIPNet.Addr().Is4() { + gatewayIP = net.ParseIP(resp.PodIPInfo[i].NetworkContainerPrimaryIPConfig.GatewayIPAddress) + } else if podIPNet.Addr().Is6() { + gatewayIP = net.ParseIP(resp.PodIPInfo[i].NetworkContainerPrimaryIPConfig.GatewayIPv6Address) + } + + if gatewayIP != nil { + gatewaysIPs[i] = gatewayIP + } else { + logger.Printf("gatewayIP is nil or gateway ip parse failed for pod ip %s", resp.PodIPInfo[i].PodIPConfig.IPAddress) + } } - return &podIPNets, nil + return &podIPNets, &gatewaysIPs, nil } type k8sPodEnvArgs struct { diff --git a/cns/NetworkContainerContract.go b/cns/NetworkContainerContract.go index 5f64178a46..900bf7359f 100644 --- a/cns/NetworkContainerContract.go +++ b/cns/NetworkContainerContract.go @@ -389,9 +389,10 @@ type NetworkInterfaceInfo struct { // IPConfiguration contains details about ip config to provision in the VM. type IPConfiguration struct { - IPSubnet IPSubnet - DNSServers []string - GatewayIPAddress string + IPSubnet IPSubnet + DNSServers []string + GatewayIPAddress string + GatewayIPv6Address string } // SecondaryIPConfig contains IP info of SecondaryIP @@ -746,3 +747,11 @@ type NodeRegisterRequest struct { NumCores int NmAgentSupportedApis []string } + +// IPFamily - Enum for determining IPFamily when retrieving IPs from network containers +type IPFamily string + +const ( + IPv4 IPFamily = "ipv4" + IPv6 IPFamily = "ipv6" +) diff --git a/cns/kubecontroller/nodenetworkconfig/conversion_linux.go b/cns/kubecontroller/nodenetworkconfig/conversion_linux.go index c89d41646b..9820dfc042 100644 --- a/cns/kubecontroller/nodenetworkconfig/conversion_linux.go +++ b/cns/kubecontroller/nodenetworkconfig/conversion_linux.go @@ -16,12 +16,15 @@ import ( func createNCRequestFromStaticNCHelper(nc v1alpha.NetworkContainer, primaryIPPrefix netip.Prefix, subnet cns.IPSubnet) (*cns.CreateNetworkContainerRequest, error) { secondaryIPConfigs := map[string]cns.SecondaryIPConfig{} - // iterate through all IP addresses in the subnet described by primaryPrefix and - // add them to the request as secondary IPConfigs. - for addr := primaryIPPrefix.Masked().Addr(); primaryIPPrefix.Contains(addr); addr = addr.Next() { - secondaryIPConfigs[addr.String()] = cns.SecondaryIPConfig{ - IPAddress: addr.String(), - NCVersion: int(nc.Version), + // in the case of vnet prefix on swift v2 the primary IP is a /32 and should not be added to secondary IP configs + if !primaryIPPrefix.IsSingleIP() { + // iterate through all IP addresses in the subnet described by primaryPrefix and + // add them to the request as secondary IPConfigs. + for addr := primaryIPPrefix.Masked().Addr(); primaryIPPrefix.Contains(addr); addr = addr.Next() { + secondaryIPConfigs[addr.String()] = cns.SecondaryIPConfig{ + IPAddress: addr.String(), + NCVersion: int(nc.Version), + } } } @@ -52,9 +55,13 @@ func createNCRequestFromStaticNCHelper(nc v1alpha.NetworkContainer, primaryIPPre NetworkContainerType: cns.Docker, Version: strconv.FormatInt(nc.Version, 10), //nolint:gomnd // it's decimal IPConfiguration: cns.IPConfiguration{ - IPSubnet: subnet, - GatewayIPAddress: nc.DefaultGateway, + IPSubnet: subnet, + GatewayIPAddress: nc.DefaultGateway, + GatewayIPv6Address: nc.DefaultGatewayV6, }, NCStatus: nc.Status, + NetworkInterfaceInfo: cns.NetworkInterfaceInfo{ + MACAddress: nc.MacAddress, + }, }, nil } diff --git a/cns/restserver/internalapi_linux.go b/cns/restserver/internalapi_linux.go index ef30dabf03..6a819e5b69 100644 --- a/cns/restserver/internalapi_linux.go +++ b/cns/restserver/internalapi_linux.go @@ -68,6 +68,12 @@ func (service *HTTPRestService) programSNATRules(req *cns.CreateNetworkContainer // use any secondary ip + the nnc prefix length to get an iptables rule to allow dns and imds traffic from the pods for _, v := range req.SecondaryIPConfigs { + // check if the ip address is IPv4 + if net.ParseIP(v.IPAddress).To4() == nil { + // skip if the ip address is not IPv4 + continue + } + // put the ip address in standard cidr form (where we zero out the parts that are not relevant) _, podSubnet, _ := net.ParseCIDR(v.IPAddress + "/" + fmt.Sprintf("%d", req.IPConfiguration.IPSubnet.PrefixLength)) diff --git a/cns/restserver/ipam.go b/cns/restserver/ipam.go index 883d9aaef3..8764a8aa34 100644 --- a/cns/restserver/ipam.go +++ b/cns/restserver/ipam.go @@ -995,38 +995,85 @@ func (service *HTTPRestService) AssignAvailableIPConfigs(podInfo cns.PodInfo) ([ if numOfNCs == 0 { return nil, ErrNoNCs } + + // Map used to get the number of IPFamilies across all NCs + ncIPFamilies := map[cns.IPFamily]struct{}{} + // Gets the IPFamilies from all NCs and store them in a map. This will be used to determine the number of IPs to return + for ncID := range service.state.ContainerStatus { + if len(ncIPFamilies) == 2 { + break + } + + for _, secIPConfig := range service.state.ContainerStatus[ncID].CreateNetworkContainerRequest.SecondaryIPConfigs { + if len(ncIPFamilies) == 2 { + break + } + + ip := net.ParseIP(secIPConfig.IPAddress) + if ip == nil { + continue + } + + if ip.To4() != nil { + ncIPFamilies[cns.IPv4] = struct{}{} + } else { + ncIPFamilies[cns.IPv6] = struct{}{} + } + } + } + // Makes sure we have at least one IPFamily across all NCs + numOfIPFamilies := len(ncIPFamilies) + + numberOfIPs := numOfNCs + if numOfIPFamilies != 0 { + numberOfIPs = numOfIPFamilies + } + service.Lock() defer service.Unlock() // Creates a slice of PodIpInfo with the size as number of NCs to hold the result for assigned IP configs - podIPInfo := make([]cns.PodIpInfo, numOfNCs) + podIPInfo := make([]cns.PodIpInfo, numberOfIPs) // This map is used to store whether or not we have found an available IP from an NC when looping through the pool ipsToAssign := make(map[string]cns.IPConfigurationStatus) // Searches for available IPs in the pool for _, ipState := range service.PodIPConfigState { - // check if an IP from this NC is already set side for assignment. - if _, ncAlreadyMarkedForAssignment := ipsToAssign[ipState.NCID]; ncAlreadyMarkedForAssignment { + + // get the IPFamily of the current ipState + var ipStateFamily cns.IPFamily + if net.ParseIP(ipState.IPAddress).To4() != nil { + ipStateFamily = cns.IPv4 + } else { + ipStateFamily = cns.IPv6 + } + + key := generateAssignedIPKey(ipState.NCID, ipStateFamily) + + // check if the IP with the same family type exists already + if _, ncIPFamilyAlreadyMarkedForAssignment := ipsToAssign[key]; ncIPFamilyAlreadyMarkedForAssignment { continue } // Checks if the current IP is available if ipState.GetState() != types.Available { continue } - ipsToAssign[ipState.NCID] = ipState - // Once one IP per container is found break out of the loop and stop searching - if len(ipsToAssign) == numOfNCs { + ipsToAssign[key] = ipState + // Once numberOfIPs per container is found break out of the loop and stop searching + if len(ipsToAssign) == numberOfIPs { break } } - // Checks to make sure we found one IP for each NC - if len(ipsToAssign) != numOfNCs { + // Checks to make sure we found one IP for each NCxIPFamily + if len(ipsToAssign) != numberOfIPs { for ncID := range service.state.ContainerStatus { - if _, found := ipsToAssign[ncID]; found { - continue + for ipFamily := range ncIPFamilies { + if _, found := ipsToAssign[generateAssignedIPKey(ncID, ipFamily)]; found { + continue + } + return podIPInfo, errors.Errorf("not enough IPs available of type %s for %s, waiting on Azure CNS to allocate more with NC Status: %s", + ipFamily, ncID, string(service.state.ContainerStatus[ncID].CreateNetworkContainerRequest.NCStatus)) } - return podIPInfo, errors.Errorf("not enough IPs available for %s, waiting on Azure CNS to allocate more with NC Status: %s", - ncID, string(service.state.ContainerStatus[ncID].CreateNetworkContainerRequest.NCStatus)) } } @@ -1061,10 +1108,14 @@ func (service *HTTPRestService) AssignAvailableIPConfigs(podInfo cns.PodInfo) ([ return podIPInfo, fmt.Errorf("not enough IPs available, waiting on Azure CNS to allocate more") } - logger.Printf("[AssignDesiredIPConfigs] Successfully assigned IPs for pod %+v", podInfo) + logger.Printf("[AssignAvailableIPConfigs] Successfully assigned IPs for pod %+v", podInfo) return podIPInfo, nil } +func generateAssignedIPKey(ncID string, ipFamily cns.IPFamily) string { + return fmt.Sprintf("%s_%s", ncID, string(ipFamily)) +} + // If IPConfigs are already assigned to the pod, it returns that else it returns the available ipconfigs. func requestIPConfigsHelper(service *HTTPRestService, req cns.IPConfigsRequest) ([]cns.PodIpInfo, error) { // check if ipconfigs already assigned to this pod and return if exists or error diff --git a/cns/restserver/ipam_test.go b/cns/restserver/ipam_test.go index 534cbe7f3e..69bf0ec75a 100644 --- a/cns/restserver/ipam_test.go +++ b/cns/restserver/ipam_test.go @@ -176,36 +176,46 @@ func updatePnpIDMacAddressState(svc *HTTPRestService) { } } -// create an endpoint with only one IP -func TestEndpointStateReadAndWriteSingleNC(t *testing.T) { - ncStates := []ncState{ +func TestEndpointStateReadAndWrite(t *testing.T) { + testNcs := [][]ncState{ + // single stack NC { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, }, }, - } - EndpointStateReadAndWrite(t, ncStates) -} - -// create an endpoint with one IP from each NC -func TestEndpointStateReadAndWriteMultipleNCs(t *testing.T) { - ncStates := []ncState{ + // two NCs (one V4 and one V6) { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, + }, + { + ncID: testNCIDv6, + ips: []string{ + testIP1v6, + }, }, }, + // dualstack NC { - ncID: testNCIDv6, - ips: []string{ - testIP1v6, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP1v6, + }, }, }, } - EndpointStateReadAndWrite(t, ncStates) + for _, ncState := range testNcs { + EndpointStateReadAndWrite(t, ncState) + } } // Tests the creation of an endpoint using the NCs and IPs as input and then tests the deletion of that endpoint @@ -280,35 +290,46 @@ func EndpointStateReadAndWrite(t *testing.T, ncStates []ncState) { } // assign the available IP to the new pod -func TestIPAMGetAvailableIPConfigSingleNC(t *testing.T) { - ncStates := []ncState{ +func TestIPAMGetAvailableIPConfig(t *testing.T) { + testNcs := [][]ncState{ + // single stack NC { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, }, }, - } - IPAMGetAvailableIPConfig(t, ncStates) -} - -// assign one IP per NC to the pod -func TestIPAMGetAvailableIPConfigMultipleNCs(t *testing.T) { - nsStates := []ncState{ + // two NCs (one V4 and one V6) { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, + }, + { + ncID: testNCIDv6, + ips: []string{ + testIP1v6, + }, }, }, + // dualstack NC { - ncID: testNCIDv6, - ips: []string{ - testIP1v6, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP1v6, + }, }, }, } - IPAMGetAvailableIPConfig(t, nsStates) + for _, ncState := range testNcs { + IPAMGetAvailableIPConfig(t, ncState) + } } // Add one IP per NC to the pool and request those IPs @@ -357,37 +378,51 @@ func IPAMGetAvailableIPConfig(t *testing.T, ncStates []ncState) { } } -func TestIPAMGetNextAvailableIPConfigSingleNC(t *testing.T) { - ncStates := []ncState{ +func TestIPAMGetNextAvailableIPConfig(t *testing.T) { + testNcs := [][]ncState{ + // single stack NC { - ncID: testNCID, - ips: []string{ - testIP1, - testIP2, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP2, + }, }, }, - } - IPAMGetNextAvailableIPConfig(t, ncStates) -} - -func TestIPAMGetNextAvailableIPConfigMultipleNCs(t *testing.T) { - ncStates := []ncState{ + // two NCs (one V4 and one V6) { - ncID: testNCID, - ips: []string{ - testIP1, - testIP2, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP2, + }, + }, + { + ncID: testNCIDv6, + ips: []string{ + testIP1v6, + testIP2v6, + }, }, }, + // dualstack NC { - ncID: testNCIDv6, - ips: []string{ - testIP1v6, - testIP2v6, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP2, + testIP1v6, + testIP2v6, + }, }, }, } - IPAMGetNextAvailableIPConfig(t, ncStates) + for _, ncState := range testNcs { + IPAMGetNextAvailableIPConfig(t, ncState) + } } // First IP is already assigned to a pod, want second IP @@ -440,34 +475,46 @@ func IPAMGetNextAvailableIPConfig(t *testing.T, ncStates []ncState) { } } -func TestIPAMGetAlreadyAssignedIPConfigForSamePodSingleNC(t *testing.T) { - ncStates := []ncState{ +func TestIPAMGetAlreadyAssignedIPConfigForSamePod(t *testing.T) { + testNcs := [][]ncState{ + // single stack NC { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, }, }, - } - IPAMGetAlreadyAssignedIPConfigForSamePod(t, ncStates) -} - -func TestIPAMGetAlreadyAssignedIPConfigForSamePodMultipleNCs(t *testing.T) { - ncStates := []ncState{ + // two NCs (one V4 and one V6) { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, + }, + { + ncID: testNCIDv6, + ips: []string{ + testIP1v6, + }, }, }, + // dualstack NC { - ncID: testNCIDv6, - ips: []string{ - testIP1v6, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP1v6, + }, }, }, } - IPAMGetAlreadyAssignedIPConfigForSamePod(t, ncStates) + for _, ncState := range testNcs { + IPAMGetAlreadyAssignedIPConfigForSamePod(t, ncState) + } } func IPAMGetAlreadyAssignedIPConfigForSamePod(t *testing.T, ncStates []ncState) { @@ -515,37 +562,51 @@ func IPAMGetAlreadyAssignedIPConfigForSamePod(t *testing.T, ncStates []ncState) } } -func TestIPAMAttemptToRequestIPNotFoundInPoolSingleNC(t *testing.T) { - ncStates := []ncState{ +func TestIPAMAttemptToRequestIPNotFoundInPool(t *testing.T) { + testNcs := [][]ncState{ + // single stack NC { - ncID: testNCID, - ips: []string{ - testIP1, - testIP2, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP2, + }, }, }, - } - IPAMAttemptToRequestIPNotFoundInPool(t, ncStates) -} - -func TestIPAMAttemptToRequestIPNotFoundInPoolMultipleNCs(t *testing.T) { - ncStates := []ncState{ + // two NCs (one V4 and one V6) { - ncID: testNCID, - ips: []string{ - testIP1, - testIP2, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP2, + }, + }, + { + ncID: testNCIDv6, + ips: []string{ + testIP1v6, + testIP2v6, + }, }, }, + // dualstack NC { - ncID: testNCIDv6, - ips: []string{ - testIP1v6, - testIP2v6, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP2, + testIP1v6, + testIP2v6, + }, }, }, } - IPAMAttemptToRequestIPNotFoundInPool(t, ncStates) + for _, ncState := range testNcs { + IPAMAttemptToRequestIPNotFoundInPool(t, ncState) + } } func IPAMAttemptToRequestIPNotFoundInPool(t *testing.T, ncStates []ncState) { @@ -578,34 +639,46 @@ func IPAMAttemptToRequestIPNotFoundInPool(t *testing.T, ncStates []ncState) { } } -func TestIPAMGetDesiredIPConfigWithSpecfiedIPSingleNC(t *testing.T) { - ncStates := []ncState{ +func TestIPAMGetDesiredIPConfigWithSpecfiedIP(t *testing.T) { + testNcs := [][]ncState{ + // single stack NC { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, }, }, - } - IPAMGetDesiredIPConfigWithSpecfiedIP(t, ncStates) -} - -func TestIPAMGetDesiredIPConfigWithSpecfiedIPMultipleNCs(t *testing.T) { - ncStates := []ncState{ + // two NCs (one V4 and one V6) { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, + }, + { + ncID: testNCIDv6, + ips: []string{ + testIP1v6, + }, }, }, + // dualstack NC { - ncID: testNCIDv6, - ips: []string{ - testIP1v6, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP1v6, + }, }, }, } - IPAMGetDesiredIPConfigWithSpecfiedIP(t, ncStates) + for _, ncState := range testNcs { + IPAMGetDesiredIPConfigWithSpecfiedIP(t, ncState) + } } func IPAMGetDesiredIPConfigWithSpecfiedIP(t *testing.T, ncStates []ncState) { @@ -657,34 +730,46 @@ func IPAMGetDesiredIPConfigWithSpecfiedIP(t *testing.T, ncStates []ncState) { } } -func TestIPAMFailToGetDesiredIPConfigWithAlreadyAssignedSpecfiedIPSingleNC(t *testing.T) { - ncStates := []ncState{ +func TestIPAMFailToGetDesiredIPConfigWithAlreadyAssignedSpecfiedIP(t *testing.T) { + testNcs := [][]ncState{ + // single stack NC { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, }, }, - } - IPAMFailToGetDesiredIPConfigWithAlreadyAssignedSpecfiedIP(t, ncStates) -} - -func TestIPAMFailToGetDesiredIPConfigWithAlreadyAssignedSpecfiedIPMultipleNCs(t *testing.T) { - ncStates := []ncState{ + // two NCs (one V4 and one V6) { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, + }, + { + ncID: testNCIDv6, + ips: []string{ + testIP1v6, + }, }, }, + // dualstack NC { - ncID: testNCIDv6, - ips: []string{ - testIP1v6, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP1v6, + }, }, }, } - IPAMFailToGetDesiredIPConfigWithAlreadyAssignedSpecfiedIP(t, ncStates) + for _, ncState := range testNcs { + IPAMFailToGetDesiredIPConfigWithAlreadyAssignedSpecfiedIP(t, ncState) + } } func IPAMFailToGetDesiredIPConfigWithAlreadyAssignedSpecfiedIP(t *testing.T, ncStates []ncState) { @@ -718,37 +803,51 @@ func IPAMFailToGetDesiredIPConfigWithAlreadyAssignedSpecfiedIP(t *testing.T, ncS } } -func TestIPAMFailToGetIPWhenAllIPsAreAssignedSingleNC(t *testing.T) { - ncStates := []ncState{ +func TestIPAMFailToGetIPWhenAllIPsAreAssigned(t *testing.T) { + testNcs := [][]ncState{ + // single stack NC { - ncID: testNCID, - ips: []string{ - testIP1, - testIP2, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP2, + }, }, }, - } - IPAMFailToGetIPWhenAllIPsAreAssigned(t, ncStates) -} - -func TestIPAMFailToGetIPWhenAllIPsAreAssignedMultipleNCs(t *testing.T) { - ncStates := []ncState{ + // two NCs (one V4 and one V6) { - ncID: testNCID, - ips: []string{ - testIP1, - testIP2, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP2, + }, + }, + { + ncID: testNCIDv6, + ips: []string{ + testIP1v6, + testIP2v6, + }, }, }, + // dualstack NC { - ncID: testNCIDv6, - ips: []string{ - testIP1v6, - testIP2v6, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP2, + testIP1v6, + testIP2v6, + }, }, }, } - IPAMFailToGetIPWhenAllIPsAreAssigned(t, ncStates) + for _, ncState := range testNcs { + IPAMFailToGetIPWhenAllIPsAreAssigned(t, ncState) + } } func IPAMFailToGetIPWhenAllIPsAreAssigned(t *testing.T, ncStates []ncState) { @@ -778,34 +877,46 @@ func IPAMFailToGetIPWhenAllIPsAreAssigned(t *testing.T, ncStates []ncState) { } } -func TestIPAMRequestThenReleaseThenRequestAgainSingleNC(t *testing.T) { - ncStates := []ncState{ +func TestIPAMRequestThenReleaseThenRequestAgain(t *testing.T) { + testNcs := [][]ncState{ + // single stack NC { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, }, }, - } - IPAMRequestThenReleaseThenRequestAgain(t, ncStates) -} - -func TestIPAMRequestThenReleaseThenRequestAgainMultipleNCs(t *testing.T) { - ncStates := []ncState{ + // two NCs (one V4 and one V6) { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, + }, + { + ncID: testNCIDv6, + ips: []string{ + testIP1v6, + }, }, }, + // dualstack NC { - ncID: testNCIDv6, - ips: []string{ - testIP1v6, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP1v6, + }, }, }, } - IPAMRequestThenReleaseThenRequestAgain(t, ncStates) + for _, ncState := range testNcs { + IPAMRequestThenReleaseThenRequestAgain(t, ncState) + } } // 10.0.0.1 = PodInfo1 @@ -887,34 +998,46 @@ func IPAMRequestThenReleaseThenRequestAgain(t *testing.T, ncStates []ncState) { } } -func TestIPAMReleaseIPIdempotencySingleNC(t *testing.T) { - ncStates := []ncState{ +func TestIPAMReleaseIPIdempotency(t *testing.T) { + testNcs := [][]ncState{ + // single stack NC { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, }, }, - } - IPAMReleaseIPIdempotency(t, ncStates) -} - -func TestIPAMReleaseIPIdempotencyMultipleNCs(t *testing.T) { - ncStates := []ncState{ + // two NCs (one V4 and one V6) { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, + }, + { + ncID: testNCIDv6, + ips: []string{ + testIP1v6, + }, }, }, + // dualstack NC { - ncID: testNCIDv6, - ips: []string{ - testIP1v6, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP1v6, + }, }, }, } - IPAMReleaseIPIdempotency(t, ncStates) + for _, ncState := range testNcs { + IPAMReleaseIPIdempotency(t, ncState) + } } func IPAMReleaseIPIdempotency(t *testing.T, ncStates []ncState) { @@ -943,34 +1066,46 @@ func IPAMReleaseIPIdempotency(t *testing.T, ncStates []ncState) { } } -func TestIPAMAllocateIPIdempotencySingleNC(t *testing.T) { - ncStates := []ncState{ +func TestIPAMAllocateIPIdempotency(t *testing.T) { + testNcs := [][]ncState{ + // single stack NC { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, }, }, - } - IPAMAllocateIPIdempotency(t, ncStates) -} - -func TestIPAMAllocateIPIdempotencyMultipleNCs(t *testing.T) { - ncStates := []ncState{ + // two NCs (one V4 and one V6) { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, + }, + { + ncID: testNCIDv6, + ips: []string{ + testIP1v6, + }, }, }, + // dualstack NC { - ncID: testNCIDv6, - ips: []string{ - testIP1v6, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP1v6, + }, }, }, } - IPAMAllocateIPIdempotency(t, ncStates) + for _, ncState := range testNcs { + IPAMAllocateIPIdempotency(t, ncState) + } } func IPAMAllocateIPIdempotency(t *testing.T, ncStates []ncState) { @@ -992,40 +1127,56 @@ func IPAMAllocateIPIdempotency(t *testing.T, ncStates []ncState) { } } -func TestAvailableIPConfigsSingleNC(t *testing.T) { - ncStates := []ncState{ +func TestAvailableIPConfigs(t *testing.T) { + testNcs := [][]ncState{ + // single stack NC { - ncID: testNCID, - ips: []string{ - testIP1, - testIP2, - testIP3, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP2, + testIP3, + }, }, }, - } - AvailableIPConfigs(t, ncStates) -} - -func TestAvailableIPConfigsMultipleNCs(t *testing.T) { - ncStates := []ncState{ + // two NCs (one V4 and one V6) { - ncID: testNCID, - ips: []string{ - testIP1, - testIP2, - testIP3, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP2, + testIP3, + }, + }, + { + ncID: testNCIDv6, + ips: []string{ + testIP1v6, + testIP2v6, + testIP3v6, + }, }, }, + // dualstack NC { - ncID: testNCIDv6, - ips: []string{ - testIP1v6, - testIP2v6, - testIP3v6, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP2, + testIP3, + testIP1v6, + testIP2v6, + testIP3v6, + }, }, }, } - AvailableIPConfigs(t, ncStates) + for _, ncState := range testNcs { + AvailableIPConfigs(t, ncState) + } } func AvailableIPConfigs(t *testing.T, ncStates []ncState) { @@ -1111,34 +1262,46 @@ func validateIpState(t *testing.T, actualIps []cns.IPConfigurationStatus, expect } } -func TestIPAMMarkIPCountAsPendingSingleNC(t *testing.T) { - ncStates := []ncState{ +func TestIPAMMarkIPCountAsPending(t *testing.T) { + testNcs := [][]ncState{ + // single stack NC { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, }, }, - } - IPAMMarkIPCountAsPending(t, ncStates) -} - -func TestIPAMMarkIPCountAsPendingMultipleNCs(t *testing.T) { - ncStates := []ncState{ + // two NCs (one V4 and one V6) { - ncID: testNCID, - ips: []string{ - testIP1, + { + ncID: testNCID, + ips: []string{ + testIP1, + }, + }, + { + ncID: testNCIDv6, + ips: []string{ + testIP1v6, + }, }, }, + // dualstack NC { - ncID: testNCIDv6, - ips: []string{ - testIP1v6, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP1v6, + }, }, }, } - IPAMMarkIPCountAsPending(t, ncStates) + for _, ncState := range testNcs { + IPAMMarkIPCountAsPending(t, ncState) + } } func IPAMMarkIPCountAsPending(t *testing.T, ncStates []ncState) { @@ -1270,37 +1433,51 @@ func constructSecondaryIPConfigs(ipAddress, uuid string, ncVersion int, secondar secondaryIPConfigs[uuid] = secIPConfig } -func TestIPAMMarkExistingIPConfigAsPendingSingleNC(t *testing.T) { - ncStates := []ncState{ +func TestIPAMMarkExistingIPConfigAsPending(t *testing.T) { + testNcs := [][]ncState{ + // single stack NC { - ncID: testNCID, - ips: []string{ - testIP1, - testIP2, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP2, + }, }, }, - } - IPAMMarkExistingIPConfigAsPending(t, ncStates) -} - -func TestIPAMMarkExistingIPConfigAsPendingMultipleNCs(t *testing.T) { - ncStates := []ncState{ + // two NCs (one V4 and one V6) { - ncID: testNCID, - ips: []string{ - testIP1, - testIP2, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP2, + }, + }, + { + ncID: testNCIDv6, + ips: []string{ + testIP1v6, + testIP2v6, + }, }, }, + // dualstack NC { - ncID: testNCIDv6, - ips: []string{ - testIP1v6, - testIP2v6, + { + ncID: testNCID, + ips: []string{ + testIP1, + testIP2, + testIP1v6, + testIP2v6, + }, }, }, } - IPAMMarkExistingIPConfigAsPending(t, ncStates) + for _, ncState := range testNcs { + IPAMMarkExistingIPConfigAsPending(t, ncState) + } } func IPAMMarkExistingIPConfigAsPending(t *testing.T, ncStates []ncState) { @@ -1431,27 +1608,32 @@ func TestIPAMReleaseOneIPWhenExpectedToHaveTwo(t *testing.T) { func TestIPAMFailToRequestOneIPWhenExpectedToHaveTwo(t *testing.T) { svc := getTestService(cns.KubernetesCRD) - // set state as already assigned - testState := NewPodState(testIP1, ipIDs[0][0], testNCID, types.Available, 0) + testState1, _ := NewPodStateWithOrchestratorContext(testIP1, testIPID1, testNCID, types.Assigned, 24, 0, testPod1Info) + testState2 := NewPodState(testIP2, testIPID2, testNCID, types.Available, 0) ipconfigs := map[string]cns.IPConfigurationStatus{ - testState.ID: testState, + testState1.ID: testState1, + testState2.ID: testState2, + } + testState1v6, _ := NewPodStateWithOrchestratorContext(testIP1v6, testIPID1v6, testNCIDv6, types.Assigned, 120, 0, testPod1Info) + // setting v6 IPs to Assigned so there are none available + ipconfigsV6 := map[string]cns.IPConfigurationStatus{ + testState1v6.ID: testState1v6, } - emptyIpconfigs := map[string]cns.IPConfigurationStatus{} err := UpdatePodIPConfigState(t, svc, ipconfigs, testNCID) if err != nil { t.Fatalf("Expected to not fail adding IPs to state: %+v", err) } - err = UpdatePodIPConfigState(t, svc, emptyIpconfigs, testNCIDv6) + err = UpdatePodIPConfigState(t, svc, ipconfigsV6, testNCIDv6) if err != nil { t.Fatalf("Expected to not fail adding empty NC to state: %+v", err) } // request should expect 2 IPs but there is only 1 in the pool req := cns.IPConfigsRequest{} - b, _ := testPod1Info.OrchestratorContext() + b, _ := testPod2Info.OrchestratorContext() req.OrchestratorContext = b _, err = requestIPAddressAndGetState(t, req) diff --git a/cns/restserver/util.go b/cns/restserver/util.go index 43d1e1aef9..a84eb8cef0 100644 --- a/cns/restserver/util.go +++ b/cns/restserver/util.go @@ -845,6 +845,7 @@ func (service *HTTPRestService) populateIPConfigInfoUntransacted(ipConfigStatus podIPInfo.HostPrimaryIPInfo.PrimaryIP = primaryHostInterface.PrimaryIP podIPInfo.HostPrimaryIPInfo.Subnet = primaryHostInterface.Subnet podIPInfo.HostPrimaryIPInfo.Gateway = primaryHostInterface.Gateway + podIPInfo.MacAddress = ncStatus.CreateNetworkContainerRequest.NetworkInterfaceInfo.MACAddress podIPInfo.NICType = cns.InfraNIC return nil