Skip to content

Commit e6d4273

Browse files
authored
Merge pull request kubernetes#79033 from Nordix/kubeadm-ds-pod-network-cidr
Dual-Stack Integration with Kubeadm
2 parents e283836 + 3ac7ae6 commit e6d4273

File tree

9 files changed

+92
-37
lines changed

9 files changed

+92
-37
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/componentconfigs/validation_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ func TestValidateKubeProxyConfiguration(t *testing.T) {
196196
},
197197
},
198198
},
199-
msg: "must be a valid CIDR block (e.g. 10.100.0.0/16)",
199+
msg: "must be a valid CIDR block (e.g. 10.100.0.0/16 or FD02::0:0:0/96)",
200200
expectErr: true,
201201
},
202202
{

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,7 @@ func getControllerManagerCommand(cfg *kubeadmapi.ClusterConfiguration) []string
301301
// Let the controller-manager allocate Node CIDRs for the Pod network.
302302
// Each node will get a subspace of the address CIDR provided with --pod-network-cidr.
303303
if cfg.Networking.PodSubnet != "" {
304+
// TODO(Arvinderpal): Needs to be fixed once PR #73977 lands. Should be a list of maskSizes.
304305
maskSize := calcNodeCidrSize(cfg.Networking.PodSubnet)
305306
defaultArguments["allocate-node-cidrs"] = "true"
306307
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
@@ -901,7 +901,11 @@ func RunInitNodeChecks(execer utilsexec.Interface, cfg *kubeadmapi.InitConfigura
901901
FileAvailableCheck{Path: kubeadmconstants.GetStaticPodFilepath(kubeadmconstants.Etcd, manifestsDir)},
902902
HTTPProxyCheck{Proto: "https", Host: cfg.LocalAPIEndpoint.AdvertiseAddress},
903903
HTTPProxyCIDRCheck{Proto: "https", CIDR: cfg.Networking.ServiceSubnet},
904-
HTTPProxyCIDRCheck{Proto: "https", CIDR: cfg.Networking.PodSubnet},
904+
}
905+
906+
cidrs := strings.Split(cfg.Networking.PodSubnet, ",")
907+
for _, cidr := range cidrs {
908+
checks = append(checks, HTTPProxyCIDRCheck{Proto: "https", CIDR: cidr})
905909
}
906910

907911
if !isSecondaryControlPlane {

pkg/proxy/apis/config/validation/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ go_library(
1212
importpath = "k8s.io/kubernetes/pkg/proxy/apis/config/validation",
1313
deps = [
1414
"//pkg/apis/core/validation:go_default_library",
15+
"//pkg/features:go_default_library",
1516
"//pkg/proxy/apis/config:go_default_library",
1617
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
1718
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",

pkg/proxy/apis/config/validation/validation.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"k8s.io/apimachinery/pkg/util/validation/field"
2929
componentbaseconfig "k8s.io/component-base/config"
3030
apivalidation "k8s.io/kubernetes/pkg/apis/core/validation"
31+
kubefeatures "k8s.io/kubernetes/pkg/features"
3132
kubeproxyconfig "k8s.io/kubernetes/pkg/proxy/apis/config"
3233
)
3334

@@ -67,8 +68,17 @@ func Validate(config *kubeproxyconfig.KubeProxyConfiguration) field.ErrorList {
6768
allErrs = append(allErrs, validateHostPort(config.MetricsBindAddress, newPath.Child("MetricsBindAddress"))...)
6869

6970
if config.ClusterCIDR != "" {
70-
if _, _, err := net.ParseCIDR(config.ClusterCIDR); err != nil {
71-
allErrs = append(allErrs, field.Invalid(newPath.Child("ClusterCIDR"), config.ClusterCIDR, "must be a valid CIDR block (e.g. 10.100.0.0/16)"))
71+
if config.FeatureGates[string(kubefeatures.IPv6DualStack)] {
72+
cidrs := strings.Split(config.ClusterCIDR, ",")
73+
for _, cidr := range cidrs {
74+
if _, _, err := net.ParseCIDR(cidr); err != nil {
75+
allErrs = append(allErrs, field.Invalid(newPath.Child("ClusterCIDR"), cidr, "must be a valid CIDR block (e.g. 10.100.0.0/16 or FD02::0:0:0/96)"))
76+
}
77+
}
78+
} else {
79+
if _, _, err := net.ParseCIDR(config.ClusterCIDR); err != nil {
80+
allErrs = append(allErrs, field.Invalid(newPath.Child("ClusterCIDR"), config.ClusterCIDR, "must be a valid CIDR block (e.g. 10.100.0.0/16 or FD02::0:0:0/96)"))
81+
}
7282
}
7383
}
7484

pkg/proxy/apis/config/validation/validation_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,7 @@ func TestValidateKubeProxyConfiguration(t *testing.T) {
202202
TCPCloseWaitTimeout: &metav1.Duration{Duration: 5 * time.Second},
203203
},
204204
},
205-
msg: "must be a valid CIDR block (e.g. 10.100.0.0/16)",
205+
msg: "must be a valid CIDR block (e.g. 10.100.0.0/16 or FD02::0:0:0/96)",
206206
},
207207
{
208208
config: kubeproxyconfig.KubeProxyConfiguration{

0 commit comments

Comments
 (0)