kubeadm validate maximum service subnet size

Validate that the maximum service subnet size doesn't exceed the
limits.

Co-authored-by: Arvinderpal Wander <awander@gmail.com>
This commit is contained in:
Antonio Ojea 2020-10-21 09:02:59 +02:00
parent 8b52995d32
commit 7fc6b4157b
3 changed files with 59 additions and 1 deletions

View File

@ -403,6 +403,25 @@ func ValidateIPNetFromString(subnetStr string, minAddrs int64, isDualStack bool,
return allErrs
}
// ValidateServiceSubnetSize validates that the maximum subnet size is not exceeded
// Should be a small cidr due to how it is stored in etcd.
// bigger cidr (specially those offered by IPv6) will add no value
// and significantly increase snapshotting time.
// NOTE: This is identical to validation performed in the apiserver.
func ValidateServiceSubnetSize(subnetStr string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
// subnets were already validated
subnets, _ := utilnet.ParseCIDRs(strings.Split(subnetStr, ","))
for _, serviceSubnet := range subnets {
ones, bits := serviceSubnet.Mask.Size()
if bits-ones > constants.MaximumBitsForServiceSubnet {
errMsg := fmt.Sprintf("specified service subnet is too large; for %d-bit addresses, the mask must be >= %d", bits, bits-constants.MaximumBitsForServiceSubnet)
allErrs = append(allErrs, field.Invalid(fldPath, serviceSubnet.String(), errMsg))
}
}
return allErrs
}
// ValidatePodSubnetNodeMask validates that the relation between podSubnet and node-masks is correct
func ValidatePodSubnetNodeMask(subnetStr string, c *kubeadm.ClusterConfiguration, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
@ -468,6 +487,8 @@ func ValidateNetworking(c *kubeadm.ClusterConfiguration, fldPath *field.Path) fi
if len(c.Networking.ServiceSubnet) != 0 {
allErrs = append(allErrs, ValidateIPNetFromString(c.Networking.ServiceSubnet, constants.MinimumAddressesInServiceSubnet, isDualStack, field.NewPath("serviceSubnet"))...)
// Service subnet was already validated, we need to validate now the subnet size
allErrs = append(allErrs, ValidateServiceSubnetSize(c.Networking.ServiceSubnet, field.NewPath("serviceSubnet"))...)
}
if len(c.Networking.PodSubnet) != 0 {
allErrs = append(allErrs, ValidateIPNetFromString(c.Networking.PodSubnet, constants.MinimumAddressesInPodSubnet, isDualStack, field.NewPath("podSubnet"))...)

View File

@ -287,6 +287,37 @@ func TestValidatePodSubnetNodeMask(t *testing.T) {
}
}
func TestValidateServiceSubnetSize(t *testing.T) {
var tests = []struct {
name string
subnet string
expected bool
}{
{"single IPv4, but mask too large.", "10.0.0.16/2", false},
{"single IPv6, but mask too large.", "2001:db8::1/64", false},
{"single IPv4 CIDR", "10.0.0.16/12", true},
{"single IPv6 CIDR", "2001:db8::/112", true},
// dual-stack:
{"dual, but IPv4 mask too large.", "2001:db8::1/112,10.0.0.16/6", false},
{"dual, but IPv6 mask too large.", "2001:db8::1/12,10.0.0.16/16", false},
{"dual IPv4 IPv6", "10.0.0.16/12,2001:db8::/112", true},
{"dual IPv6 IPv4", "2001:db8::/112,10.0.0.16/12", true},
}
for _, rt := range tests {
actual := ValidateServiceSubnetSize(rt.subnet, nil)
if (len(actual) == 0) != rt.expected {
t.Errorf(
"%s test case failed :\n\texpected: %t\n\t actual: %t\n\t err(s): %v\n\t",
rt.name,
rt.expected,
(len(actual) == 0),
actual,
)
}
}
}
func TestValidateHostPort(t *testing.T) {
var tests = []struct {
name string
@ -521,7 +552,7 @@ func TestValidateInitConfiguration(t *testing.T) {
},
},
Networking: kubeadm.Networking{
ServiceSubnet: "2001:db8::1/98",
ServiceSubnet: "2001:db8::1/112",
DNSDomain: "cluster.local",
},
CertificatesDir: "/some/other/cert/dir",

View File

@ -199,6 +199,12 @@ const (
// We need at least ten, because the DNS service is always at the tenth cluster clusterIP
MinimumAddressesInServiceSubnet = 10
// MaximumBitsForServiceSubnet defines maximum possible size of the service subnet in terms of bits.
// For example, if the value is 20, then the largest supported service subnet is /12 for IPv4 and /108 for IPv6.
// Note however that anything in between /108 and /112 will be clamped to /112 due to the limitations of the underlying allocation logic.
// TODO: https://github.com/kubernetes/enhancements/pull/1881
MaximumBitsForServiceSubnet = 20
// MinimumAddressesInPodSubnet defines minimum amount of pods in the cluster.
// We need at least more than services, an IPv4 /28 or IPv6 /128 subnet means 14 util addresses
MinimumAddressesInPodSubnet = 14