Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[feat][wip] : add ipv6 ipam to CCM #369

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
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
65 changes: 23 additions & 42 deletions cloud/linode/nodeipamcontroller.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,20 +28,21 @@
v1 "k8s.io/client-go/informers/core/v1"
"k8s.io/client-go/kubernetes"
cloudprovider "k8s.io/cloud-provider"
nodeipamcontroller "k8s.io/kubernetes/pkg/controller/nodeipam"
"k8s.io/kubernetes/pkg/controller/nodeipam/ipam"
netutils "k8s.io/utils/net"

nodeipamcontroller "github.com/linode/linode-cloud-controller-manager/cloud/nodeipam"
"github.com/linode/linode-cloud-controller-manager/cloud/nodeipam/ipam"
)

const (
maxAllowedNodeCIDRs = 2
maxAllowedNodeCIDRsIPv4 = 1
)

var (
// defaultNodeMaskCIDRIPv4 is default mask size for IPv4 node cidr
defaultNodeMaskCIDRIPv4 = 24
// defaultNodeMaskCIDRIPv6 is default mask size for IPv6 node cidr
defaultNodeMaskCIDRIPv6 = 64
defaultNodeMaskCIDRIPv6 = 112
)

func startNodeIpamController(stopCh <-chan struct{}, cloud cloudprovider.Interface, nodeInformer v1.NodeInformer, kubeclient kubernetes.Interface) error {
Expand All @@ -54,19 +55,21 @@
}

// failure: bad cidrs in config
clusterCIDRs, dualStack, err := processCIDRs(Options.ClusterCIDRIPv4)
clusterCIDRs, err := processCIDRs(Options.ClusterCIDRIPv4)
if err != nil {
return fmt.Errorf("processCIDRs failed: %w", err)
}

// failure: more than one cidr but they are not configured as dual stack
if len(clusterCIDRs) > 1 && !dualStack {
return fmt.Errorf("len of ClusterCIDRs==%v and they are not configured as dual stack (at least one from each IPFamily", len(clusterCIDRs))
if len(clusterCIDRs) == 0 {
return fmt.Errorf("no clusterCIDR specified. Must specify --cluster-cidr if --allocate-node-cidrs is set")
}

Check warning on line 65 in cloud/linode/nodeipamcontroller.go

View check run for this annotation

Codecov / codecov/patch

cloud/linode/nodeipamcontroller.go#L64-L65

Added lines #L64 - L65 were not covered by tests

if len(clusterCIDRs) > maxAllowedNodeCIDRsIPv4 {
return fmt.Errorf("too many clusterCIDRs specified for ipv4, max allowed is %d", maxAllowedNodeCIDRsIPv4)
}

// failure: more than cidrs is not allowed even with dual stack
if len(clusterCIDRs) > maxAllowedNodeCIDRs {
return fmt.Errorf("len of clusters is:%v > more than max allowed of %d", len(clusterCIDRs), maxAllowedNodeCIDRs)
if clusterCIDRs[0].IP.To4() == nil {
return fmt.Errorf("clusterCIDR %s is not ipv4", clusterCIDRs[0].String())
}

/* TODO: uncomment and fix if we want to support service cidr overlap with nodecidr
Expand Down Expand Up @@ -98,7 +101,7 @@
}
*/

nodeCIDRMaskSizes := setNodeCIDRMaskSizes(clusterCIDRs)
nodeCIDRMaskSizes := setNodeCIDRMaskSizes()

ctx := wait.ContextForChannel(stopCh)

Expand All @@ -111,7 +114,7 @@
serviceCIDR,
secondaryServiceCIDR,
nodeCIDRMaskSizes,
ipam.RangeAllocatorType,
ipam.CloudAllocatorType,
)
if err != nil {
return err
Expand All @@ -121,47 +124,25 @@
return nil
}

// processCIDRs is a helper function that works on a comma separated cidrs and returns
// a list of typed cidrs
// a flag if cidrs represents a dual stack
// error if failed to parse any of the cidrs
func processCIDRs(cidrsList string) ([]*net.IPNet, bool, error) {
// processCIDR is a helper function that works on cidr and returns a list of typed cidrs
// error if failed to parse the cidr
func processCIDRs(cidrsList string) ([]*net.IPNet, error) {
cidrsSplit := strings.Split(strings.TrimSpace(cidrsList), ",")

cidrs, err := netutils.ParseCIDRs(cidrsSplit)
if err != nil {
return nil, false, err
}

// if cidrs has an error then the previous call will fail
// safe to ignore error checking on next call
dualstack, err := netutils.IsDualStackCIDRs(cidrs)
if err != nil {
return nil, false, fmt.Errorf("failed to perform dualstack check on cidrs: %w", err)
return nil, err
}

return cidrs, dualstack, nil
return cidrs, nil
}

func setNodeCIDRMaskSizes(clusterCIDRs []*net.IPNet) []int {
sortedSizes := func(maskSizeIPv4, maskSizeIPv6 int) []int {
nodeMaskCIDRs := make([]int, len(clusterCIDRs))

for idx, clusterCIDR := range clusterCIDRs {
if netutils.IsIPv6CIDR(clusterCIDR) {
nodeMaskCIDRs[idx] = maskSizeIPv6
} else {
nodeMaskCIDRs[idx] = maskSizeIPv4
}
}
return nodeMaskCIDRs
}

func setNodeCIDRMaskSizes() []int {
if Options.NodeCIDRMaskSizeIPv4 != 0 {
defaultNodeMaskCIDRIPv4 = Options.NodeCIDRMaskSizeIPv4
}
if Options.NodeCIDRMaskSizeIPv6 != 0 {
defaultNodeMaskCIDRIPv6 = Options.NodeCIDRMaskSizeIPv6
}
return sortedSizes(defaultNodeMaskCIDRIPv4, defaultNodeMaskCIDRIPv6)
return []int{defaultNodeMaskCIDRIPv4, defaultNodeMaskCIDRIPv6}
}
116 changes: 30 additions & 86 deletions cloud/linode/nodeipamcontroller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,65 +33,22 @@ import (

func Test_setNodeCIDRMaskSizes(t *testing.T) {
type args struct {
clusterCIDRs []*net.IPNet
ipv4NetMask int
ipv6NetMask int
ipv4NetMask int
ipv6NetMask int
}
_, ipv4Net, _ := net.ParseCIDR("10.192.0.0/10")
_, ipv6Net, _ := net.ParseCIDR("fd00::/56")
tests := []struct {
name string
args args
want []int
}{
{
name: "empty cluster cidrs",
args: args{
clusterCIDRs: []*net.IPNet{},
},
want: []int{},
},
{
name: "single cidr",
args: args{
clusterCIDRs: []*net.IPNet{
{
IP: ipv4Net.IP,
Mask: ipv4Net.Mask,
},
},
},
want: []int{defaultNodeMaskCIDRIPv4},
},
{
name: "two cidrs",
args: args{
clusterCIDRs: []*net.IPNet{
{
IP: ipv4Net.IP,
Mask: ipv4Net.Mask,
},
{
IP: ipv6Net.IP,
Mask: ipv6Net.Mask,
},
},
},
name: "default cidr mask sizes",
args: args{},
want: []int{defaultNodeMaskCIDRIPv4, defaultNodeMaskCIDRIPv6},
},
{
name: "two cidrs with custom mask sizes",
args: args{
clusterCIDRs: []*net.IPNet{
{
IP: ipv4Net.IP,
Mask: ipv4Net.Mask,
},
{
IP: ipv6Net.IP,
Mask: ipv6Net.Mask,
},
},
ipv4NetMask: 25,
ipv6NetMask: 80,
},
Expand All @@ -112,7 +69,7 @@ func Test_setNodeCIDRMaskSizes(t *testing.T) {
if tt.args.ipv6NetMask != 0 {
Options.NodeCIDRMaskSizeIPv6 = tt.args.ipv6NetMask
}
got := setNodeCIDRMaskSizes(tt.args.clusterCIDRs)
got := setNodeCIDRMaskSizes()
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("setNodeCIDRMaskSizes() = %v, want %v", got, tt.want)
}
Expand All @@ -125,22 +82,19 @@ func Test_processCIDRs(t *testing.T) {
cidrsList string
}
_, ipv4Net, _ := net.ParseCIDR("10.192.0.0/10")
_, ipv6Net, _ := net.ParseCIDR("fd00::/56")
tests := []struct {
name string
args args
want []*net.IPNet
ipv6Enabled bool
wantErr bool
name string
args args
want []*net.IPNet
wantErr bool
}{
{
name: "empty cidr list",
args: args{
cidrsList: "",
},
want: nil,
ipv6Enabled: false,
wantErr: true,
want: nil,
wantErr: true,
},
{
name: "valid ipv4 cidr",
Expand All @@ -153,41 +107,19 @@ func Test_processCIDRs(t *testing.T) {
Mask: ipv4Net.Mask,
},
},
ipv6Enabled: false,
wantErr: false,
},
{
name: "valid ipv4 and ipv6 cidrs",
args: args{
cidrsList: "10.192.0.0/10,fd00::/56",
},
want: []*net.IPNet{
{
IP: ipv4Net.IP,
Mask: ipv4Net.Mask,
},
{
IP: ipv6Net.IP,
Mask: ipv6Net.Mask,
},
},
ipv6Enabled: true,
wantErr: false,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, got1, err := processCIDRs(tt.args.cidrsList)
got, err := processCIDRs(tt.args.cidrsList)
if (err != nil) != tt.wantErr {
t.Errorf("processCIDRs() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("processCIDRs() got = %v, want %v", got, tt.want)
}
if got1 != tt.ipv6Enabled {
t.Errorf("processCIDRs() got1 = %v, want %v", got1, tt.ipv6Enabled)
}
})
}
}
Expand Down Expand Up @@ -220,7 +152,19 @@ func Test_startNodeIpamController(t *testing.T) {
wantErr: false,
},
{
name: "incorrect cluster-cidrs specified",
name: "allocate-node-cidrs set but cluster-cidr not set",
args: args{
stopCh: make(<-chan struct{}),
cloud: nil,
nodeInformer: nil,
kubeclient: nil,
allocateNodeCIDRs: true,
clusterCIDR: "",
},
wantErr: true,
},
{
name: "incorrect cluster-cidr specified",
args: args{
stopCh: make(<-chan struct{}),
cloud: nil,
Expand All @@ -232,26 +176,26 @@ func Test_startNodeIpamController(t *testing.T) {
wantErr: true,
},
{
name: "more than one ipv4 cidrs specified",
name: "ipv6 cidr specified",
args: args{
stopCh: make(<-chan struct{}),
cloud: nil,
nodeInformer: nil,
kubeclient: nil,
allocateNodeCIDRs: true,
clusterCIDR: "10.192.0.0/10,192.168.0.0/16",
clusterCIDR: "fd00::/80",
},
wantErr: true,
},
{
name: "more than two cidrs specified",
name: "more than one cidr specified",
args: args{
stopCh: make(<-chan struct{}),
cloud: nil,
nodeInformer: nil,
kubeclient: nil,
allocateNodeCIDRs: true,
clusterCIDR: "10.192.0.0/10,fd00::/80,192.168.0.0/16",
clusterCIDR: "10.192.0.0/10,fd00::/80",
},
wantErr: true,
},
Expand Down
19 changes: 19 additions & 0 deletions cloud/nodeipam/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
/*
Copyright 2014 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

// Package nodeipam contains code for syncing cloud instances with
// node registry
package nodeipam
Loading
Loading