Skip to content

Commit 55f5522

Browse files
committed
adding changes to CNS to allow for dualstack NCs
1 parent 7e6e30d commit 55f5522

File tree

6 files changed

+545
-298
lines changed

6 files changed

+545
-298
lines changed

cns/NetworkContainerContract.go

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ type CreateNetworkContainerRequest struct {
127127
EndpointPolicies []NetworkContainerRequestPolicies
128128
NCStatus v1alpha.NCStatus
129129
NetworkInterfaceInfo NetworkInterfaceInfo //nolint // introducing new field for backendnic, to be used later by cni code
130+
IPFamilies map[IPFamily]struct{}
130131
}
131132

132133
func (req *CreateNetworkContainerRequest) Validate() error {
@@ -742,3 +743,11 @@ type NodeRegisterRequest struct {
742743
NumCores int
743744
NmAgentSupportedApis []string
744745
}
746+
747+
// IPFamily - Enum for determining IPFamily when retrieving IPs from network containers
748+
type IPFamily string
749+
750+
const (
751+
IPv4Family IPFamily = "ipv4"
752+
IPv6Family IPFamily = "ipv6"
753+
)

cns/kubecontroller/nodenetworkconfig/conversion_linux.go

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package nodenetworkconfig
22

33
import (
4+
"fmt"
45
"net/netip"
56
"strconv"
67

@@ -15,6 +16,7 @@ import (
1516
//nolint:gocritic //ignore hugeparam
1617
func createNCRequestFromStaticNCHelper(nc v1alpha.NetworkContainer, primaryIPPrefix netip.Prefix, subnet cns.IPSubnet) (*cns.CreateNetworkContainerRequest, error) {
1718
secondaryIPConfigs := map[string]cns.SecondaryIPConfig{}
19+
ipFamilies := map[cns.IPFamily]struct{}{}
1820

1921
// iterate through all IP addresses in the subnet described by primaryPrefix and
2022
// add them to the request as secondary IPConfigs.
@@ -24,11 +26,17 @@ func createNCRequestFromStaticNCHelper(nc v1alpha.NetworkContainer, primaryIPPre
2426
NCVersion: int(nc.Version),
2527
}
2628
}
29+
// adds the IPFamily of the primary CIDR to the set
30+
if primaryIPPrefix.Addr().Is4() {
31+
ipFamilies[cns.IPv4Family] = struct{}{}
32+
} else {
33+
ipFamilies[cns.IPv6Family] = struct{}{}
34+
}
2735

2836
// Add IPs from CIDR block to the secondary IPConfigs
2937
if nc.Type == v1alpha.VNETBlock {
30-
3138
for _, ipAssignment := range nc.IPAssignments {
39+
// Here we would need to check all other assigned CIDR Blocks that aren't the primary.
3240
cidrPrefix, err := netip.ParsePrefix(ipAssignment.IP)
3341
if err != nil {
3442
return nil, errors.Wrapf(err, "invalid CIDR block: %s", ipAssignment.IP)
@@ -42,9 +50,17 @@ func createNCRequestFromStaticNCHelper(nc v1alpha.NetworkContainer, primaryIPPre
4250
NCVersion: int(nc.Version),
4351
}
4452
}
53+
// adds the IPFamily of the secondary CIDR to the set
54+
if cidrPrefix.Addr().Is4() {
55+
ipFamilies[cns.IPv4Family] = struct{}{}
56+
} else {
57+
ipFamilies[cns.IPv6Family] = struct{}{}
58+
}
4559
}
4660
}
4761

62+
fmt.Printf("IPFamilies found on NC %+v are %+v", nc.ID, ipFamilies)
63+
4864
return &cns.CreateNetworkContainerRequest{
4965
HostPrimaryIP: nc.NodeIP,
5066
SecondaryIPConfigs: secondaryIPConfigs,
@@ -55,6 +71,7 @@ func createNCRequestFromStaticNCHelper(nc v1alpha.NetworkContainer, primaryIPPre
5571
IPSubnet: subnet,
5672
GatewayIPAddress: nc.DefaultGateway,
5773
},
58-
NCStatus: nc.Status,
74+
NCStatus: nc.Status,
75+
IPFamilies: ipFamilies,
5976
}, nil
6077
}

cns/restserver/internalapi_test.go

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"fmt"
99
"math/rand"
1010
"net"
11+
"net/netip"
1112
"os"
1213
"reflect"
1314
"strconv"
@@ -914,11 +915,22 @@ func generateNetworkContainerRequest(secondaryIps map[string]cns.SecondaryIPConf
914915
ipSubnet.PrefixLength = subnetPrfixLength
915916
ipConfig.IPSubnet = ipSubnet
916917

918+
ipFamilies := map[cns.IPFamily]struct{}{}
919+
for _, secIPConfig := range secondaryIps {
920+
IP, _ := netip.ParseAddr(secIPConfig.IPAddress)
921+
if IP.Is4() {
922+
ipFamilies[cns.IPv4Family] = struct{}{}
923+
} else {
924+
ipFamilies[cns.IPv6Family] = struct{}{}
925+
}
926+
}
927+
917928
req := cns.CreateNetworkContainerRequest{
918929
NetworkContainerType: dockerContainerType,
919930
NetworkContainerid: ncID,
920931
IPConfiguration: ipConfig,
921932
Version: ncVersion,
933+
IPFamilies: ipFamilies,
922934
}
923935

924936
ncVersionInInt, _ := strconv.Atoi(ncVersion)

cns/restserver/ipam.go

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ var (
2525
ErrStoreEmpty = errors.New("empty endpoint state store")
2626
ErrParsePodIPFailed = errors.New("failed to parse pod's ip")
2727
ErrNoNCs = errors.New("no NCs found in the CNS internal state")
28+
ErrNoIPFamilies = errors.New("No IP Families found on NCs")
2829
ErrOptManageEndpointState = errors.New("CNS is not set to manage the endpoint state")
2930
ErrEndpointStateNotFound = errors.New("endpoint state could not be found in the statefile")
3031
ErrGetAllNCResponseEmpty = errors.New("failed to get NC responses from statefile")
@@ -989,44 +990,69 @@ func (service *HTTPRestService) AssignDesiredIPConfigs(podInfo cns.PodInfo, desi
989990
// Assigns an available IP from each NC on the NNC. If there is one NC then we expect to only have one IP return
990991
// In the case of dualstack we would expect to have one IPv6 from one NC and one IPv4 from a second NC
991992
func (service *HTTPRestService) AssignAvailableIPConfigs(podInfo cns.PodInfo) ([]cns.PodIpInfo, error) {
992-
// Gets the number of NCs which will determine the number of IPs given to a pod
993-
numOfNCs := len(service.state.ContainerStatus)
994-
// if there are no NCs on the NNC there will be no IPs in the pool so return error
995-
if numOfNCs == 0 {
993+
// Map used to get the number of IPFamilies across all NCs
994+
ipFamilies := map[cns.IPFamily]struct{}{}
995+
996+
// checks to make sure we have at least one NC
997+
if len(service.state.ContainerStatus) == 0 {
996998
return nil, ErrNoNCs
997999
}
1000+
1001+
// Gets the IPFamilies from all NCs and stores them in a map. This will be ued to determine the amount of IPs to return
1002+
for ncID := range service.state.ContainerStatus {
1003+
for ipFamily := range service.state.ContainerStatus[ncID].CreateNetworkContainerRequest.IPFamilies {
1004+
ipFamilies[ipFamily] = struct{}{}
1005+
}
1006+
}
1007+
1008+
// Makes sure we have at least one IPFamily across all NCs
1009+
numOfIPFamilies := len(ipFamilies)
1010+
if numOfIPFamilies == 0 {
1011+
return nil, ErrNoIPFamilies
1012+
}
1013+
9981014
service.Lock()
9991015
defer service.Unlock()
10001016
// Creates a slice of PodIpInfo with the size as number of NCs to hold the result for assigned IP configs
1001-
podIPInfo := make([]cns.PodIpInfo, numOfNCs)
1017+
podIPInfo := make([]cns.PodIpInfo, numOfIPFamilies)
10021018
// This map is used to store whether or not we have found an available IP from an NC when looping through the pool
1003-
ipsToAssign := make(map[string]cns.IPConfigurationStatus)
1019+
ipsToAssign := make(map[cns.IPFamily]cns.IPConfigurationStatus)
10041020

10051021
// Searches for available IPs in the pool
10061022
for _, ipState := range service.PodIPConfigState {
1007-
// check if an IP from this NC is already set side for assignment.
1008-
if _, ncAlreadyMarkedForAssignment := ipsToAssign[ipState.NCID]; ncAlreadyMarkedForAssignment {
1023+
// get the IPFamily of the current ipState
1024+
var ipStateFamily cns.IPFamily
1025+
if net.ParseIP(ipState.IPAddress).To4() != nil {
1026+
ipStateFamily = cns.IPv4Family
1027+
} else {
1028+
ipStateFamily = cns.IPv6Family
1029+
}
1030+
1031+
// check if the IP with the same family type exists already
1032+
if _, IPFamilyAlreadyMarkedForAssignment := ipsToAssign[ipStateFamily]; IPFamilyAlreadyMarkedForAssignment {
10091033
continue
10101034
}
10111035
// Checks if the current IP is available
10121036
if ipState.GetState() != types.Available {
10131037
continue
10141038
}
1015-
ipsToAssign[ipState.NCID] = ipState
1016-
// Once one IP per container is found break out of the loop and stop searching
1017-
if len(ipsToAssign) == numOfNCs {
1039+
ipsToAssign[ipStateFamily] = ipState
1040+
// Once one IP per family is found break out of the loop and stop searching
1041+
if len(ipsToAssign) == numOfIPFamilies {
10181042
break
10191043
}
10201044
}
10211045

1022-
// Checks to make sure we found one IP for each NC
1023-
if len(ipsToAssign) != numOfNCs {
1046+
// Checks to make sure we found one IP for each IPFamily
1047+
if len(ipsToAssign) != numOfIPFamilies {
10241048
for ncID := range service.state.ContainerStatus {
1025-
if _, found := ipsToAssign[ncID]; found {
1026-
continue
1049+
for ipFamily := range service.state.ContainerStatus[ncID].CreateNetworkContainerRequest.IPFamilies {
1050+
if _, found := ipsToAssign[ipFamily]; found {
1051+
continue
1052+
}
1053+
return podIPInfo, errors.Errorf("not enough IPs available of type %s for %s, waiting on Azure CNS to allocate more with NC Status: %s",
1054+
ipFamily, ncID, string(service.state.ContainerStatus[ncID].CreateNetworkContainerRequest.NCStatus))
10271055
}
1028-
return podIPInfo, errors.Errorf("not enough IPs available for %s, waiting on Azure CNS to allocate more with NC Status: %s",
1029-
ncID, string(service.state.ContainerStatus[ncID].CreateNetworkContainerRequest.NCStatus))
10301056
}
10311057
}
10321058

0 commit comments

Comments
 (0)