Skip to content

Commit 3268674

Browse files
committed
Update Node pod CIDR in ipam controller for for Multi Pod CIDR support with Multi Networking
Depends on whether the node has label for subnet and Pod range: - if no label, node ipam controller keeps the old way to compare default GNP and Network to get the default CIDRs - if label exists, node ipam controller use the labels to get the default CIDRs also revert change in pull#607
1 parent 398b1a1 commit 3268674

File tree

5 files changed

+152
-30
lines changed

5 files changed

+152
-30
lines changed

pkg/controller/nodeipam/ipam/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ go_test(
7070
"//pkg/controller/nodeipam/ipam/cidrset",
7171
"//pkg/controller/nodeipam/ipam/test",
7272
"//pkg/controller/testutil",
73+
"//pkg/util/node",
7374
"//providers/gce",
7475
"//vendor/github.com/GoogleCloudPlatform/k8s-cloud-provider/pkg/cloud/meta",
7576
"//vendor/github.com/google/go-cmp/cmp",

pkg/controller/nodeipam/ipam/cloud_cidr_allocator.go

Lines changed: 38 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -342,39 +342,23 @@ func (ca *cloudCIDRAllocator) updateCIDRAllocation(nodeName string) error {
342342
}
343343
} else {
344344
// multi-networking enabled clusters
345-
cidrStrings, err = ca.performMultiNetworkCIDRAllocation(node, instance.NetworkInterfaces)
345+
hasNodeLabels, defaultSubnet, defaultPodRange := getNodeDefaultLabels(node)
346+
// if there's no node label get the cidrStrings with the old way by comparing the default Network and GNP
347+
cidrStrings, err = ca.performMultiNetworkCIDRAllocation(node, instance.NetworkInterfaces, hasNodeLabels)
346348
if err != nil {
347-
nodeutil.RecordNodeStatusChange(ca.recorder, node, "CIDRNotAvailable")
348-
return fmt.Errorf("failed to get cidr(s) from provider: %v", err)
349+
nodeutil.RecordNodeStatusChange(ca.recorder, node, "AnnotationsNotAvailable")
350+
return fmt.Errorf("failed to perform node annotations for multi-networking: %v", err)
349351
}
350-
}
351-
if len(cidrStrings) == 0 {
352-
nodeutil.RecordNodeStatusChange(ca.recorder, node, "CIDRNotAvailable")
353-
return fmt.Errorf("failed to allocate cidr: Node %v has no CIDRs", node.Name)
354-
}
355-
// Can have at most 2 ips (one for v4 and one for v6)
356-
if len(cidrStrings) > 2 {
357-
klog.InfoS("Got more than 2 ips, truncating to 2", "cidrStrings", cidrStrings)
358-
cidrStrings = cidrStrings[:2]
359-
}
360-
361-
cidrs, err := netutils.ParseCIDRs(cidrStrings)
362-
if err != nil {
363-
return fmt.Errorf("failed to parse strings %v as CIDRs: %v", cidrStrings, err)
364-
}
365-
if len(cidrs) > 1 {
366-
if dualStack, _ := netutils.IsDualStackCIDRs(cidrs); !dualStack {
367-
return fmt.Errorf("err: IPs are not dual stack, CIDRS: %v", cidrStrings)
352+
if hasNodeLabels {
353+
cidrStrings = ca.extractDefaultNwCIDRs(instance.NetworkInterfaces, defaultSubnet, defaultPodRange)
368354
}
369355
}
370356

371-
node.Spec.PodCIDR = cidrStrings[0]
372-
node.Spec.PodCIDRs = cidrStrings
373-
374-
err = ca.updateNodeCIDR(node, oldNode)
375-
if err != nil {
357+
// update Node.Spec.PodCIDR(s)
358+
if err = ca.updateNodePodCIDRWithCidrStrings(oldNode, node, cidrStrings); err != nil {
376359
return err
377360
}
361+
378362
if !reflect.DeepEqual(node.Annotations, oldNode.Annotations) {
379363
// retain old north interfaces annotation
380364
var oldNorthInterfacesAnnotation networkv1.NorthInterfacesAnnotation
@@ -409,6 +393,34 @@ func (ca *cloudCIDRAllocator) updateCIDRAllocation(nodeName string) error {
409393
return err
410394
}
411395

396+
// updateNodePodCIDRWithCidrStrings update the Node object with Spec.PodCIDR(s),
397+
// returns error if cidrStrings is not valid or fails to update the Node object
398+
func (ca *cloudCIDRAllocator) updateNodePodCIDRWithCidrStrings(oldNode *v1.Node, node *v1.Node, cidrStrings []string) error {
399+
if len(cidrStrings) == 0 {
400+
nodeutil.RecordNodeStatusChange(ca.recorder, node, "CIDRNotAvailable")
401+
return fmt.Errorf("failed to allocate cidr: Node %v has no CIDRs", node.Name)
402+
}
403+
// Can have at most 2 ips (one for v4 and one for v6)
404+
if len(cidrStrings) > 2 {
405+
klog.InfoS("Got more than 2 ips, truncating to 2", "cidrStrings", cidrStrings)
406+
cidrStrings = cidrStrings[:2]
407+
}
408+
409+
cidrs, err := netutils.ParseCIDRs(cidrStrings)
410+
if err != nil {
411+
return fmt.Errorf("failed to parse strings %v as CIDRs: %v", cidrStrings, err)
412+
}
413+
if len(cidrs) > 1 {
414+
if dualStack, _ := netutils.IsDualStackCIDRs(cidrs); !dualStack {
415+
return fmt.Errorf("err: IPs are not dual stack, CIDRS: %v", cidrStrings)
416+
}
417+
}
418+
node.Spec.PodCIDR = cidrStrings[0]
419+
node.Spec.PodCIDRs = cidrStrings
420+
421+
return ca.updateNodeCIDR(node, oldNode)
422+
}
423+
412424
func (ca *cloudCIDRAllocator) setNetworkCondition(node *v1.Node) {
413425
cond := v1.NodeCondition{
414426
Type: v1.NodeNetworkUnavailable,

pkg/controller/nodeipam/ipam/cloud_cidr_allocator_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import (
4040
clSetFake "k8s.io/cloud-provider-gcp/crd/client/network/clientset/versioned/fake"
4141
networkinformers "k8s.io/cloud-provider-gcp/crd/client/network/informers/externalversions"
4242
"k8s.io/cloud-provider-gcp/pkg/controller/testutil"
43+
utilnode "k8s.io/cloud-provider-gcp/pkg/util/node"
4344
"k8s.io/cloud-provider-gcp/providers/gce"
4445
metricsUtil "k8s.io/component-base/metrics/testutil"
4546
)
@@ -531,6 +532,64 @@ func TestUpdateCIDRAllocation(t *testing.T) {
531532
expectedUpdate: true,
532533
expectedMetrics: map[string]float64{},
533534
},
535+
{
536+
name: "[mn] default network only, get PodCIDR with node labels",
537+
networks: []*networkv1.Network{
538+
network(networkv1.DefaultPodNetworkName, defaultGKENetworkParamsName, false),
539+
},
540+
gkeNwParams: []*networkv1.GKENetworkParamSet{
541+
gkeNetworkParams(defaultGKENetworkParamsName, defaultVPCName, defaultVPCSubnetName, nil),
542+
},
543+
fakeNodeHandler: &testutil.FakeNodeHandler{
544+
Existing: []*v1.Node{
545+
{
546+
ObjectMeta: metav1.ObjectMeta{
547+
Name: "test",
548+
Labels: map[string]string{
549+
utilnode.NodePoolSubnetLabelPrefix: "default",
550+
utilnode.NodePoolPodRangeLabelPrefix: defaultSecondaryRangeA,
551+
},
552+
},
553+
Spec: v1.NodeSpec{
554+
ProviderID: "gce://test-project/us-central1-b/test",
555+
},
556+
Status: v1.NodeStatus{
557+
Capacity: v1.ResourceList{},
558+
},
559+
},
560+
},
561+
Clientset: fake.NewSimpleClientset(),
562+
},
563+
gceInstance: []*compute.Instance{
564+
{
565+
Name: "test",
566+
NetworkInterfaces: []*compute.NetworkInterface{
567+
interfaces(defaultVPCName, defaultVPCSubnetName, "80.1.172.1", []*compute.AliasIpRange{
568+
{IpCidrRange: "192.168.1.0/24", SubnetworkRangeName: defaultSecondaryRangeA},
569+
{IpCidrRange: "10.11.1.0/24", SubnetworkRangeName: defaultSecondaryRangeB},
570+
}),
571+
},
572+
},
573+
},
574+
nodeChanges: func(node *v1.Node) {
575+
node.Spec.PodCIDR = "192.168.1.0/24"
576+
node.Spec.PodCIDRs = []string{"192.168.1.0/24"}
577+
node.Status.Conditions = []v1.NodeCondition{
578+
{
579+
Type: "NetworkUnavailable",
580+
Status: "False",
581+
Reason: "RouteCreated",
582+
Message: "NodeController create implicit route",
583+
},
584+
}
585+
node.Annotations = map[string]string{
586+
networkv1.NorthInterfacesAnnotationKey: "[]",
587+
networkv1.MultiNetworkAnnotationKey: "[]",
588+
}
589+
},
590+
expectedUpdate: true,
591+
expectedMetrics: map[string]float64{},
592+
},
534593
{
535594
name: "[mn] one additional network along with default network",
536595
networks: []*networkv1.Network{

pkg/controller/nodeipam/ipam/multinetwork_cloud_cidr_allocator.go

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,18 +11,19 @@ import (
1111
"k8s.io/apimachinery/pkg/api/resource"
1212
"k8s.io/apimachinery/pkg/labels"
1313
networkv1 "k8s.io/cloud-provider-gcp/crd/apis/network/v1"
14+
utilnode "k8s.io/cloud-provider-gcp/pkg/util/node"
1415
"k8s.io/klog/v2"
1516
netutils "k8s.io/utils/net"
1617
)
1718

1819
// performMultiNetworkCIDRAllocation receives the existing Node object and its
1920
// GCE interfaces, and is updated with the corresponding annotations for
2021
// MultiNetwork and NorthInterfaces and the capacity for the additional networks.
21-
// It also returns calculated cidrs for default Network.
22+
// It also returns calculated cidrs for default Network if there're no node labels.
2223
//
2324
// NorthInterfacesAnnotationKey is modified on Network Ready condition changes.
2425
// MultiNetworkAnnotationKey is modified on Node's NodeNetworkAnnotationKey changes.
25-
func (ca *cloudCIDRAllocator) performMultiNetworkCIDRAllocation(node *v1.Node, interfaces []*compute.NetworkInterface) (defaultNwCIDRs []string, err error) {
26+
func (ca *cloudCIDRAllocator) performMultiNetworkCIDRAllocation(node *v1.Node, interfaces []*compute.NetworkInterface, hasNodeLabels bool) (defaultNwCIDRs []string, err error) {
2627
northInterfaces := networkv1.NorthInterfacesAnnotation{}
2728
additionalNodeNetworks := networkv1.MultiNetworkAnnotation{}
2829

@@ -96,13 +97,16 @@ func (ca *cloudCIDRAllocator) performMultiNetworkCIDRAllocation(node *v1.Node, i
9697
}
9798
klog.V(2).InfoS("found an allocatable secondary range for the interface on network", "nodeName", node.Name, "networkName", network.Name)
9899
processedNetworks[network.Name] = struct{}{}
99-
if networkv1.IsDefaultNetwork(network.Name) {
100+
// for defaultNwCIDRs, if there're no NodeLabels keep this,
101+
// otherwise get the CIDR with labels
102+
if networkv1.IsDefaultNetwork(network.Name) && !hasNodeLabels {
100103
defaultNwCIDRs = append(defaultNwCIDRs, ipRange.IpCidrRange)
101104
ipv6Addr := ca.cloud.GetIPV6Address(inf)
102105
if ipv6Addr != nil {
103106
defaultNwCIDRs = append(defaultNwCIDRs, ipv6Addr.String())
104107
}
105-
} else {
108+
}
109+
if !networkv1.IsDefaultNetwork(network.Name) {
106110
northInterfaces = append(northInterfaces, networkv1.NorthInterface{Network: network.Name, IpAddress: inf.NetworkIP})
107111
if _, ok := upStatusNetworks[network.Name]; ok {
108112
additionalNodeNetworks = append(additionalNodeNetworks, networkv1.NodeNetwork{Name: network.Name, Scope: "host-local", Cidrs: []string{ipRange.IpCidrRange}})
@@ -118,6 +122,42 @@ func (ca *cloudCIDRAllocator) performMultiNetworkCIDRAllocation(node *v1.Node, i
118122
return defaultNwCIDRs, nil
119123
}
120124

125+
// getNodeDefaultLabels returns true if the node has labels for subnet and Pod range
126+
func getNodeDefaultLabels(node *v1.Node) (bool, string, string) {
127+
defaultSubnet, foundSubnet := node.Labels[utilnode.NodePoolSubnetLabelPrefix]
128+
defaultPodRange, foundRange := node.Labels[utilnode.NodePoolPodRangeLabelPrefix]
129+
if !foundSubnet || defaultSubnet == "" || !foundRange || defaultPodRange == "" {
130+
return false, "", ""
131+
}
132+
return true, defaultSubnet, defaultPodRange
133+
}
134+
135+
// extractDefaultNwCIDRs returns the Pod CIDRs for default Network.
136+
// Different subnet can have the same secondary range name, here uses the subnet and range name
137+
// to find the matching CIDR(s)
138+
func (ca *cloudCIDRAllocator) extractDefaultNwCIDRs(interfaces []*compute.NetworkInterface, defaultSubnet, defaultPodRange string) (defaultNwCIDRs []string) {
139+
out:
140+
for _, inf := range interfaces {
141+
// extra the subnetwork name from the URL
142+
parts := strings.Split(inf.Subnetwork, "/subnetworks/")
143+
if parts[1] != defaultSubnet {
144+
continue
145+
}
146+
for _, ipRange := range inf.AliasIpRanges {
147+
if ipRange.SubnetworkRangeName != defaultPodRange {
148+
continue
149+
}
150+
defaultNwCIDRs = append(defaultNwCIDRs, ipRange.IpCidrRange)
151+
ipv6Addr := ca.cloud.GetIPV6Address(inf)
152+
if ipv6Addr != nil {
153+
defaultNwCIDRs = append(defaultNwCIDRs, ipv6Addr.String())
154+
}
155+
break out
156+
}
157+
}
158+
return defaultNwCIDRs
159+
}
160+
121161
func updateAnnotations(node *v1.Node, northInterfaces networkv1.NorthInterfacesAnnotation, additionalNodeNetworks networkv1.MultiNetworkAnnotation) error {
122162
northInterfaceAnn, err := networkv1.MarshalNorthInterfacesAnnotation(northInterfaces)
123163
if err != nil {

pkg/util/node/node.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,16 @@ import (
3030
"k8s.io/klog/v2"
3131
)
3232

33+
// Labels definitions.
34+
const (
35+
// NodePoolPodRangeLabelPrefix is the prefix for the default Pod range
36+
// name for the node and it can be different with cluster Pod range
37+
NodePoolPodRangeLabelPrefix = "cloud.google.com/gke-np-default-pod-range"
38+
// NodePoolSubnetLabelPrefix is the prefix for the default subnet
39+
// name for the node
40+
NodePoolSubnetLabelPrefix = "cloud.google.com/gke-np-default-subnet"
41+
)
42+
3343
type nodeForConditionPatch struct {
3444
Status nodeStatusForPatch `json:"status"`
3545
}

0 commit comments

Comments
 (0)