Introduce APIs to support multiple ClusterCIDRs (#108290)

* Introduce networking/v1alpha1 api, ClusterCIDRConfig type

Introduce networking/v1alpha1 api group.

Add `ClusterCIDRConfig` type to networking/v1alpha1 api group, this type
will enable the NodeIPAM controller to support multiple ClusterCIDRs.

* Change ClusterCIDRConfig.NodeSelector type in api

* Fix review comments for API

* Update ClusterCIDRConfig API Spec

Introduce PerNodeHostBits field, remove PerNodeMaskSize
This commit is contained in:
Sarvesh Rangnekar
2022-03-30 19:39:00 -07:00
committed by GitHub
parent 8b158fa730
commit b9792a9dae
99 changed files with 8758 additions and 1 deletions

View File

@@ -370,3 +370,62 @@ func ScopedResourceSelectorRequirementsAsSelector(ssr v1.ScopedResourceSelectorR
selector = selector.Add(*r)
return selector, nil
}
// NodeSelectorRequirementsAsLabelRequirements converts the NodeSelectorRequirement
// type to a labels.Requirement type.
func NodeSelectorRequirementsAsLabelRequirements(nsr v1.NodeSelectorRequirement) (*labels.Requirement, error) {
var op selection.Operator
switch nsr.Operator {
case v1.NodeSelectorOpIn:
op = selection.In
case v1.NodeSelectorOpNotIn:
op = selection.NotIn
case v1.NodeSelectorOpExists:
op = selection.Exists
case v1.NodeSelectorOpDoesNotExist:
op = selection.DoesNotExist
case v1.NodeSelectorOpGt:
op = selection.GreaterThan
case v1.NodeSelectorOpLt:
op = selection.LessThan
default:
return nil, fmt.Errorf("%q is not a valid node selector operator", nsr.Operator)
}
return labels.NewRequirement(nsr.Key, op, nsr.Values)
}
// NodeSelectorAsSelector converts the NodeSelector api type into a struct that
// implements labels.Selector
// Note: This function should be kept in sync with the selector methods in
// pkg/labels/selector.go
func NodeSelectorAsSelector(ns *v1.NodeSelector) (labels.Selector, error) {
if ns == nil {
return labels.Nothing(), nil
}
if len(ns.NodeSelectorTerms) == 0 {
return labels.Everything(), nil
}
requirements := make([]labels.Requirement, 0)
for _, nsTerm := range ns.NodeSelectorTerms {
for _, expr := range nsTerm.MatchExpressions {
req, err := NodeSelectorRequirementsAsLabelRequirements(expr)
if err != nil {
return nil, err
}
requirements = append(requirements, *req)
}
for _, field := range nsTerm.MatchFields {
req, err := NodeSelectorRequirementsAsLabelRequirements(field)
if err != nil {
return nil, err
}
requirements = append(requirements, *req)
}
}
selector := labels.NewSelector()
selector = selector.Add(requirements...)
return selector, nil
}

View File

@@ -24,6 +24,7 @@ import (
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/networking"
"k8s.io/kubernetes/pkg/apis/networking/v1"
"k8s.io/kubernetes/pkg/apis/networking/v1alpha1"
"k8s.io/kubernetes/pkg/apis/networking/v1beta1"
)
@@ -36,5 +37,6 @@ func Install(scheme *runtime.Scheme) {
utilruntime.Must(networking.AddToScheme(scheme))
utilruntime.Must(v1.AddToScheme(scheme))
utilruntime.Must(v1beta1.AddToScheme(scheme))
utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta1.SchemeGroupVersion))
utilruntime.Must(v1alpha1.AddToScheme(scheme))
utilruntime.Must(scheme.SetVersionPriority(v1.SchemeGroupVersion, v1beta1.SchemeGroupVersion, v1alpha1.SchemeGroupVersion))
}

View File

@@ -52,6 +52,8 @@ func addKnownTypes(scheme *runtime.Scheme) error {
&IngressList{},
&IngressClass{},
&IngressClassList{},
&ClusterCIDRConfig{},
&ClusterCIDRConfigList{},
)
return nil
}

View File

@@ -585,3 +585,61 @@ type ServiceBackendPort struct {
// +optional
Number int32
}
// +genclient
// +genclient:nonNamespaced
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ClusterCIDRConfig is the Schema for the clustercidrconfigs API.
type ClusterCIDRConfig struct {
metav1.TypeMeta
metav1.ObjectMeta
Spec ClusterCIDRConfigSpec
Status ClusterCIDRConfigStatus
}
// ClusterCIDRConfigSpec defines the desired state of ClusterCIDRConfig.
type ClusterCIDRConfigSpec struct {
// NodeSelector defines which nodes the config is applicable to.
// An empty or nil NodeSelector functions as a default that applies to all nodes.
// This field is immutable.
// +optional
NodeSelector *api.NodeSelector
// PerNodeHostBits defines the number of host bits to be configured per node.
// A subnet mask determines how much of the address is used for network bits
// and host bits. For example and IPv4 address of 192.168.0.0/24, splits the
// address into 24 bits for the network portion and 8 bits for the host portion.
// For a /24 mask for IPv4 or a /120 for IPv6, configure PerNodeHostBits=8
// This field is immutable.
// +optional
PerNodeHostBits int32
// IPv4CIDR defines an IPv4 IP block in CIDR notation(e.g. "10.0.0.0/8").
// This field is immutable.
// +optional
IPv4CIDR string
// IPv6CIDR defines an IPv6 IP block in CIDR notation(e.g. "fd12:3456:789a:1::/64").
// This field is immutable.
// +optional
IPv6CIDR string
}
// ClusterCIDRConfigStatus defines the observed state of ClusterCIDRConfig.
type ClusterCIDRConfigStatus struct {
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
// ClusterCIDRConfigList contains a list of ClusterCIDRConfigs.
type ClusterCIDRConfigList struct {
metav1.TypeMeta
// +optional
metav1.ListMeta
// Items is the list of ClusterCIDRConfigs.
Items []ClusterCIDRConfig
}

View File

@@ -0,0 +1,25 @@
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
"k8s.io/apimachinery/pkg/runtime"
)
func addDefaultingFuncs(scheme *runtime.Scheme) error {
return RegisterDefaults(scheme)
}

View File

@@ -0,0 +1,24 @@
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/networking
// +k8s:conversion-gen-external-types=k8s.io/api/networking/v1alpha1
// +k8s:conversion-gen=k8s.io/kubernetes/pkg/apis/extensions
// +k8s:defaulter-gen=TypeMeta
// +k8s:defaulter-gen-input=k8s.io/api/networking/v1alpha1
// +groupName=networking.k8s.io
package v1alpha1 // import "k8s.io/kubernetes/pkg/apis/networking/v1alpha1"

View File

@@ -0,0 +1,45 @@
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1alpha1
import (
networkingv1alpha1 "k8s.io/api/networking/v1alpha1"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// GroupName is the group name use in this package.
const GroupName = "networking.k8s.io"
// SchemeGroupVersion is group version used to register these objects.
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1alpha1"}
// Resource takes an unqualified resource and returns a Group qualified GroupResource.
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
localSchemeBuilder = &networkingv1alpha1.SchemeBuilder
AddToScheme = localSchemeBuilder.AddToScheme
)
func init() {
// We only register manually written functions here. The registration of the
// generated functions takes place in the generated files. The separation
// makes the code compile even when the generated files are missing.
localSchemeBuilder.Register(addDefaultingFuncs)
}

View File

@@ -0,0 +1,181 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by conversion-gen. DO NOT EDIT.
package v1alpha1
import (
unsafe "unsafe"
v1 "k8s.io/api/core/v1"
v1alpha1 "k8s.io/api/networking/v1alpha1"
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
core "k8s.io/kubernetes/pkg/apis/core"
networking "k8s.io/kubernetes/pkg/apis/networking"
)
func init() {
localSchemeBuilder.Register(RegisterConversions)
}
// RegisterConversions adds conversion functions to the given scheme.
// Public to allow building arbitrary schemes.
func RegisterConversions(s *runtime.Scheme) error {
if err := s.AddGeneratedConversionFunc((*v1alpha1.ClusterCIDRConfig)(nil), (*networking.ClusterCIDRConfig)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_ClusterCIDRConfig_To_networking_ClusterCIDRConfig(a.(*v1alpha1.ClusterCIDRConfig), b.(*networking.ClusterCIDRConfig), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*networking.ClusterCIDRConfig)(nil), (*v1alpha1.ClusterCIDRConfig)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_networking_ClusterCIDRConfig_To_v1alpha1_ClusterCIDRConfig(a.(*networking.ClusterCIDRConfig), b.(*v1alpha1.ClusterCIDRConfig), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.ClusterCIDRConfigList)(nil), (*networking.ClusterCIDRConfigList)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_ClusterCIDRConfigList_To_networking_ClusterCIDRConfigList(a.(*v1alpha1.ClusterCIDRConfigList), b.(*networking.ClusterCIDRConfigList), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*networking.ClusterCIDRConfigList)(nil), (*v1alpha1.ClusterCIDRConfigList)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_networking_ClusterCIDRConfigList_To_v1alpha1_ClusterCIDRConfigList(a.(*networking.ClusterCIDRConfigList), b.(*v1alpha1.ClusterCIDRConfigList), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.ClusterCIDRConfigSpec)(nil), (*networking.ClusterCIDRConfigSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_ClusterCIDRConfigSpec_To_networking_ClusterCIDRConfigSpec(a.(*v1alpha1.ClusterCIDRConfigSpec), b.(*networking.ClusterCIDRConfigSpec), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*networking.ClusterCIDRConfigSpec)(nil), (*v1alpha1.ClusterCIDRConfigSpec)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_networking_ClusterCIDRConfigSpec_To_v1alpha1_ClusterCIDRConfigSpec(a.(*networking.ClusterCIDRConfigSpec), b.(*v1alpha1.ClusterCIDRConfigSpec), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*v1alpha1.ClusterCIDRConfigStatus)(nil), (*networking.ClusterCIDRConfigStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_v1alpha1_ClusterCIDRConfigStatus_To_networking_ClusterCIDRConfigStatus(a.(*v1alpha1.ClusterCIDRConfigStatus), b.(*networking.ClusterCIDRConfigStatus), scope)
}); err != nil {
return err
}
if err := s.AddGeneratedConversionFunc((*networking.ClusterCIDRConfigStatus)(nil), (*v1alpha1.ClusterCIDRConfigStatus)(nil), func(a, b interface{}, scope conversion.Scope) error {
return Convert_networking_ClusterCIDRConfigStatus_To_v1alpha1_ClusterCIDRConfigStatus(a.(*networking.ClusterCIDRConfigStatus), b.(*v1alpha1.ClusterCIDRConfigStatus), scope)
}); err != nil {
return err
}
return nil
}
func autoConvert_v1alpha1_ClusterCIDRConfig_To_networking_ClusterCIDRConfig(in *v1alpha1.ClusterCIDRConfig, out *networking.ClusterCIDRConfig, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_v1alpha1_ClusterCIDRConfigSpec_To_networking_ClusterCIDRConfigSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := Convert_v1alpha1_ClusterCIDRConfigStatus_To_networking_ClusterCIDRConfigStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
// Convert_v1alpha1_ClusterCIDRConfig_To_networking_ClusterCIDRConfig is an autogenerated conversion function.
func Convert_v1alpha1_ClusterCIDRConfig_To_networking_ClusterCIDRConfig(in *v1alpha1.ClusterCIDRConfig, out *networking.ClusterCIDRConfig, s conversion.Scope) error {
return autoConvert_v1alpha1_ClusterCIDRConfig_To_networking_ClusterCIDRConfig(in, out, s)
}
func autoConvert_networking_ClusterCIDRConfig_To_v1alpha1_ClusterCIDRConfig(in *networking.ClusterCIDRConfig, out *v1alpha1.ClusterCIDRConfig, s conversion.Scope) error {
out.ObjectMeta = in.ObjectMeta
if err := Convert_networking_ClusterCIDRConfigSpec_To_v1alpha1_ClusterCIDRConfigSpec(&in.Spec, &out.Spec, s); err != nil {
return err
}
if err := Convert_networking_ClusterCIDRConfigStatus_To_v1alpha1_ClusterCIDRConfigStatus(&in.Status, &out.Status, s); err != nil {
return err
}
return nil
}
// Convert_networking_ClusterCIDRConfig_To_v1alpha1_ClusterCIDRConfig is an autogenerated conversion function.
func Convert_networking_ClusterCIDRConfig_To_v1alpha1_ClusterCIDRConfig(in *networking.ClusterCIDRConfig, out *v1alpha1.ClusterCIDRConfig, s conversion.Scope) error {
return autoConvert_networking_ClusterCIDRConfig_To_v1alpha1_ClusterCIDRConfig(in, out, s)
}
func autoConvert_v1alpha1_ClusterCIDRConfigList_To_networking_ClusterCIDRConfigList(in *v1alpha1.ClusterCIDRConfigList, out *networking.ClusterCIDRConfigList, s conversion.Scope) error {
out.ListMeta = in.ListMeta
out.Items = *(*[]networking.ClusterCIDRConfig)(unsafe.Pointer(&in.Items))
return nil
}
// Convert_v1alpha1_ClusterCIDRConfigList_To_networking_ClusterCIDRConfigList is an autogenerated conversion function.
func Convert_v1alpha1_ClusterCIDRConfigList_To_networking_ClusterCIDRConfigList(in *v1alpha1.ClusterCIDRConfigList, out *networking.ClusterCIDRConfigList, s conversion.Scope) error {
return autoConvert_v1alpha1_ClusterCIDRConfigList_To_networking_ClusterCIDRConfigList(in, out, s)
}
func autoConvert_networking_ClusterCIDRConfigList_To_v1alpha1_ClusterCIDRConfigList(in *networking.ClusterCIDRConfigList, out *v1alpha1.ClusterCIDRConfigList, s conversion.Scope) error {
out.ListMeta = in.ListMeta
out.Items = *(*[]v1alpha1.ClusterCIDRConfig)(unsafe.Pointer(&in.Items))
return nil
}
// Convert_networking_ClusterCIDRConfigList_To_v1alpha1_ClusterCIDRConfigList is an autogenerated conversion function.
func Convert_networking_ClusterCIDRConfigList_To_v1alpha1_ClusterCIDRConfigList(in *networking.ClusterCIDRConfigList, out *v1alpha1.ClusterCIDRConfigList, s conversion.Scope) error {
return autoConvert_networking_ClusterCIDRConfigList_To_v1alpha1_ClusterCIDRConfigList(in, out, s)
}
func autoConvert_v1alpha1_ClusterCIDRConfigSpec_To_networking_ClusterCIDRConfigSpec(in *v1alpha1.ClusterCIDRConfigSpec, out *networking.ClusterCIDRConfigSpec, s conversion.Scope) error {
out.NodeSelector = (*core.NodeSelector)(unsafe.Pointer(in.NodeSelector))
out.PerNodeHostBits = in.PerNodeHostBits
out.IPv4CIDR = in.IPv4CIDR
out.IPv6CIDR = in.IPv6CIDR
return nil
}
// Convert_v1alpha1_ClusterCIDRConfigSpec_To_networking_ClusterCIDRConfigSpec is an autogenerated conversion function.
func Convert_v1alpha1_ClusterCIDRConfigSpec_To_networking_ClusterCIDRConfigSpec(in *v1alpha1.ClusterCIDRConfigSpec, out *networking.ClusterCIDRConfigSpec, s conversion.Scope) error {
return autoConvert_v1alpha1_ClusterCIDRConfigSpec_To_networking_ClusterCIDRConfigSpec(in, out, s)
}
func autoConvert_networking_ClusterCIDRConfigSpec_To_v1alpha1_ClusterCIDRConfigSpec(in *networking.ClusterCIDRConfigSpec, out *v1alpha1.ClusterCIDRConfigSpec, s conversion.Scope) error {
out.NodeSelector = (*v1.NodeSelector)(unsafe.Pointer(in.NodeSelector))
out.PerNodeHostBits = in.PerNodeHostBits
out.IPv4CIDR = in.IPv4CIDR
out.IPv6CIDR = in.IPv6CIDR
return nil
}
// Convert_networking_ClusterCIDRConfigSpec_To_v1alpha1_ClusterCIDRConfigSpec is an autogenerated conversion function.
func Convert_networking_ClusterCIDRConfigSpec_To_v1alpha1_ClusterCIDRConfigSpec(in *networking.ClusterCIDRConfigSpec, out *v1alpha1.ClusterCIDRConfigSpec, s conversion.Scope) error {
return autoConvert_networking_ClusterCIDRConfigSpec_To_v1alpha1_ClusterCIDRConfigSpec(in, out, s)
}
func autoConvert_v1alpha1_ClusterCIDRConfigStatus_To_networking_ClusterCIDRConfigStatus(in *v1alpha1.ClusterCIDRConfigStatus, out *networking.ClusterCIDRConfigStatus, s conversion.Scope) error {
return nil
}
// Convert_v1alpha1_ClusterCIDRConfigStatus_To_networking_ClusterCIDRConfigStatus is an autogenerated conversion function.
func Convert_v1alpha1_ClusterCIDRConfigStatus_To_networking_ClusterCIDRConfigStatus(in *v1alpha1.ClusterCIDRConfigStatus, out *networking.ClusterCIDRConfigStatus, s conversion.Scope) error {
return autoConvert_v1alpha1_ClusterCIDRConfigStatus_To_networking_ClusterCIDRConfigStatus(in, out, s)
}
func autoConvert_networking_ClusterCIDRConfigStatus_To_v1alpha1_ClusterCIDRConfigStatus(in *networking.ClusterCIDRConfigStatus, out *v1alpha1.ClusterCIDRConfigStatus, s conversion.Scope) error {
return nil
}
// Convert_networking_ClusterCIDRConfigStatus_To_v1alpha1_ClusterCIDRConfigStatus is an autogenerated conversion function.
func Convert_networking_ClusterCIDRConfigStatus_To_v1alpha1_ClusterCIDRConfigStatus(in *networking.ClusterCIDRConfigStatus, out *v1alpha1.ClusterCIDRConfigStatus, s conversion.Scope) error {
return autoConvert_networking_ClusterCIDRConfigStatus_To_v1alpha1_ClusterCIDRConfigStatus(in, out, s)
}

View File

@@ -0,0 +1,33 @@
//go:build !ignore_autogenerated
// +build !ignore_autogenerated
/*
Copyright The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Code generated by defaulter-gen. DO NOT EDIT.
package v1alpha1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// RegisterDefaults adds defaulters functions to the given scheme.
// Public to allow building arbitrary schemes.
// All generated defaulters are covering - they call all nested defaulters.
func RegisterDefaults(scheme *runtime.Scheme) error {
return nil
}

View File

@@ -602,3 +602,81 @@ func allowInvalidWildcardHostRule(oldIngress *networking.Ingress) bool {
}
return false
}
// ValidateClusterCIDRConfigName validates that the given name can be used as an
// ClusterCIDRConfig name.
var ValidateClusterCIDRConfigName = apimachineryvalidation.NameIsDNSLabel
// ValidateClusterCIDRConfig validates a clusterCIDRConfig.
func ValidateClusterCIDRConfig(ccc *networking.ClusterCIDRConfig) field.ErrorList {
allErrs := apivalidation.ValidateObjectMeta(&ccc.ObjectMeta, false, ValidateClusterCIDRConfigName, field.NewPath("metadata"))
allErrs = append(allErrs, ValidateClusterCIDRConfigSpec(&ccc.Spec, field.NewPath("spec"))...)
return allErrs
}
// ValidateClusterCIDRConfigSpec validates clusterCIDRConfig Spec.
func ValidateClusterCIDRConfigSpec(spec *networking.ClusterCIDRConfigSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if spec.NodeSelector != nil {
allErrs = append(allErrs, apivalidation.ValidateNodeSelector(spec.NodeSelector, fldPath.Child("nodeSelector"))...)
}
// Validate if CIDR is configured for at least one IP Family(IPv4/IPv6).
if spec.IPv4CIDR == "" && spec.IPv6CIDR == "" {
allErrs = append(allErrs, field.Required(fldPath, "one or both of `ipv4` and `ipv6` must be configured"))
}
// Validate configured IPv4 CIDR and PerNodeHostBits.
if spec.IPv4CIDR != "" {
if !netutils.IsIPv4CIDRString(spec.IPv4CIDR) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("ipv4CIDR"), spec.IPv4CIDR, "must be a valid IPv4 CIDR"))
return allErrs
}
allErrs = append(allErrs, validatePerNodeHostBits(spec.IPv4CIDR, spec.PerNodeHostBits, 32, fldPath)...)
}
// Validate configured IPv6 CIDR and PerNodeHostBits.
if spec.IPv6CIDR != "" {
if !netutils.IsIPv6CIDRString(spec.IPv6CIDR) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("ipv6CIDR"), spec.IPv6CIDR, "must be a valid IPv6 CIDR"))
return allErrs
}
allErrs = append(allErrs, validatePerNodeHostBits(spec.IPv6CIDR, spec.PerNodeHostBits, 128, fldPath)...)
}
return allErrs
}
func validatePerNodeHostBits(configCIDR string, perNodeHostBits, maxMaskSize int32, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
minPerNodeHostBits := int32(4)
_, cidr, _ := netutils.ParseCIDRSloppy(configCIDR)
maskSize, _ := cidr.Mask.Size()
maxPerNodeHostBits := maxMaskSize - int32(maskSize)
if perNodeHostBits < minPerNodeHostBits || perNodeHostBits > maxPerNodeHostBits {
allErrs = append(allErrs, field.Invalid(fldPath.Child("perNodeHostBits"), perNodeHostBits, fmt.Sprintf("must be greater than %d and less than or equal to %d", minPerNodeHostBits, maxPerNodeHostBits)))
}
return allErrs
}
// ValidateClusterCIDRConfigUpdate tests if an update to a ClusterCIDRConfig is valid.
func ValidateClusterCIDRConfigUpdate(update, old *networking.ClusterCIDRConfig) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...)
allErrs = append(allErrs, validateClusterCIDRConfigUpdateSpec(&update.Spec, &old.Spec, field.NewPath("spec"))...)
return allErrs
}
func validateClusterCIDRConfigUpdateSpec(update, old *networking.ClusterCIDRConfigSpec, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
allErrs = append(allErrs, apivalidation.ValidateImmutableField(update.NodeSelector, old.NodeSelector, fldPath.Child("nodeSelector"))...)
allErrs = append(allErrs, apivalidation.ValidateImmutableField(update.PerNodeHostBits, old.PerNodeHostBits, fldPath.Child("perNodeHostBits"))...)
allErrs = append(allErrs, apivalidation.ValidateImmutableField(update.IPv4CIDR, old.IPv4CIDR, fldPath.Child("ipv4CIDR"))...)
allErrs = append(allErrs, apivalidation.ValidateImmutableField(update.IPv6CIDR, old.IPv6CIDR, fldPath.Child("ipv6CIDR"))...)
return allErrs
}

View File

@@ -1986,3 +1986,187 @@ func TestValidateIngressStatusUpdate(t *testing.T) {
}
}
}
func makeValidClusterCIDRConfig() *networking.ClusterCIDRConfig {
return &networking.ClusterCIDRConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
ResourceVersion: "9",
},
Spec: networking.ClusterCIDRConfigSpec{
PerNodeHostBits: int32(8),
IPv4CIDR: "10.1.0.0/16",
IPv6CIDR: "fd00:1:1::/64",
NodeSelector: &api.NodeSelector{
NodeSelectorTerms: []api.NodeSelectorTerm{
{
MatchExpressions: []api.NodeSelectorRequirement{
{
Key: "foo",
Operator: api.NodeSelectorOpIn,
Values: []string{"bar"},
},
},
},
},
},
},
}
}
type cccTweak func(ccc *networking.ClusterCIDRConfig)
func makeClusterCIDRConfigCustom(tweaks ...cccTweak) *networking.ClusterCIDRConfig {
ccc := makeValidClusterCIDRConfig()
for _, fn := range tweaks {
fn(ccc)
}
return ccc
}
func makeNodeSelector(key string, op api.NodeSelectorOperator, values []string) *api.NodeSelector {
return &api.NodeSelector{
NodeSelectorTerms: []api.NodeSelectorTerm{
{
MatchExpressions: []api.NodeSelectorRequirement{
{
Key: key,
Operator: op,
Values: values,
},
},
},
},
}
}
func TestValidateClusterCIDRConfig(t *testing.T) {
// Tweaks used below.
setIPv4CIDR := func(perNodeHostBits int32, ipv4CIDR string) cccTweak {
return func(ccc *networking.ClusterCIDRConfig) {
ccc.Spec.IPv4CIDR = ipv4CIDR
ccc.Spec.PerNodeHostBits = perNodeHostBits
}
}
setIPv6CIDR := func(perNodeHostBits int32, ipv6CIDR string) cccTweak {
return func(ccc *networking.ClusterCIDRConfig) {
ccc.Spec.IPv6CIDR = ipv6CIDR
ccc.Spec.PerNodeHostBits = perNodeHostBits
}
}
setNodeSelector := func(nodeSelector *api.NodeSelector) cccTweak {
return func(ccc *networking.ClusterCIDRConfig) {
ccc.Spec.NodeSelector = nodeSelector
}
}
validNodeSelector := makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"})
successCases := map[string]*networking.ClusterCIDRConfig{
"valid IPv6 only ClusterCIDRConfig": makeClusterCIDRConfigCustom(setIPv4CIDR(8, "")),
"valid IPv4 only ClusterCIDRConfig": makeClusterCIDRConfigCustom(setIPv6CIDR(8, "")),
"valid DualStack ClusterCIDRConfig with no NodeSelector": makeClusterCIDRConfigCustom(setNodeSelector(nil)),
"valid NodeSelector": makeClusterCIDRConfigCustom(setNodeSelector(validNodeSelector)),
}
// Success cases are expected to pass validation.
for k, v := range successCases {
if errs := ValidateClusterCIDRConfig(v); len(errs) != 0 {
t.Errorf("Expected success for test '%s', got %v", k, errs)
}
}
invalidNodeSelector := makeNodeSelector("NoUppercaseOrSpecialCharsLike=Equals", api.NodeSelectorOpIn, []string{"bar"})
errorCases := map[string]*networking.ClusterCIDRConfig{
// Config test.
"empty spec.IPv4CIDR and spec.IPv6CIDR": makeClusterCIDRConfigCustom(
setIPv4CIDR(8, ""), setIPv6CIDR(8, "")),
"invalid spec.NodeSelector": makeClusterCIDRConfigCustom(
setNodeSelector(invalidNodeSelector)),
// IPv4 tests.
"invalid spec.IPv4CIDR": makeClusterCIDRConfigCustom(
setIPv4CIDR(8, "test")),
"valid IPv6 CIDR in spec.IPv4CIDR": makeClusterCIDRConfigCustom(
setIPv4CIDR(8, "fd00::/120")),
"invalid spec.PerNodeHostBits with IPv4 CIDR": makeClusterCIDRConfigCustom(
setIPv4CIDR(100, "10.2.0.0/16")),
"invalid spec.IPv4.PerNodeHostBits > CIDR Host Bits": makeClusterCIDRConfigCustom(
setIPv4CIDR(24, "10.2.0.0/16")),
// IPv6 tests.
"invalid spec.IPv6CIDR": makeClusterCIDRConfigCustom(
setIPv6CIDR(8, "testv6")),
"valid IPv4 CIDR in spec.IPv6CIDR": makeClusterCIDRConfigCustom(
setIPv6CIDR(8, "10.2.0.0/16")),
"invalid spec.PerNodeHostBits with IPv6 CIDR": makeClusterCIDRConfigCustom(
setIPv6CIDR(1000, "fd00::/120")),
"invalid spec.IPv6.PerNodeMaskSize < CIDR Mask": makeClusterCIDRConfigCustom(
setIPv6CIDR(12, "fd00::/120")),
}
// Error cases are not expected to pass validation.
for testName, ccc := range errorCases {
if errs := ValidateClusterCIDRConfig(ccc); len(errs) == 0 {
t.Errorf("Expected failure for test: %s", testName)
}
}
}
func TestValidateClusterConfigUpdate(t *testing.T) {
oldCCC := makeValidClusterCIDRConfig()
// Tweaks used below.
setIPv4CIDR := func(perNodeHostBits int32, ipv4CIDR string) cccTweak {
return func(ccc *networking.ClusterCIDRConfig) {
ccc.Spec.IPv4CIDR = ipv4CIDR
ccc.Spec.PerNodeHostBits = perNodeHostBits
}
}
setIPv6CIDR := func(perNodeHostBits int32, ipv6CIDR string) cccTweak {
return func(ccc *networking.ClusterCIDRConfig) {
ccc.Spec.IPv6CIDR = ipv6CIDR
ccc.Spec.PerNodeHostBits = perNodeHostBits
}
}
setNodeSelector := func(nodeSelector *api.NodeSelector) cccTweak {
return func(ccc *networking.ClusterCIDRConfig) {
ccc.Spec.NodeSelector = nodeSelector
}
}
updateNodeSelector := makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar2"})
successCases := map[string]*networking.ClusterCIDRConfig{
"update with no tweaks": makeClusterCIDRConfigCustom(),
}
// Error cases are not expected to pass validation.
for testName, ccc := range successCases {
errs := ValidateClusterCIDRConfigUpdate(ccc, oldCCC)
if len(errs) != 0 {
t.Errorf("Expected success for test '%s', got %v", testName, errs)
}
}
errorCases := map[string]*networking.ClusterCIDRConfig{
"update spec.IPv4": makeClusterCIDRConfigCustom(setIPv4CIDR(8, "10.2.0.0/16")),
"update spec.IPv6": makeClusterCIDRConfigCustom(setIPv6CIDR(8, "fd00:2:/112")),
"update spec.NodeSelector": makeClusterCIDRConfigCustom(setNodeSelector(
updateNodeSelector)),
}
// Error cases are not expected to pass validation.
for testName, ccc := range errorCases {
errs := ValidateClusterCIDRConfigUpdate(ccc, oldCCC)
if len(errs) == 0 {
t.Errorf("Expected failure for test: %s", testName)
}
}
}

View File

@@ -28,6 +28,104 @@ import (
core "k8s.io/kubernetes/pkg/apis/core"
)
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterCIDRConfig) DeepCopyInto(out *ClusterCIDRConfig) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
in.Spec.DeepCopyInto(&out.Spec)
out.Status = in.Status
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterCIDRConfig.
func (in *ClusterCIDRConfig) DeepCopy() *ClusterCIDRConfig {
if in == nil {
return nil
}
out := new(ClusterCIDRConfig)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ClusterCIDRConfig) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterCIDRConfigList) DeepCopyInto(out *ClusterCIDRConfigList) {
*out = *in
out.TypeMeta = in.TypeMeta
in.ListMeta.DeepCopyInto(&out.ListMeta)
if in.Items != nil {
in, out := &in.Items, &out.Items
*out = make([]ClusterCIDRConfig, len(*in))
for i := range *in {
(*in)[i].DeepCopyInto(&(*out)[i])
}
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterCIDRConfigList.
func (in *ClusterCIDRConfigList) DeepCopy() *ClusterCIDRConfigList {
if in == nil {
return nil
}
out := new(ClusterCIDRConfigList)
in.DeepCopyInto(out)
return out
}
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
func (in *ClusterCIDRConfigList) DeepCopyObject() runtime.Object {
if c := in.DeepCopy(); c != nil {
return c
}
return nil
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterCIDRConfigSpec) DeepCopyInto(out *ClusterCIDRConfigSpec) {
*out = *in
if in.NodeSelector != nil {
in, out := &in.NodeSelector, &out.NodeSelector
*out = new(core.NodeSelector)
(*in).DeepCopyInto(*out)
}
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterCIDRConfigSpec.
func (in *ClusterCIDRConfigSpec) DeepCopy() *ClusterCIDRConfigSpec {
if in == nil {
return nil
}
out := new(ClusterCIDRConfigSpec)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *ClusterCIDRConfigStatus) DeepCopyInto(out *ClusterCIDRConfigStatus) {
*out = *in
return
}
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterCIDRConfigStatus.
func (in *ClusterCIDRConfigStatus) DeepCopy() *ClusterCIDRConfigStatus {
if in == nil {
return nil
}
out := new(ClusterCIDRConfigStatus)
in.DeepCopyInto(out)
return out
}
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *HTTPIngressPath) DeepCopyInto(out *HTTPIngressPath) {
*out = *in