diff --git a/README.md b/README.md index 9d8a521a..5cd14b26 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,9 @@ A Cloud Controller Manager (CCM) is a Kubernetes control plane component that em - Manages network policies - Configures network routes for optimal communication +#### NodeIPAM Controller +- Manages and configures pod CIDRs to nodes + ## Requirements - Kubernetes 1.22+ @@ -75,6 +78,7 @@ A Cloud Controller Manager (CCM) is a Kubernetes control plane component that em - [Firewall Setup](docs/configuration/firewall.md) - [Route Configuration](docs/configuration/routes.md) - [Session Affinity](docs/configuration/session-affinity.md) + - [NodeIPAM Configuration](docs/configuration/nodeipam.md) ### Examples and Development - [Examples](docs/examples/README.md) - Real-world usage examples diff --git a/cloud/linode/cloud.go b/cloud/linode/cloud.go index 82747537..b91e76fe 100644 --- a/cloud/linode/cloud.go +++ b/cloud/linode/cloud.go @@ -51,6 +51,10 @@ var Options struct { NodeBalancerBackendIPv4Subnet string GlobalStopChannel chan<- struct{} EnableIPv6ForLoadBalancers bool + AllocateNodeCIDRs bool + ClusterCIDRIPv4 string + NodeCIDRMaskSizeIPv4 int + NodeCIDRMaskSizeIPv6 int } type linodeCloud struct { @@ -187,6 +191,10 @@ func (c *linodeCloud) Initialize(clientBuilder cloudprovider.ControllerClientBui serviceInformer := sharedInformer.Core().V1().Services() nodeInformer := sharedInformer.Core().V1().Nodes() + if err := startNodeIpamController(stopCh, c, nodeInformer, kubeclient); err != nil { + klog.Fatal("starting of node ipam controller failed", err) + } + if c.linodeTokenHealthChecker != nil { go c.linodeTokenHealthChecker.Run(stopCh) } diff --git a/cloud/linode/nodeipamcontroller.go b/cloud/linode/nodeipamcontroller.go new file mode 100644 index 00000000..678574c5 --- /dev/null +++ b/cloud/linode/nodeipamcontroller.go @@ -0,0 +1,167 @@ +/* +Copyright 2018 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. +*/ + +// This file holds the code related with the sample nodeipamcontroller +// which demonstrates how cloud providers add external controllers to cloud-controller-manager + +package linode + +import ( + "fmt" + "net" + "strings" + + "k8s.io/apimachinery/pkg/util/wait" + 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" +) + +const ( + maxAllowedNodeCIDRs = 2 +) + +var ( + // defaultNodeMaskCIDRIPv4 is default mask size for IPv4 node cidr + defaultNodeMaskCIDRIPv4 = 24 + // defaultNodeMaskCIDRIPv6 is default mask size for IPv6 node cidr + defaultNodeMaskCIDRIPv6 = 64 +) + +func startNodeIpamController(stopCh <-chan struct{}, cloud cloudprovider.Interface, nodeInformer v1.NodeInformer, kubeclient kubernetes.Interface) error { + var serviceCIDR *net.IPNet + var secondaryServiceCIDR *net.IPNet + + // should we start nodeIPAM + if !Options.AllocateNodeCIDRs { + return nil + } + + // failure: bad cidrs in config + clusterCIDRs, dualStack, 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)) + } + + // 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) + } + + /* TODO: uncomment and fix if we want to support service cidr overlap with nodecidr + // service cidr processing + if len(strings.TrimSpace(nodeIPAMConfig.ServiceCIDR)) != 0 { + _, serviceCIDR, err = netutils.ParseCIDRSloppy(nodeIPAMConfig.ServiceCIDR) + if err != nil { + klog.ErrorS(err, "Unsuccessful parsing of service CIDR", "CIDR", nodeIPAMConfig.ServiceCIDR) + } + } + + if len(strings.TrimSpace(nodeIPAMConfig.SecondaryServiceCIDR)) != 0 { + _, secondaryServiceCIDR, err = netutils.ParseCIDRSloppy(nodeIPAMConfig.SecondaryServiceCIDR) + if err != nil { + klog.ErrorS(err, "Unsuccessful parsing of service CIDR", "CIDR", nodeIPAMConfig.SecondaryServiceCIDR) + } + } + + // the following checks are triggered if both serviceCIDR and secondaryServiceCIDR are provided + if serviceCIDR != nil && secondaryServiceCIDR != nil { + // should be dual stack (from different IPFamilies) + dualstackServiceCIDR, err := netutils.IsDualStackCIDRs([]*net.IPNet{serviceCIDR, secondaryServiceCIDR}) + if err != nil { + return nil, false, fmt.Errorf("failed to perform dualstack check on serviceCIDR and secondaryServiceCIDR error:%v", err) + } + if !dualstackServiceCIDR { + return nil, false, fmt.Errorf("serviceCIDR and secondaryServiceCIDR are not dualstack (from different IPfamiles)") + } + } + */ + + nodeCIDRMaskSizes := setNodeCIDRMaskSizes(clusterCIDRs) + + ctx := wait.ContextForChannel(stopCh) + + nodeIpamController, err := nodeipamcontroller.NewNodeIpamController( + ctx, + nodeInformer, + cloud, + kubeclient, + clusterCIDRs, + serviceCIDR, + secondaryServiceCIDR, + nodeCIDRMaskSizes, + ipam.RangeAllocatorType, + ) + if err != nil { + return err + } + + go nodeIpamController.Run(ctx) + 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) { + 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 cidrs, dualstack, 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 + } + + if Options.NodeCIDRMaskSizeIPv4 != 0 { + defaultNodeMaskCIDRIPv4 = Options.NodeCIDRMaskSizeIPv4 + } + if Options.NodeCIDRMaskSizeIPv6 != 0 { + defaultNodeMaskCIDRIPv6 = Options.NodeCIDRMaskSizeIPv6 + } + return sortedSizes(defaultNodeMaskCIDRIPv4, defaultNodeMaskCIDRIPv6) +} diff --git a/cloud/linode/nodeipamcontroller_test.go b/cloud/linode/nodeipamcontroller_test.go new file mode 100644 index 00000000..6c15e1a5 --- /dev/null +++ b/cloud/linode/nodeipamcontroller_test.go @@ -0,0 +1,286 @@ +/* +Copyright 2018 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. +*/ + +// This file holds the code related with the sample nodeipamcontroller +// which demonstrates how cloud providers add external controllers to cloud-controller-manager + +package linode + +import ( + "net" + "reflect" + "testing" + + "k8s.io/client-go/informers" + v1 "k8s.io/client-go/informers/core/v1" + "k8s.io/client-go/kubernetes" + "k8s.io/client-go/kubernetes/fake" + cloudprovider "k8s.io/cloud-provider" +) + +func Test_setNodeCIDRMaskSizes(t *testing.T) { + type args struct { + clusterCIDRs []*net.IPNet + 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, + }, + }, + }, + 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, + }, + want: []int{25, 80}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + oldCIDRMaskSizeIPv4 := Options.NodeCIDRMaskSizeIPv4 + oldCIDRMaskSizeIPv6 := Options.NodeCIDRMaskSizeIPv6 + defer func() { + Options.NodeCIDRMaskSizeIPv4 = oldCIDRMaskSizeIPv4 + Options.NodeCIDRMaskSizeIPv6 = oldCIDRMaskSizeIPv6 + }() + if tt.args.ipv4NetMask != 0 { + Options.NodeCIDRMaskSizeIPv4 = tt.args.ipv4NetMask + } + if tt.args.ipv6NetMask != 0 { + Options.NodeCIDRMaskSizeIPv6 = tt.args.ipv6NetMask + } + got := setNodeCIDRMaskSizes(tt.args.clusterCIDRs) + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("setNodeCIDRMaskSizes() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_processCIDRs(t *testing.T) { + type args struct { + 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: "empty cidr list", + args: args{ + cidrsList: "", + }, + want: nil, + ipv6Enabled: false, + wantErr: true, + }, + { + name: "valid ipv4 cidr", + args: args{ + cidrsList: "10.192.0.0/10", + }, + want: []*net.IPNet{ + { + IP: ipv4Net.IP, + 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, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, got1, 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) + } + }) + } +} + +func Test_startNodeIpamController(t *testing.T) { + type args struct { + stopCh <-chan struct{} + cloud cloudprovider.Interface + nodeInformer v1.NodeInformer + kubeclient kubernetes.Interface + allocateNodeCIDRs bool + clusterCIDR string + } + kubeClient := fake.NewSimpleClientset() + tests := []struct { + name string + args args + wantErr bool + }{ + { + name: "allocate-node-cidrs not set", + args: args{ + stopCh: make(<-chan struct{}), + cloud: nil, + nodeInformer: nil, + kubeclient: nil, + allocateNodeCIDRs: false, + clusterCIDR: "", + }, + wantErr: false, + }, + { + name: "incorrect cluster-cidrs specified", + args: args{ + stopCh: make(<-chan struct{}), + cloud: nil, + nodeInformer: nil, + kubeclient: nil, + allocateNodeCIDRs: true, + clusterCIDR: "10.10.10.10", + }, + wantErr: true, + }, + { + name: "more than one ipv4 cidrs 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", + }, + wantErr: true, + }, + { + name: "more than two cidrs 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", + }, + wantErr: true, + }, + { + name: "correct cidrs specified", + args: args{ + stopCh: make(<-chan struct{}), + cloud: nil, + nodeInformer: informers.NewSharedInformerFactory(kubeClient, 0).Core().V1().Nodes(), + kubeclient: kubeClient, + allocateNodeCIDRs: true, + clusterCIDR: "10.192.0.0/10", + }, + wantErr: false, + }, + } + for _, tt := range tests { + currAllocateNodeCIDRs := Options.AllocateNodeCIDRs + currClusterCIDR := Options.ClusterCIDRIPv4 + defer func() { + Options.AllocateNodeCIDRs = currAllocateNodeCIDRs + Options.ClusterCIDRIPv4 = currClusterCIDR + }() + Options.AllocateNodeCIDRs = tt.args.allocateNodeCIDRs + Options.ClusterCIDRIPv4 = tt.args.clusterCIDR + t.Run(tt.name, func(t *testing.T) { + if err := startNodeIpamController(tt.args.stopCh, tt.args.cloud, tt.args.nodeInformer, tt.args.kubeclient); (err != nil) != tt.wantErr { + t.Errorf("startNodeIpamController() error = %v, wantErr %v", err, tt.wantErr) + } + }) + } +} diff --git a/deploy/chart/templates/daemonset.yaml b/deploy/chart/templates/daemonset.yaml index b9b2effa..60c75f2c 100644 --- a/deploy/chart/templates/daemonset.yaml +++ b/deploy/chart/templates/daemonset.yaml @@ -50,6 +50,24 @@ spec: {{- with .Values.linodegoDebug }} - --linodego-debug={{ . }} {{- end }} + {{- $clusterCIDR := .Values.clusterCIDR }} + {{- if and .Values.routeController .Values.routeController.clusterCIDR }} + {{- $clusterCIDR = .Values.routeController.clusterCIDR }} + {{- end }} + {{- if .Values.enableNodeIPAM }} + {{- with .Values.enableNodeIPAM }} + - --allocate-node-cidrs={{ . }} + {{- end }} + {{- if not $clusterCIDR }} + {{- fail "clusterCIDR is required if enableNodeIPAM is set" }} + {{- end }} + {{- with .Values.nodeCIDRMaskSizeIPv4 }} + - --node-cidr-mask-size-ipv4={{ . }} + {{- end }} + {{- with .Values.nodeCIDRMaskSizeIPv6 }} + - --node-cidr-mask-size-ipv6={{ . }} + {{- end }} + {{- end }} {{- $vpcNames := .Values.vpcNames }} {{- if and .Values.routeController .Values.routeController.vpcNames }} {{- $vpcNames = .Values.routeController.vpcNames }} @@ -70,8 +88,10 @@ spec: {{- if not (or $vpcName $vpcNames) }} {{- fail "Neither vpcName nor vpcNames is set. Please set one of them." }} {{- end }} + {{- if not $clusterCIDR }} + {{- fail "clusterCIDR is required if route-controller is enabled" }} + {{- end }} - --configure-cloud-routes={{ default true .Values.routeController.configureCloudRoutes }} - - --cluster-cidr={{ required "A valid .Values.routeController.clusterCIDR is required" .Values.routeController.clusterCIDR }} {{- with .Values.routeController.routeReconciliationPeriod }} - --route-reconciliation-period={{ . }} {{- end }} @@ -84,7 +104,10 @@ spec: {{- end }} {{- with $subnetNames }} - --subnet-names={{ . }} - {{ end }} + {{- end }} + {{- with $clusterCIDR }} + - --cluster-cidr={{ . }} + {{- end }} {{- if .Values.sharedIPLoadBalancing }} {{- with .Values.sharedIPLoadBalancing.bgpNodeSelector }} - --bgp-node-selector={{ . }} diff --git a/deploy/chart/values.yaml b/deploy/chart/values.yaml index 7da9649c..2f0d24b2 100644 --- a/deploy/chart/values.yaml +++ b/deploy/chart/values.yaml @@ -72,9 +72,15 @@ tolerations: # vpcName: [Deprecated: use vpcNames instead] # vpcNames: # subnetNames: -# clusterCIDR: 10.0.0.0/8 +# clusterCIDR: 10.192.0.0/10 # configureCloudRoutes: true +# This section adds ability to enable nodeipam-controller for ccm +# enableNodeIPAM: false +# clusterCIDR: 10.192.0.0/10 +# nodeCIDRMaskSizeIPv4: 24 +# nodeCIDRMaskSizeIPv6: 64 + # vpcs and subnets that node internal IPs will be assigned from (not required if already specified in routeController) # vpcName: [Deprecated: use vpcNames instead] # vpcNames: diff --git a/docs/configuration/environment.md b/docs/configuration/environment.md index 9d3624de..fb6f04a9 100644 --- a/docs/configuration/environment.md +++ b/docs/configuration/environment.md @@ -48,6 +48,8 @@ The CCM supports the following flags: | `--nodebalancer-tags` | `[]` | Linode tags to apply to all NodeBalancers | | `--nodebalancer-backend-ipv4-subnet` | `""` | ipv4 subnet to use for NodeBalancer backends | | `--enable-ipv6-for-loadbalancers` | `false` | Set both IPv4 and IPv6 addresses for all LoadBalancer services (when disabled, only IPv4 is used). This can also be configured per-service using the `service.beta.kubernetes.io/linode-loadbalancer-enable-ipv6-ingress` annotation. | +| `--node-cidr-mask-size-ipv4` | `24` | ipv4 cidr mask size for pod cidrs allocated to nodes | +| `--node-cidr-mask-size-ipv6` | `64` | ipv6 cidr mask size for pod cidrs allocated to nodes | ## Configuration Methods diff --git a/docs/configuration/nodeipam.md b/docs/configuration/nodeipam.md new file mode 100644 index 00000000..b093a431 --- /dev/null +++ b/docs/configuration/nodeipam.md @@ -0,0 +1,37 @@ +# Node IPAM using CCM + +## Overview + +Linode CCM supports configuring and managing pod CIDRs allocated to nodes. This includes both ipv4 and ipv6 CIDRs. It is disabled by default. It can be enabled by starting CCM with `--allocate-node-cidrs` and `--cluster-cidr` flags. + +```yaml +spec: + template: + spec: + containers: + - name: ccm-linode + args: + - --allocate-node-cidrs=true + - --cluster-cidr=10.192.0.0/10 +``` + +Once enabled, CCM will manage and allocate pod CIDRs to nodes. + +Note: +Make sure node IPAM allocation is disabled in kube-controller-manager to avoid both controllers competing to assign CIDRs to nodes. To make sure its disabled, check and make sure kube-controller-manager is not started with `--allocate-node-cidrs` flag. + +## Allocated subnet size +By default, CCM allocates /24 subnet for ipv4 addresses and /64 for ipv6 addresses to nodes. If one wants different subnet range, it can be configured by using `--node-cidr-mask-size-ipv4` and `--node-cidr-mask-size-ipv6` flags. + +```yaml +spec: + template: + spec: + containers: + - name: ccm-linode + args: + - --allocate-node-cidrs=true + - --cluster-cidr=10.192.0.0/10,fd00::/56 + - --node-cidr-mask-size-ipv4=25 + - --node-cidr-mask-size-ipv6=64 +``` diff --git a/go.mod b/go.mod index 1ed11434..0fcbb3f3 100644 --- a/go.mod +++ b/go.mod @@ -46,6 +46,7 @@ require ( github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/distribution/reference v0.6.0 // indirect github.com/emicklei/go-restful/v3 v3.12.1 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.8.0 // indirect @@ -95,6 +96,7 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/oklog/ulid v1.3.1 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/petermattis/goid v0.0.0-20241211131331-93ee7e083c43 // indirect @@ -161,6 +163,8 @@ require ( k8s.io/controller-manager v0.32.3 // indirect k8s.io/kms v0.32.3 // indirect k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect + k8s.io/kubelet v0.0.0 // indirect + k8s.io/kubernetes v1.32.3 sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.1 // indirect sigs.k8s.io/gateway-api v1.2.1 // indirect sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect @@ -168,3 +172,21 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) + +// Fixes for `unknown revision v0.0.0` reported by `go list -modfile=go.mod -m -json -mod=mod all` +replace ( + k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.32.3 + k8s.io/cri-api => k8s.io/cri-api v0.32.3 + k8s.io/cri-client => k8s.io/cri-client v0.32.3 + k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.32.3 + k8s.io/dynamic-resource-allocation => k8s.io/dynamic-resource-allocation v0.32.3 + k8s.io/externaljwt => k8s.io/externaljwt v0.32.3 + k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.32.3 + k8s.io/kube-proxy => k8s.io/kube-proxy v0.32.3 + k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.32.3 + k8s.io/kubelet => k8s.io/kubelet v0.32.3 + k8s.io/metrics => k8s.io/metrics v0.32.3 + k8s.io/mount-utils => k8s.io/mount-utils v0.32.3 + k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.32.3 + k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.32.3 +) diff --git a/go.sum b/go.sum index a4533348..5e8f7082 100644 --- a/go.sum +++ b/go.sum @@ -58,6 +58,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= @@ -243,6 +245,8 @@ github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b h1:FfH+VrHHk6Lxt9HdVS0PXzSXFyS2NbZKXv33FYPol0A= github.com/opentracing/opentracing-go v1.2.1-0.20220228012449-10b1cf09e00b/go.mod h1:AC62GU6hc0BrNm+9RK9VSiwa/EUe1bkIeFORAMcHvJU= github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M= @@ -495,6 +499,10 @@ k8s.io/kms v0.32.3 h1:HhHw5+pRCzEJp3oFFJ1q5W2N6gAI7YkUg4ay4Z0dgwM= k8s.io/kms v0.32.3/go.mod h1:Bk2evz/Yvk0oVrvm4MvZbgq8BD34Ksxs2SRHn4/UiOM= k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg= k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas= +k8s.io/kubelet v0.32.3 h1:B9HzW4yB67flx8tN2FYuDwZvxnmK3v5EjxxFvOYjmc8= +k8s.io/kubelet v0.32.3/go.mod h1:yyAQSCKC+tjSlaFw4HQG7Jein+vo+GeKBGdXdQGvL1U= +k8s.io/kubernetes v1.32.3 h1:2A58BlNME8NwsMawmnM6InYo3Jf35Nw5G79q46kXwoA= +k8s.io/kubernetes v1.32.3/go.mod h1:GvhiBeolvSRzBpFlgM0z/Bbu3Oxs9w3P6XfEgYaMi8k= k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0= k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.1 h1:uOuSLOMBWkJH0TWa9X6l+mj5nZdm6Ay6Bli8HL8rNfk= diff --git a/main.go b/main.go index f11a2614..4123318a 100644 --- a/main.go +++ b/main.go @@ -93,6 +93,8 @@ func main() { command.Flags().StringVar(&linode.Options.NodeBalancerBackendIPv4Subnet, "nodebalancer-backend-ipv4-subnet", "", "ipv4 subnet to use for NodeBalancer backends") command.Flags().StringSliceVar(&linode.Options.NodeBalancerTags, "nodebalancer-tags", []string{}, "Linode tags to apply to all NodeBalancers") command.Flags().BoolVar(&linode.Options.EnableIPv6ForLoadBalancers, "enable-ipv6-for-loadbalancers", false, "set both IPv4 and IPv6 addresses for all LoadBalancer services (when disabled, only IPv4 is used)") + command.Flags().IntVar(&linode.Options.NodeCIDRMaskSizeIPv4, "node-cidr-mask-size-ipv4", 0, "ipv4 cidr mask size for pod cidrs allocated to nodes") + command.Flags().IntVar(&linode.Options.NodeCIDRMaskSizeIPv6, "node-cidr-mask-size-ipv6", 0, "ipv6 cidr mask size for pod cidrs allocated to nodes") // Set static flags command.Flags().VisitAll(func(fl *pflag.Flag) { @@ -154,6 +156,14 @@ func main() { func cloudInitializer(config *config.CompletedConfig) cloudprovider.Interface { // initialize cloud provider with the cloud provider name and config file provided + if config.ComponentConfig.KubeCloudShared.AllocateNodeCIDRs { + linode.Options.AllocateNodeCIDRs = true + if config.ComponentConfig.KubeCloudShared.ClusterCIDR == "" { + fmt.Fprintf(os.Stderr, "--cluster-cidr is not set. This is required if --allocate-node-cidrs is set.\n") + os.Exit(1) + } + linode.Options.ClusterCIDRIPv4 = config.ComponentConfig.KubeCloudShared.ClusterCIDR + } cloud, err := cloudprovider.InitCloudProvider(linode.ProviderName, "") if err != nil { klog.Fatalf("Cloud provider could not be initialized: %v", err)