Skip to content

Commit 3ac7ae6

Browse files
committed
kubeadm --pod-network-cidr supports a comma separated list of pod
CIDRs. This is a necesary change for dual-stack.
1 parent ec77598 commit 3ac7ae6

File tree

5 files changed

+77
-33
lines changed

5 files changed

+77
-33
lines changed

cmd/kubeadm/app/apis/kubeadm/validation/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ go_library(
2121
"//staging/src/k8s.io/cluster-bootstrap/token/util:go_default_library",
2222
"//vendor/github.com/pkg/errors:go_default_library",
2323
"//vendor/github.com/spf13/pflag:go_default_library",
24+
"//vendor/k8s.io/utils/net:go_default_library",
2425
],
2526
)
2627

cmd/kubeadm/app/apis/kubeadm/validation/validation.go

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ import (
4040
"k8s.io/kubernetes/cmd/kubeadm/app/features"
4141
kubeadmutil "k8s.io/kubernetes/cmd/kubeadm/app/util"
4242
"k8s.io/kubernetes/pkg/registry/core/service/ipallocator"
43+
utilnet "k8s.io/utils/net"
4344
)
4445

4546
// ValidateInitConfiguration validates an InitConfiguration object and collects all encountered errors
@@ -48,6 +49,7 @@ func ValidateInitConfiguration(c *kubeadm.InitConfiguration) field.ErrorList {
4849
allErrs = append(allErrs, ValidateNodeRegistrationOptions(&c.NodeRegistration, field.NewPath("nodeRegistration"))...)
4950
allErrs = append(allErrs, ValidateBootstrapTokens(c.BootstrapTokens, field.NewPath("bootstrapTokens"))...)
5051
allErrs = append(allErrs, ValidateClusterConfiguration(&c.ClusterConfiguration)...)
52+
// TODO(Arvinderpal): update advertiseAddress validation for dual-stack once it's implemented.
5153
allErrs = append(allErrs, ValidateAPIEndpoint(&c.LocalAPIEndpoint, field.NewPath("localAPIEndpoint"))...)
5254
// TODO: Maybe validate that .CertificateKey is a valid hex encoded AES key
5355
return allErrs
@@ -56,7 +58,7 @@ func ValidateInitConfiguration(c *kubeadm.InitConfiguration) field.ErrorList {
5658
// ValidateClusterConfiguration validates an ClusterConfiguration object and collects all encountered errors
5759
func ValidateClusterConfiguration(c *kubeadm.ClusterConfiguration) field.ErrorList {
5860
allErrs := field.ErrorList{}
59-
allErrs = append(allErrs, ValidateNetworking(&c.Networking, field.NewPath("networking"))...)
61+
allErrs = append(allErrs, ValidateNetworking(c, field.NewPath("networking"))...)
6062
allErrs = append(allErrs, ValidateAPIServer(&c.APIServer, field.NewPath("apiServer"))...)
6163
allErrs = append(allErrs, ValidateAbsolutePath(c.CertificatesDir, field.NewPath("certificatesDir"))...)
6264
allErrs = append(allErrs, ValidateFeatureGates(c.FeatureGates, field.NewPath("featureGates"))...)
@@ -369,30 +371,53 @@ func ValidateHostPort(endpoint string, fldPath *field.Path) field.ErrorList {
369371
}
370372

371373
// ValidateIPNetFromString validates network portion of ip address
372-
func ValidateIPNetFromString(subnet string, minAddrs int64, fldPath *field.Path) field.ErrorList {
374+
func ValidateIPNetFromString(subnetStr string, minAddrs int64, isDualStack bool, fldPath *field.Path) field.ErrorList {
373375
allErrs := field.ErrorList{}
374-
_, svcSubnet, err := net.ParseCIDR(subnet)
375-
if err != nil {
376-
allErrs = append(allErrs, field.Invalid(fldPath, subnet, "couldn't parse subnet"))
377-
return allErrs
378-
}
379-
numAddresses := ipallocator.RangeSize(svcSubnet)
380-
if numAddresses < minAddrs {
381-
allErrs = append(allErrs, field.Invalid(fldPath, subnet, "subnet is too small"))
376+
if isDualStack {
377+
subnets, err := utilnet.ParseCIDRs(strings.Split(subnetStr, ","))
378+
if err != nil {
379+
allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, err.Error()))
380+
} else {
381+
areDualStackCIDRs, err := utilnet.IsDualStackCIDRs(subnets)
382+
if err != nil {
383+
allErrs = append(allErrs, field.Invalid(fldPath, subnets, err.Error()))
384+
} else if !areDualStackCIDRs {
385+
allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, "expected at least one IP from each family (v4 or v6) for dual-stack networking"))
386+
}
387+
for _, s := range subnets {
388+
numAddresses := ipallocator.RangeSize(s)
389+
if numAddresses < minAddrs {
390+
allErrs = append(allErrs, field.Invalid(fldPath, s, "subnet is too small"))
391+
}
392+
}
393+
}
394+
} else {
395+
_, svcSubnet, err := net.ParseCIDR(subnetStr)
396+
if err != nil {
397+
allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, "couldn't parse subnet"))
398+
return allErrs
399+
}
400+
numAddresses := ipallocator.RangeSize(svcSubnet)
401+
if numAddresses < minAddrs {
402+
allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, "subnet is too small"))
403+
}
382404
}
383405
return allErrs
384406
}
385407

386408
// ValidateNetworking validates networking configuration
387-
func ValidateNetworking(c *kubeadm.Networking, fldPath *field.Path) field.ErrorList {
409+
func ValidateNetworking(c *kubeadm.ClusterConfiguration, fldPath *field.Path) field.ErrorList {
388410
allErrs := field.ErrorList{}
389411
dnsDomainFldPath := field.NewPath("dnsDomain")
390-
for _, err := range validation.IsDNS1123Subdomain(c.DNSDomain) {
391-
allErrs = append(allErrs, field.Invalid(dnsDomainFldPath, c.DNSDomain, err))
412+
for _, err := range validation.IsDNS1123Subdomain(c.Networking.DNSDomain) {
413+
allErrs = append(allErrs, field.Invalid(dnsDomainFldPath, c.Networking.DNSDomain, err))
392414
}
393-
allErrs = append(allErrs, ValidateIPNetFromString(c.ServiceSubnet, constants.MinimumAddressesInServiceSubnet, field.NewPath("serviceSubnet"))...)
394-
if len(c.PodSubnet) != 0 {
395-
allErrs = append(allErrs, ValidateIPNetFromString(c.PodSubnet, constants.MinimumAddressesInServiceSubnet, field.NewPath("podSubnet"))...)
415+
// check if dual-stack feature-gate is enabled
416+
isDualStack := features.Enabled(c.FeatureGates, features.IPv6DualStack)
417+
// TODO(Arvinderpal): use isDualStack flag once list of service CIDRs is supported (PR: #79386)
418+
allErrs = append(allErrs, ValidateIPNetFromString(c.Networking.ServiceSubnet, constants.MinimumAddressesInServiceSubnet, false /*isDualStack*/, field.NewPath("serviceSubnet"))...)
419+
if len(c.Networking.PodSubnet) != 0 {
420+
allErrs = append(allErrs, ValidateIPNetFromString(c.Networking.PodSubnet, constants.MinimumAddressesInServiceSubnet, isDualStack, field.NewPath("podSubnet"))...)
396421
}
397422
return allErrs
398423
}
@@ -455,7 +480,6 @@ func ValidateFeatureGates(featureGates map[string]bool, fldPath *field.Path) fie
455480
fmt.Sprintf("%s is not a valid feature name.", k)))
456481
}
457482
}
458-
459483
return allErrs
460484
}
461485

cmd/kubeadm/app/apis/kubeadm/validation/validation_test.go

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -194,29 +194,43 @@ func TestValidateIPFromString(t *testing.T) {
194194

195195
func TestValidateIPNetFromString(t *testing.T) {
196196
var tests = []struct {
197-
name string
198-
subnet string
199-
minaddrs int64
200-
expected bool
197+
name string
198+
subnet string
199+
minaddrs int64
200+
checkDualStack bool
201+
expected bool
201202
}{
202-
{"invalid missing CIDR", "", 0, false},
203-
{"invalid CIDR missing decimal points in IPv4 address and / mask", "1234", 0, false},
204-
{"invalid CIDR use of letters instead of numbers and / mask", "abc", 0, false},
205-
{"invalid IPv4 address provided instead of CIDR representation", "1.2.3.4", 0, false},
206-
{"invalid IPv6 address provided instead of CIDR representation", "2001:db8::1", 0, false},
207-
{"valid, but IPv4 CIDR too small. At least 10 addresses needed", "10.0.0.16/29", 10, false},
208-
{"valid, but IPv6 CIDR too small. At least 10 addresses needed", "2001:db8::/125", 10, false},
209-
{"valid IPv4 CIDR", "10.0.0.16/12", 10, true},
210-
{"valid IPv6 CIDR", "2001:db8::/98", 10, true},
203+
{"invalid missing CIDR", "", 0, false, false},
204+
{"invalid CIDR missing decimal points in IPv4 address and / mask", "1234", 0, false, false},
205+
{"invalid CIDR use of letters instead of numbers and / mask", "abc", 0, false, false},
206+
{"invalid IPv4 address provided instead of CIDR representation", "1.2.3.4", 0, false, false},
207+
{"invalid IPv6 address provided instead of CIDR representation", "2001:db8::1", 0, false, false},
208+
{"valid, but IPv4 CIDR too small. At least 10 addresses needed", "10.0.0.16/29", 10, false, false},
209+
{"valid, but IPv6 CIDR too small. At least 10 addresses needed", "2001:db8::/125", 10, false, false},
210+
{"valid IPv4 CIDR", "10.0.0.16/12", 10, false, true},
211+
{"valid IPv6 CIDR", "2001:db8::/98", 10, false, true},
212+
// dual-stack:
213+
{"invalid missing CIDR", "", 0, true, false},
214+
{"invalid only an IPv4 CIDR specified", "10.0.0.16/12", 10, true, false},
215+
{"invalid only an IPv6 CIDR specified", "2001:db8::/98", 10, true, false},
216+
{"invalid IPv4 address provided instead of CIDR representation", "1.2.3.4,2001:db8::/98", 0, true, false},
217+
{"invalid IPv6 address provided instead of CIDR representation", "2001:db8::1,10.0.0.16/12", 0, true, false},
218+
{"valid, but IPv4 CIDR too small. At least 10 addresses needed", "10.0.0.16/29,2001:db8::/98", 10, true, false},
219+
{"valid, but IPv6 CIDR too small. At least 10 addresses needed", "10.0.0.16/12,2001:db8::/125", 10, true, false},
220+
{"valid, but only IPv4 family addresses specified. IPv6 CIDR is necessary.", "10.0.0.16/12,192.168.0.0/16", 10, true, false},
221+
{"valid, but only IPv6 family addresses specified. IPv4 CIDR is necessary.", "2001:db8::/98,2005:db8::/98", 10, true, false},
222+
{"valid IPv4 and IPv6 CIDR", "10.0.0.16/12,2001:db8::/98", 10, true, true},
223+
{"valid IPv6 and IPv4 CIDR", "10.0.0.16/12,2001:db8::/98", 10, true, true},
211224
}
212225
for _, rt := range tests {
213-
actual := ValidateIPNetFromString(rt.subnet, rt.minaddrs, nil)
226+
actual := ValidateIPNetFromString(rt.subnet, rt.minaddrs, rt.checkDualStack, nil)
214227
if (len(actual) == 0) != rt.expected {
215228
t.Errorf(
216-
"%s test case failed :\n\texpected: %t\n\t actual: %t",
229+
"%s test case failed :\n\texpected: %t\n\t actual: %t\n\t err(s): %v\n\t",
217230
rt.name,
218231
rt.expected,
219232
(len(actual) == 0),
233+
actual,
220234
)
221235
}
222236
}

cmd/kubeadm/app/phases/controlplane/manifests.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,7 @@ func getControllerManagerCommand(cfg *kubeadmapi.ClusterConfiguration) []string
292292
// Let the controller-manager allocate Node CIDRs for the Pod network.
293293
// Each node will get a subspace of the address CIDR provided with --pod-network-cidr.
294294
if cfg.Networking.PodSubnet != "" {
295+
// TODO(Arvinderpal): Needs to be fixed once PR #73977 lands. Should be a list of maskSizes.
295296
maskSize := calcNodeCidrSize(cfg.Networking.PodSubnet)
296297
defaultArguments["allocate-node-cidrs"] = "true"
297298
defaultArguments["cluster-cidr"] = cfg.Networking.PodSubnet

cmd/kubeadm/app/preflight/checks.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -908,7 +908,11 @@ func RunInitNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.InitConfigura
908908
FileAvailableCheck{Path: kubeadmconstants.GetStaticPodFilepath(kubeadmconstants.Etcd, manifestsDir)},
909909
HTTPProxyCheck{Proto: "https", Host: cfg.LocalAPIEndpoint.AdvertiseAddress},
910910
HTTPProxyCIDRCheck{Proto: "https", CIDR: cfg.Networking.ServiceSubnet},
911-
HTTPProxyCIDRCheck{Proto: "https", CIDR: cfg.Networking.PodSubnet},
911+
}
912+
913+
cidrs := strings.Split(cfg.Networking.PodSubnet, ",")
914+
for _, cidr := range cidrs {
915+
checks = append(checks, HTTPProxyCIDRCheck{Proto: "https", CIDR: cidr})
912916
}
913917

914918
if !isSecondaryControlPlane {

0 commit comments

Comments
 (0)