1
16
17
18
19
20 package main
21
22 import (
23 "context"
24 "errors"
25 "fmt"
26 "net"
27 "strings"
28
29 cloudprovider "k8s.io/cloud-provider"
30 "k8s.io/cloud-provider/app"
31 cloudcontrollerconfig "k8s.io/cloud-provider/app/config"
32 genericcontrollermanager "k8s.io/controller-manager/app"
33 "k8s.io/controller-manager/controller"
34 "k8s.io/klog/v2"
35 nodeipamcontrolleroptions "k8s.io/kubernetes/cmd/kube-controller-manager/app/options"
36 nodeipamcontroller "k8s.io/kubernetes/pkg/controller/nodeipam"
37 nodeipamconfig "k8s.io/kubernetes/pkg/controller/nodeipam/config"
38 "k8s.io/kubernetes/pkg/controller/nodeipam/ipam"
39 netutils "k8s.io/utils/net"
40 )
41
42 const (
43
44 defaultNodeMaskCIDRIPv4 = 24
45
46 defaultNodeMaskCIDRIPv6 = 64
47 )
48
49 type nodeIPAMController struct {
50 nodeIPAMControllerConfiguration nodeipamconfig.NodeIPAMControllerConfiguration
51 nodeIPAMControllerOptions nodeipamcontrolleroptions.NodeIPAMControllerOptions
52 }
53
54 func (nodeIpamController *nodeIPAMController) StartNodeIpamControllerWrapper(initContext app.ControllerInitContext, completedConfig *cloudcontrollerconfig.CompletedConfig, cloud cloudprovider.Interface) app.InitFunc {
55 allErrors := nodeIpamController.nodeIPAMControllerOptions.Validate()
56 if len(allErrors) > 0 {
57 klog.Fatal("NodeIPAM controller values are not properly set.")
58 }
59 nodeIpamController.nodeIPAMControllerOptions.ApplyTo(&nodeIpamController.nodeIPAMControllerConfiguration)
60
61 return func(ctx context.Context, controllerContext genericcontrollermanager.ControllerContext) (controller.Interface, bool, error) {
62 return startNodeIpamController(ctx, initContext, completedConfig, nodeIpamController.nodeIPAMControllerConfiguration, controllerContext, cloud)
63 }
64 }
65
66 func startNodeIpamController(ctx context.Context, initContext app.ControllerInitContext, ccmConfig *cloudcontrollerconfig.CompletedConfig, nodeIPAMConfig nodeipamconfig.NodeIPAMControllerConfiguration, controllerCtx genericcontrollermanager.ControllerContext, cloud cloudprovider.Interface) (controller.Interface, bool, error) {
67 var serviceCIDR *net.IPNet
68 var secondaryServiceCIDR *net.IPNet
69
70
71 if !ccmConfig.ComponentConfig.KubeCloudShared.AllocateNodeCIDRs {
72 return nil, false, nil
73 }
74
75
76 if cloud == nil && ccmConfig.ComponentConfig.KubeCloudShared.CIDRAllocatorType == string(ipam.CloudAllocatorType) {
77 return nil, false, errors.New("--cidr-allocator-type is set to 'CloudAllocator' but cloud provider is not configured")
78 }
79
80
81 clusterCIDRs, dualStack, err := processCIDRs(ccmConfig.ComponentConfig.KubeCloudShared.ClusterCIDR)
82 if err != nil {
83 return nil, false, err
84 }
85
86
87 if len(clusterCIDRs) > 1 && !dualStack {
88 return nil, false, fmt.Errorf("len of ClusterCIDRs==%v and they are not configured as dual stack (at least one from each IPFamily", len(clusterCIDRs))
89 }
90
91
92 if len(clusterCIDRs) > 2 {
93 return nil, false, fmt.Errorf("len of clusters is:%v > more than max allowed of 2", len(clusterCIDRs))
94 }
95
96
97 if len(strings.TrimSpace(nodeIPAMConfig.ServiceCIDR)) != 0 {
98 _, serviceCIDR, err = netutils.ParseCIDRSloppy(nodeIPAMConfig.ServiceCIDR)
99 if err != nil {
100 klog.ErrorS(err, "Unsuccessful parsing of service CIDR", "CIDR", nodeIPAMConfig.ServiceCIDR)
101 }
102 }
103
104 if len(strings.TrimSpace(nodeIPAMConfig.SecondaryServiceCIDR)) != 0 {
105 _, secondaryServiceCIDR, err = netutils.ParseCIDRSloppy(nodeIPAMConfig.SecondaryServiceCIDR)
106 if err != nil {
107 klog.ErrorS(err, "Unsuccessful parsing of service CIDR", "CIDR", nodeIPAMConfig.SecondaryServiceCIDR)
108 }
109 }
110
111
112 if serviceCIDR != nil && secondaryServiceCIDR != nil {
113
114 dualstackServiceCIDR, err := netutils.IsDualStackCIDRs([]*net.IPNet{serviceCIDR, secondaryServiceCIDR})
115 if err != nil {
116 return nil, false, fmt.Errorf("failed to perform dualstack check on serviceCIDR and secondaryServiceCIDR error:%v", err)
117 }
118 if !dualstackServiceCIDR {
119 return nil, false, fmt.Errorf("serviceCIDR and secondaryServiceCIDR are not dualstack (from different IPfamiles)")
120 }
121 }
122
123 nodeCIDRMaskSizes, err := setNodeCIDRMaskSizes(nodeIPAMConfig, clusterCIDRs)
124 if err != nil {
125 return nil, false, err
126 }
127
128 nodeIpamController, err := nodeipamcontroller.NewNodeIpamController(
129 ctx,
130 controllerCtx.InformerFactory.Core().V1().Nodes(),
131 cloud,
132 controllerCtx.ClientBuilder.ClientOrDie(initContext.ClientName),
133 clusterCIDRs,
134 serviceCIDR,
135 secondaryServiceCIDR,
136 nodeCIDRMaskSizes,
137 ipam.CIDRAllocatorType(ccmConfig.ComponentConfig.KubeCloudShared.CIDRAllocatorType),
138 )
139 if err != nil {
140 return nil, true, err
141 }
142 go nodeIpamController.Run(ctx)
143 return nil, true, nil
144 }
145
146
147
148
149
150 func processCIDRs(cidrsList string) ([]*net.IPNet, bool, error) {
151 cidrsSplit := strings.Split(strings.TrimSpace(cidrsList), ",")
152
153 cidrs, err := netutils.ParseCIDRs(cidrsSplit)
154 if err != nil {
155 return nil, false, err
156 }
157
158
159
160 dualstack, _ := netutils.IsDualStackCIDRs(cidrs)
161
162 return cidrs, dualstack, nil
163 }
164
165
166
167
168 func setNodeCIDRMaskSizes(cfg nodeipamconfig.NodeIPAMControllerConfiguration, clusterCIDRs []*net.IPNet) ([]int, error) {
169
170 sortedSizes := func(maskSizeIPv4, maskSizeIPv6 int) []int {
171 nodeMaskCIDRs := make([]int, len(clusterCIDRs))
172
173 for idx, clusterCIDR := range clusterCIDRs {
174 if netutils.IsIPv6CIDR(clusterCIDR) {
175 nodeMaskCIDRs[idx] = maskSizeIPv6
176 } else {
177 nodeMaskCIDRs[idx] = maskSizeIPv4
178 }
179 }
180 return nodeMaskCIDRs
181 }
182
183
184 ipv4Mask, ipv6Mask := defaultNodeMaskCIDRIPv4, defaultNodeMaskCIDRIPv6
185 isDualstack := len(clusterCIDRs) > 1
186
187
188 if isDualstack {
189
190 if cfg.NodeCIDRMaskSize != 0 {
191 return nil, errors.New("usage of --node-cidr-mask-size is not allowed with dual-stack clusters")
192 }
193
194 if cfg.NodeCIDRMaskSizeIPv4 != 0 {
195 ipv4Mask = int(cfg.NodeCIDRMaskSizeIPv4)
196 }
197 if cfg.NodeCIDRMaskSizeIPv6 != 0 {
198 ipv6Mask = int(cfg.NodeCIDRMaskSizeIPv6)
199 }
200 return sortedSizes(ipv4Mask, ipv6Mask), nil
201 }
202
203 maskConfigured := cfg.NodeCIDRMaskSize != 0
204 maskV4Configured := cfg.NodeCIDRMaskSizeIPv4 != 0
205 maskV6Configured := cfg.NodeCIDRMaskSizeIPv6 != 0
206 isSingleStackIPv6 := netutils.IsIPv6CIDR(clusterCIDRs[0])
207
208
209 if maskConfigured {
210
211 if maskV4Configured || maskV6Configured {
212 return nil, errors.New("usage of --node-cidr-mask-size-ipv4 and --node-cidr-mask-size-ipv6 is not allowed if --node-cidr-mask-size is set. For dual-stack clusters please unset it and use IPFamily specific flags")
213 }
214
215 mask := int(cfg.NodeCIDRMaskSize)
216 return sortedSizes(mask, mask), nil
217 }
218
219 if maskV4Configured {
220 if isSingleStackIPv6 {
221 return nil, errors.New("usage of --node-cidr-mask-size-ipv4 is not allowed for a single-stack IPv6 cluster")
222 }
223
224 ipv4Mask = int(cfg.NodeCIDRMaskSizeIPv4)
225 }
226
227
228 if maskV6Configured {
229 if !isSingleStackIPv6 {
230 return nil, errors.New("usage of --node-cidr-mask-size-ipv6 is not allowed for a single-stack IPv4 cluster")
231 }
232
233 ipv6Mask = int(cfg.NodeCIDRMaskSizeIPv6)
234 }
235 return sortedSizes(ipv4Mask, ipv6Mask), nil
236 }
237
View as plain text