k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/cmd/cloud-controller-manager/nodeipamcontroller.go (about) 1 /* 2 Copyright 2018 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 // This file holds the code related with the sample nodeipamcontroller 18 // which demonstrates how cloud providers add external controllers to cloud-controller-manager 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 // defaultNodeMaskCIDRIPv4 is default mask size for IPv4 node cidr 44 defaultNodeMaskCIDRIPv4 = 24 45 // defaultNodeMaskCIDRIPv6 is default mask size for IPv6 node cidr 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 // should we start nodeIPAM 71 if !ccmConfig.ComponentConfig.KubeCloudShared.AllocateNodeCIDRs { 72 return nil, false, nil 73 } 74 75 // Cannot run cloud ipam controller if cloud provider is nil (--cloud-provider not set or set to 'external') 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 // failure: bad cidrs in config 81 clusterCIDRs, dualStack, err := processCIDRs(ccmConfig.ComponentConfig.KubeCloudShared.ClusterCIDR) 82 if err != nil { 83 return nil, false, err 84 } 85 86 // failure: more than one cidr but they are not configured as dual stack 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 // failure: more than cidrs is not allowed even with dual stack 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 // service cidr processing 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 // the following checks are triggered if both serviceCIDR and secondaryServiceCIDR are provided 112 if serviceCIDR != nil && secondaryServiceCIDR != nil { 113 // should be dual stack (from different IPFamilies) 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 // processCIDRs is a helper function that works on a comma separated cidrs and returns 147 // a list of typed cidrs 148 // a flag if cidrs represents a dual stack 149 // error if failed to parse any of the cidrs 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 // if cidrs has an error then the previous call will fail 159 // safe to ignore error checking on next call 160 dualstack, _ := netutils.IsDualStackCIDRs(cidrs) 161 162 return cidrs, dualstack, nil 163 } 164 165 // setNodeCIDRMaskSizes returns the IPv4 and IPv6 node cidr mask sizes to the value provided 166 // for --node-cidr-mask-size-ipv4 and --node-cidr-mask-size-ipv6 respectively. If value not provided, 167 // then it will return default IPv4 and IPv6 cidr mask sizes. 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 // --node-cidr-mask-size flag is incompatible with dual stack clusters. 184 ipv4Mask, ipv6Mask := defaultNodeMaskCIDRIPv4, defaultNodeMaskCIDRIPv6 185 isDualstack := len(clusterCIDRs) > 1 186 187 // case one: cluster is dualstack (i.e, more than one cidr) 188 if isDualstack { 189 // if --node-cidr-mask-size then fail, user must configure the correct dual-stack mask sizes (or use default) 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 // original flag is set 209 if maskConfigured { 210 // original mask flag is still the main reference. 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 // !maskV4Configured && !maskConfigured && maskV6Configured 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 }