kubeadm: dual-stack validation allow single stack

It turns out that the dual-stack feature enabled doesn't mean that
the cluster MUST be dual-stack, it only indicates that it MAY be
dual-stack but CAN be single-stack.

We should relax the validation to allow single-stack clusters
with dual-stack enabled.
This commit is contained in:
Antonio Ojea 2020-01-22 10:25:14 +01:00
parent 1735f7a2b6
commit 6dda7adaf5
No known key found for this signature in database
GPG Key ID: E4833AA228D4E824
2 changed files with 29 additions and 25 deletions

View File

@ -372,33 +372,32 @@ func ValidateHostPort(endpoint string, fldPath *field.Path) field.ErrorList {
// ValidateIPNetFromString validates network portion of ip address
func ValidateIPNetFromString(subnetStr string, minAddrs int64, isDualStack bool, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if isDualStack {
subnets, err := utilnet.ParseCIDRs(strings.Split(subnetStr, ","))
subnets, err := utilnet.ParseCIDRs(strings.Split(subnetStr, ","))
if err != nil {
allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, "couldn't parse subnet"))
return allErrs
}
switch {
// if DualStack only 2 CIDRs allowed
case isDualStack && len(subnets) > 2:
allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, "expected one (IPv4 or IPv6) CIDR or two CIDRs from each family for dual-stack networking"))
// if DualStack and there are 2 CIDRs validate if there is at least one of each IP family
case isDualStack && len(subnets) == 2:
areDualStackCIDRs, err := utilnet.IsDualStackCIDRs(subnets)
if err != nil {
allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, err.Error()))
} else {
areDualStackCIDRs, err := utilnet.IsDualStackCIDRs(subnets)
if err != nil {
allErrs = append(allErrs, field.Invalid(fldPath, subnets, err.Error()))
} else if !areDualStackCIDRs {
allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, "expected at least one IP from each family (v4 or v6) for dual-stack networking"))
}
for _, s := range subnets {
numAddresses := utilnet.RangeSize(s)
if numAddresses < minAddrs {
allErrs = append(allErrs, field.Invalid(fldPath, s, "subnet is too small"))
}
}
} else if !areDualStackCIDRs {
allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, "expected one (IPv4 or IPv6) CIDR or two CIDRs from each family for dual-stack networking"))
}
} else {
_, svcSubnet, err := net.ParseCIDR(subnetStr)
if err != nil {
allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, "couldn't parse subnet"))
return allErrs
}
numAddresses := utilnet.RangeSize(svcSubnet)
// if not DualStack only one CIDR allowed
case !isDualStack && len(subnets) > 1:
allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, "only one CIDR allowed for single-stack networking"))
}
// validate the subnet/s
for _, s := range subnets {
numAddresses := utilnet.RangeSize(s)
if numAddresses < minAddrs {
allErrs = append(allErrs, field.Invalid(fldPath, subnetStr, "subnet is too small"))
allErrs = append(allErrs, field.Invalid(fldPath, s, "subnet is too small"))
}
}
return allErrs

View File

@ -197,18 +197,21 @@ func TestValidateIPNetFromString(t *testing.T) {
expected bool
}{
{"invalid missing CIDR", "", 0, false, false},
{"invalid CIDR", "a", 0, false, false},
{"invalid CIDR missing decimal points in IPv4 address and / mask", "1234", 0, false, false},
{"invalid CIDR use of letters instead of numbers and / mask", "abc", 0, false, false},
{"invalid IPv4 address provided instead of CIDR representation", "1.2.3.4", 0, false, false},
{"invalid IPv6 address provided instead of CIDR representation", "2001:db8::1", 0, false, false},
{"invalid multiple CIDR provided in a single stack cluster", "2001:db8::1/64,1.2.3.4/24", 0, false, false},
{"invalid multiple CIDR provided in a single stack cluster and one invalid subnet", "2001:db8::1/64,a", 0, false, false},
{"valid, but IPv4 CIDR too small. At least 10 addresses needed", "10.0.0.16/29", 10, false, false},
{"valid, but IPv6 CIDR too small. At least 10 addresses needed", "2001:db8::/125", 10, false, false},
{"valid IPv4 CIDR", "10.0.0.16/12", 10, false, true},
{"valid IPv6 CIDR", "2001:db8::/98", 10, false, true},
// dual-stack:
{"invalid missing CIDR", "", 0, true, false},
{"invalid only an IPv4 CIDR specified", "10.0.0.16/12", 10, true, false},
{"invalid only an IPv6 CIDR specified", "2001:db8::/98", 10, true, false},
{"valid dual-stack enabled but only an IPv4 CIDR specified", "10.0.0.16/12", 10, true, true},
{"valid dual-stack enabled but only an IPv6 CIDR specified", "2001:db8::/98", 10, true, true},
{"invalid IPv4 address provided instead of CIDR representation", "1.2.3.4,2001:db8::/98", 0, true, false},
{"invalid IPv6 address provided instead of CIDR representation", "2001:db8::1,10.0.0.16/12", 0, true, false},
{"valid, but IPv4 CIDR too small. At least 10 addresses needed", "10.0.0.16/29,2001:db8::/98", 10, true, false},
@ -217,6 +220,8 @@ func TestValidateIPNetFromString(t *testing.T) {
{"valid, but only IPv6 family addresses specified. IPv4 CIDR is necessary.", "2001:db8::/98,2005:db8::/98", 10, true, false},
{"valid IPv4 and IPv6 CIDR", "10.0.0.16/12,2001:db8::/98", 10, true, true},
{"valid IPv6 and IPv4 CIDR", "10.0.0.16/12,2001:db8::/98", 10, true, true},
{"invalid IPv6 and IPv4 CIDR with more than 2 subnets", "10.0.0.16/12,2001:db8::/98,192.168.0.0/16", 10, true, false},
{"invalid IPv6 and IPv4 CIDR with more than 2 subnets", "10.0.0.16/12,2001:db8::/98,192.168.0.0/16,a.b.c.d/24", 10, true, false},
}
for _, rt := range tests {
actual := ValidateIPNetFromString(rt.subnet, rt.minaddrs, rt.checkDualStack, nil)