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

View File

@@ -45,6 +45,7 @@ import (
eventsv1beta1 "k8s.io/api/events/v1beta1"
flowcontrolv1alpha1 "k8s.io/api/flowcontrol/v1alpha1"
networkingapiv1 "k8s.io/api/networking/v1"
networkingapiv1alpha1 "k8s.io/api/networking/v1alpha1"
nodev1 "k8s.io/api/node/v1"
nodev1beta1 "k8s.io/api/node/v1beta1"
policyapiv1 "k8s.io/api/policy/v1"
@@ -710,6 +711,7 @@ var (
// alphaAPIGroupVersionsDisabledByDefault holds the alpha APIs we have. They are always disabled by default.
alphaAPIGroupVersionsDisabledByDefault = []schema.GroupVersion{
apiserverinternalv1alpha1.SchemeGroupVersion,
networkingapiv1alpha1.SchemeGroupVersion,
storageapiv1alpha1.SchemeGroupVersion,
flowcontrolv1alpha1.SchemeGroupVersion,
}

View File

@@ -684,6 +684,10 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"k8s.io/api/networking/v1.NetworkPolicySpec": schema_k8sio_api_networking_v1_NetworkPolicySpec(ref),
"k8s.io/api/networking/v1.NetworkPolicyStatus": schema_k8sio_api_networking_v1_NetworkPolicyStatus(ref),
"k8s.io/api/networking/v1.ServiceBackendPort": schema_k8sio_api_networking_v1_ServiceBackendPort(ref),
"k8s.io/api/networking/v1alpha1.ClusterCIDRConfig": schema_k8sio_api_networking_v1alpha1_ClusterCIDRConfig(ref),
"k8s.io/api/networking/v1alpha1.ClusterCIDRConfigList": schema_k8sio_api_networking_v1alpha1_ClusterCIDRConfigList(ref),
"k8s.io/api/networking/v1alpha1.ClusterCIDRConfigSpec": schema_k8sio_api_networking_v1alpha1_ClusterCIDRConfigSpec(ref),
"k8s.io/api/networking/v1alpha1.ClusterCIDRConfigStatus": schema_k8sio_api_networking_v1alpha1_ClusterCIDRConfigStatus(ref),
"k8s.io/api/networking/v1beta1.HTTPIngressPath": schema_k8sio_api_networking_v1beta1_HTTPIngressPath(ref),
"k8s.io/api/networking/v1beta1.HTTPIngressRuleValue": schema_k8sio_api_networking_v1beta1_HTTPIngressRuleValue(ref),
"k8s.io/api/networking/v1beta1.Ingress": schema_k8sio_api_networking_v1beta1_Ingress(ref),
@@ -34136,6 +34140,163 @@ func schema_k8sio_api_networking_v1_ServiceBackendPort(ref common.ReferenceCallb
}
}
func schema_k8sio_api_networking_v1alpha1_ClusterCIDRConfig(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "ClusterCIDRConfig is the Schema for the clustercidrconfigs API.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata",
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"),
},
},
"spec": {
SchemaProps: spec.SchemaProps{
Description: "Spec is the desired state of the ClusterCIDRConfig. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status",
Default: map[string]interface{}{},
Ref: ref("k8s.io/api/networking/v1alpha1.ClusterCIDRConfigSpec"),
},
},
"status": {
SchemaProps: spec.SchemaProps{
Description: "Status is the current state of the ClusterCIDRConfig. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status",
Default: map[string]interface{}{},
Ref: ref("k8s.io/api/networking/v1alpha1.ClusterCIDRConfigStatus"),
},
},
},
},
},
Dependencies: []string{
"k8s.io/api/networking/v1alpha1.ClusterCIDRConfigSpec", "k8s.io/api/networking/v1alpha1.ClusterCIDRConfigStatus", "k8s.io/apimachinery/pkg/apis/meta/v1.ObjectMeta"},
}
}
func schema_k8sio_api_networking_v1alpha1_ClusterCIDRConfigList(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "ClusterCIDRConfigList contains a list of ClusterCIDRConfig.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"kind": {
SchemaProps: spec.SchemaProps{
Description: "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds",
Type: []string{"string"},
Format: "",
},
},
"apiVersion": {
SchemaProps: spec.SchemaProps{
Description: "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
Type: []string{"string"},
Format: "",
},
},
"metadata": {
SchemaProps: spec.SchemaProps{
Description: "Standard object's metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata",
Default: map[string]interface{}{},
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"),
},
},
"items": {
SchemaProps: spec.SchemaProps{
Description: "Items is the list of ClusterCIDRConfigs.",
Type: []string{"array"},
Items: &spec.SchemaOrArray{
Schema: &spec.Schema{
SchemaProps: spec.SchemaProps{
Default: map[string]interface{}{},
Ref: ref("k8s.io/api/networking/v1alpha1.ClusterCIDRConfig"),
},
},
},
},
},
},
Required: []string{"items"},
},
},
Dependencies: []string{
"k8s.io/api/networking/v1alpha1.ClusterCIDRConfig", "k8s.io/apimachinery/pkg/apis/meta/v1.ListMeta"},
}
}
func schema_k8sio_api_networking_v1alpha1_ClusterCIDRConfigSpec(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "ClusterCIDRConfigSpec defines the desired state of ClusterCIDRConfig.",
Type: []string{"object"},
Properties: map[string]spec.Schema{
"nodeSelector": {
SchemaProps: spec.SchemaProps{
Description: "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.",
Ref: ref("k8s.io/api/core/v1.NodeSelector"),
},
},
"perNodeHostBits": {
SchemaProps: spec.SchemaProps{
Description: "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.",
Default: 0,
Type: []string{"integer"},
Format: "int32",
},
},
"ipv4CIDR": {
SchemaProps: spec.SchemaProps{
Description: "IPv4CIDR defines an IPv4 IP block in CIDR notation(e.g. \"10.0.0.0/8\"). This field is immutable.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
"ipv6CIDR": {
SchemaProps: spec.SchemaProps{
Description: "IPv6CIDR defines an IPv6 IP block in CIDR notation(e.g. \"fd12:3456:789a:1::/64\"). This field is immutable.",
Default: "",
Type: []string{"string"},
Format: "",
},
},
},
},
},
Dependencies: []string{
"k8s.io/api/core/v1.NodeSelector"},
}
}
func schema_k8sio_api_networking_v1alpha1_ClusterCIDRConfigStatus(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Description: "ClusterCIDRConfigStatus defines the observed state of ClusterCIDRConfig.",
Type: []string{"object"},
},
},
}
}
func schema_k8sio_api_networking_v1beta1_HTTPIngressPath(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{

View File

@@ -64,6 +64,7 @@ func NewStorageFactoryConfig() *StorageFactoryConfig {
// TODO (https://github.com/kubernetes/kubernetes/issues/108451): remove the override in
// 1.25.
apisstorage.Resource("csistoragecapacities").WithVersion("v1beta1"),
networking.Resource("clustercidrconfigs").WithVersion("v1alpha1"),
}
return &StorageFactoryConfig{

View File

@@ -37,6 +37,7 @@ import (
discoveryv1beta1 "k8s.io/api/discovery/v1beta1"
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
flowcontrolv1beta2 "k8s.io/api/flowcontrol/v1beta2"
networkingv1alpha1 "k8s.io/api/networking/v1alpha1"
policyv1beta1 "k8s.io/api/policy/v1beta1"
rbacv1beta1 "k8s.io/api/rbac/v1beta1"
schedulingv1 "k8s.io/api/scheduling/v1"
@@ -591,6 +592,18 @@ func AddHandlers(h printers.PrintHandler) {
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
}
h.TableHandler(scaleColumnDefinitions, printScale)
clusterCIDRConfigColumnDefinitions := []metav1.TableColumnDefinition{
{Name: "Name", Type: "string", Format: "name", Description: metav1.ObjectMeta{}.SwaggerDoc()["name"]},
{Name: "PerNodeHostBits", Type: "string", Description: networkingv1alpha1.ClusterCIDRConfigSpec{}.SwaggerDoc()["perNodeHostBits"]},
{Name: "IPv4CIDR", Type: "string", Description: networkingv1alpha1.ClusterCIDRConfigSpec{}.SwaggerDoc()["ipv4CIDR"]},
{Name: "IPv6CIDR", Type: "string", Description: networkingv1alpha1.ClusterCIDRConfigSpec{}.SwaggerDoc()["ipv6CIDR"]},
{Name: "Age", Type: "string", Description: metav1.ObjectMeta{}.SwaggerDoc()["creationTimestamp"]},
{Name: "NodeSelector", Type: "string", Priority: 1, Description: networkingv1alpha1.ClusterCIDRConfigSpec{}.SwaggerDoc()["nodeSelector"]},
}
h.TableHandler(clusterCIDRConfigColumnDefinitions, printClusterCIDRConfig)
h.TableHandler(clusterCIDRConfigColumnDefinitions, printClusterCIDRConfigList)
}
// Pass ports=nil for all ports.
@@ -2624,6 +2637,57 @@ func printPriorityLevelConfigurationList(list *flowcontrol.PriorityLevelConfigur
return rows, nil
}
func printClusterCIDRConfig(obj *networking.ClusterCIDRConfig, options printers.GenerateOptions) ([]metav1.TableRow, error) {
row := metav1.TableRow{
Object: runtime.RawExtension{Object: obj},
}
ipv4 := "<none>"
ipv6 := "<none>"
if obj.Spec.IPv4CIDR != "" {
ipv4 = obj.Spec.IPv4CIDR
}
if obj.Spec.IPv6CIDR != "" {
ipv6 = obj.Spec.IPv6CIDR
}
row.Cells = append(row.Cells, obj.Name, fmt.Sprint(obj.Spec.PerNodeHostBits), ipv4, ipv6, translateTimestampSince(obj.CreationTimestamp))
if options.Wide {
nodeSelector := "<none>"
if obj.Spec.NodeSelector != nil {
allTerms := make([]string, 0)
for _, term := range obj.Spec.NodeSelector.NodeSelectorTerms {
if len(term.MatchExpressions) > 0 {
matchExpressions := fmt.Sprintf("MatchExpressions: %v", term.MatchExpressions)
allTerms = append(allTerms, matchExpressions)
}
if len(term.MatchFields) > 0 {
matchFields := fmt.Sprintf("MatchFields: %v", term.MatchFields)
allTerms = append(allTerms, matchFields)
}
}
nodeSelector = strings.Join(allTerms, ",")
}
row.Cells = append(row.Cells, nodeSelector)
}
return []metav1.TableRow{row}, nil
}
func printClusterCIDRConfigList(list *networking.ClusterCIDRConfigList, options printers.GenerateOptions) ([]metav1.TableRow, error) {
rows := make([]metav1.TableRow, 0, len(list.Items))
for i := range list.Items {
r, err := printClusterCIDRConfig(&list.Items[i], options)
if err != nil {
return nil, err
}
rows = append(rows, r...)
}
return rows, nil
}
func printScale(obj *autoscaling.Scale, options printers.GenerateOptions) ([]metav1.TableRow, error) {
row := metav1.TableRow{
Object: runtime.RawExtension{Object: obj},

View File

@@ -5860,3 +5860,277 @@ func TestPrintScale(t *testing.T) {
}
}
}
func TestPrintClusterCIDRConfig(t *testing.T) {
ipv4CIDR := "10.1.0.0/16"
perNodeHostBits := int32(8)
ipv6CIDR := "fd00:1:1::/64"
tests := []struct {
ccc networking.ClusterCIDRConfig
options printers.GenerateOptions
expected []metav1.TableRow
}{
{
// Test name, IPv4 only with no node selector.
ccc: networking.ClusterCIDRConfig{
ObjectMeta: metav1.ObjectMeta{Name: "test1"},
Spec: networking.ClusterCIDRConfigSpec{
PerNodeHostBits: perNodeHostBits,
IPv4CIDR: ipv4CIDR,
},
},
options: printers.GenerateOptions{},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age.
expected: []metav1.TableRow{{Cells: []interface{}{"test1", "8", ipv4CIDR, "<none>", "<unknown>"}}},
},
{
// Test name, IPv4 only with node selector, Not wide.
ccc: networking.ClusterCIDRConfig{
ObjectMeta: metav1.ObjectMeta{Name: "test2"},
Spec: networking.ClusterCIDRConfigSpec{
PerNodeHostBits: perNodeHostBits,
IPv4CIDR: ipv4CIDR,
// Does NOT get printed.
NodeSelector: makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"}),
},
},
options: printers.GenerateOptions{},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age.
expected: []metav1.TableRow{{Cells: []interface{}{"test2", "8", ipv4CIDR, "<none>", "<unknown>"}}},
},
{
// Test name, IPv4 only with no node selector, wide.
ccc: networking.ClusterCIDRConfig{
ObjectMeta: metav1.ObjectMeta{Name: "test3"},
Spec: networking.ClusterCIDRConfigSpec{
PerNodeHostBits: perNodeHostBits,
IPv4CIDR: ipv4CIDR,
},
},
options: printers.GenerateOptions{Wide: true},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age, NodeSelector .
expected: []metav1.TableRow{{Cells: []interface{}{"test3", "8", ipv4CIDR, "<none>", "<unknown>", "<none>"}}},
},
{
// Test name, IPv4 only with node selector, wide.
ccc: networking.ClusterCIDRConfig{
ObjectMeta: metav1.ObjectMeta{Name: "test4"},
Spec: networking.ClusterCIDRConfigSpec{
PerNodeHostBits: perNodeHostBits,
IPv4CIDR: ipv4CIDR,
NodeSelector: makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"}),
},
},
options: printers.GenerateOptions{Wide: true},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age, NodeSelector .
expected: []metav1.TableRow{{Cells: []interface{}{"test4", "8", ipv4CIDR, "<none>", "<unknown>", "MatchExpressions: [{foo In [bar]}]"}}},
},
{
// Test name, IPv6 only with no node selector.
ccc: networking.ClusterCIDRConfig{
ObjectMeta: metav1.ObjectMeta{Name: "test5"},
Spec: networking.ClusterCIDRConfigSpec{
PerNodeHostBits: perNodeHostBits,
IPv6CIDR: ipv6CIDR,
},
},
options: printers.GenerateOptions{},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age
expected: []metav1.TableRow{{Cells: []interface{}{"test5", "8", "<none>", ipv6CIDR, "<unknown>"}}},
},
{
// Test name, IPv6 only with node selector, Not wide.
ccc: networking.ClusterCIDRConfig{
ObjectMeta: metav1.ObjectMeta{Name: "test6"},
Spec: networking.ClusterCIDRConfigSpec{
PerNodeHostBits: perNodeHostBits,
IPv6CIDR: ipv6CIDR,
// Does NOT get printed.
NodeSelector: makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"}),
},
},
options: printers.GenerateOptions{},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age.
expected: []metav1.TableRow{{Cells: []interface{}{"test6", "8", "<none>", ipv6CIDR, "<unknown>"}}},
},
{
// Test name, IPv6 only with no node selector, wide.
ccc: networking.ClusterCIDRConfig{
ObjectMeta: metav1.ObjectMeta{Name: "test7"},
Spec: networking.ClusterCIDRConfigSpec{
PerNodeHostBits: perNodeHostBits,
IPv6CIDR: ipv6CIDR,
},
},
options: printers.GenerateOptions{Wide: true},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age, NodeSelector .
expected: []metav1.TableRow{{Cells: []interface{}{"test7", "8", "<none>", ipv6CIDR, "<unknown>", "<none>"}}},
},
{
// Test name, IPv6 only with node selector, wide.
ccc: networking.ClusterCIDRConfig{
ObjectMeta: metav1.ObjectMeta{Name: "test8"},
Spec: networking.ClusterCIDRConfigSpec{
PerNodeHostBits: perNodeHostBits,
IPv6CIDR: ipv6CIDR,
NodeSelector: makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"}),
},
},
options: printers.GenerateOptions{Wide: true},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age, NodeSelector .
expected: []metav1.TableRow{{Cells: []interface{}{"test8", "8", "<none>", ipv6CIDR, "<unknown>", "MatchExpressions: [{foo In [bar]}]"}}},
},
{
// Test name, DualStack with no node selector.
ccc: networking.ClusterCIDRConfig{
ObjectMeta: metav1.ObjectMeta{Name: "test9"},
Spec: networking.ClusterCIDRConfigSpec{
PerNodeHostBits: perNodeHostBits,
IPv4CIDR: ipv4CIDR,
IPv6CIDR: ipv6CIDR,
},
},
options: printers.GenerateOptions{},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age.
expected: []metav1.TableRow{{Cells: []interface{}{"test9", "8", ipv4CIDR, ipv6CIDR, "<unknown>"}}},
},
{
// Test name,DualStack with node selector, Not wide.
ccc: networking.ClusterCIDRConfig{
ObjectMeta: metav1.ObjectMeta{Name: "test10"},
Spec: networking.ClusterCIDRConfigSpec{
PerNodeHostBits: perNodeHostBits,
IPv4CIDR: ipv4CIDR,
IPv6CIDR: ipv6CIDR,
// Does NOT get printed.
NodeSelector: makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"}),
},
},
options: printers.GenerateOptions{},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age.
expected: []metav1.TableRow{{Cells: []interface{}{"test10", "8", ipv4CIDR, ipv6CIDR, "<unknown>"}}},
},
{
// Test name, DualStack with no node selector, wide.
ccc: networking.ClusterCIDRConfig{
ObjectMeta: metav1.ObjectMeta{Name: "test11"},
Spec: networking.ClusterCIDRConfigSpec{
PerNodeHostBits: perNodeHostBits,
IPv4CIDR: ipv4CIDR,
IPv6CIDR: ipv6CIDR,
},
},
options: printers.GenerateOptions{Wide: true},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age, NodeSelector.
expected: []metav1.TableRow{{Cells: []interface{}{"test11", "8", ipv4CIDR, ipv6CIDR, "<unknown>", "<none>"}}},
},
{
// Test name, DualStack with node selector, wide.
ccc: networking.ClusterCIDRConfig{
ObjectMeta: metav1.ObjectMeta{Name: "test12"},
Spec: networking.ClusterCIDRConfigSpec{
PerNodeHostBits: perNodeHostBits,
IPv4CIDR: ipv4CIDR,
IPv6CIDR: ipv6CIDR,
NodeSelector: makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"}),
},
},
options: printers.GenerateOptions{Wide: true},
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age, NodeSelector .
expected: []metav1.TableRow{{Cells: []interface{}{"test12", "8", ipv4CIDR, ipv6CIDR, "<unknown>", "MatchExpressions: [{foo In [bar]}]"}}},
},
}
for i, test := range tests {
rows, err := printClusterCIDRConfig(&test.ccc, test.options)
if err != nil {
t.Fatal(err)
}
for i := range rows {
rows[i].Object.Object = nil
}
if !reflect.DeepEqual(test.expected, rows) {
t.Errorf("%d mismatch: %s", i, diff.ObjectReflectDiff(test.expected, rows))
}
}
}
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 TestPrintClusterCIDRConfigList(t *testing.T) {
cccList := networking.ClusterCIDRConfigList{
Items: []networking.ClusterCIDRConfig{
{
ObjectMeta: metav1.ObjectMeta{Name: "ccc1"},
Spec: networking.ClusterCIDRConfigSpec{
PerNodeHostBits: int32(8),
IPv4CIDR: "10.1.0.0/16",
IPv6CIDR: "fd00:1:1::/64",
NodeSelector: makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"}),
},
},
{
ObjectMeta: metav1.ObjectMeta{Name: "ccc2"},
Spec: networking.ClusterCIDRConfigSpec{
PerNodeHostBits: int32(8),
IPv4CIDR: "10.2.0.0/16",
IPv6CIDR: "fd00:2:1::/64",
NodeSelector: makeNodeSelector("foo", api.NodeSelectorOpIn, []string{"bar"}),
},
},
},
}
tests := []struct {
options printers.GenerateOptions
expected []metav1.TableRow
}{
{
// Test name, DualStack with node selector, wide.
options: printers.GenerateOptions{Wide: false},
expected: []metav1.TableRow{
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age.
{Cells: []interface{}{"ccc1", "8", "10.1.0.0/16", "fd00:1:1::/64", "<unknown>"}},
{Cells: []interface{}{"ccc2", "8", "10.2.0.0/16", "fd00:2:1::/64", "<unknown>"}},
},
},
{
// Test name, DualStack with node selector, wide.
options: printers.GenerateOptions{Wide: true},
expected: []metav1.TableRow{
// Columns: Name, PerNodeHostBits, IPv4, IPv6, Age, NodeSelector.
{Cells: []interface{}{"ccc1", "8", "10.1.0.0/16", "fd00:1:1::/64", "<unknown>", "MatchExpressions: [{foo In [bar]}]"}},
{Cells: []interface{}{"ccc2", "8", "10.2.0.0/16", "fd00:2:1::/64", "<unknown>", "MatchExpressions: [{foo In [bar]}]"}},
},
},
}
for _, test := range tests {
rows, err := printClusterCIDRConfigList(&cccList, test.options)
if err != nil {
t.Fatalf("Error printing service list: %#v", err)
}
for i := range rows {
rows[i].Object.Object = nil
}
if !reflect.DeepEqual(test.expected, rows) {
t.Errorf("mismatch: %s", diff.ObjectReflectDiff(test.expected, rows))
}
}
}

View File

@@ -0,0 +1,17 @@
/*
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 clustercidrconfig // import "k8s.io/kubernetes/pkg/registry/networking/clustercidrconfig"

View File

@@ -0,0 +1,63 @@
/*
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 storage
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistry "k8s.io/apiserver/pkg/registry/generic/registry"
"k8s.io/apiserver/pkg/registry/rest"
networkingapi "k8s.io/kubernetes/pkg/apis/networking"
"k8s.io/kubernetes/pkg/printers"
printersinternal "k8s.io/kubernetes/pkg/printers/internalversion"
printerstorage "k8s.io/kubernetes/pkg/printers/storage"
"k8s.io/kubernetes/pkg/registry/networking/clustercidrconfig"
)
// REST implements a RESTStorage for ClusterCIDRConfigs against etcd.
type REST struct {
*genericregistry.Store
}
// NewREST returns a RESTStorage object that will work against ClusterCIDRConfigs.
func NewREST(optsGetter generic.RESTOptionsGetter) (*REST, error) {
store := &genericregistry.Store{
NewFunc: func() runtime.Object { return &networkingapi.ClusterCIDRConfig{} },
NewListFunc: func() runtime.Object { return &networkingapi.ClusterCIDRConfigList{} },
DefaultQualifiedResource: networkingapi.Resource("clustercidrconfigs"),
CreateStrategy: clustercidrconfig.Strategy,
UpdateStrategy: clustercidrconfig.Strategy,
DeleteStrategy: clustercidrconfig.Strategy,
TableConvertor: printerstorage.TableConvertor{TableGenerator: printers.NewTableGenerator().With(printersinternal.AddHandlers)},
}
options := &generic.StoreOptions{RESTOptions: optsGetter}
if err := store.CompleteWithOptions(options); err != nil {
return nil, err
}
return &REST{store}, nil
}
// Implement ShortNamesProvider.
var _ rest.ShortNamesProvider = &REST{}
// ShortNames implements the ShortNamesProvider interface. Returns a list of short names for a resource.
func (r *REST) ShortNames() []string {
return []string{"ccc"}
}

View File

@@ -0,0 +1,197 @@
/*
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 storage
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apiserver/pkg/registry/generic"
genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing"
etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing"
api "k8s.io/kubernetes/pkg/apis/core"
_ "k8s.io/kubernetes/pkg/apis/extensions/install"
"k8s.io/kubernetes/pkg/apis/networking"
_ "k8s.io/kubernetes/pkg/apis/networking/install"
"k8s.io/kubernetes/pkg/registry/registrytest"
)
func newStorage(t *testing.T) (*REST, *etcd3testing.EtcdTestServer) {
etcdStorage, server := registrytest.NewEtcdStorageForResource(t, networking.Resource("clustercidrconfigs"))
restOptions := generic.RESTOptions{
StorageConfig: etcdStorage,
Decorator: generic.UndecoratedStorage,
DeleteCollectionWorkers: 1,
ResourcePrefix: "clustercidrconfigs",
}
clusterCIDRConfigStorage, err := NewREST(restOptions)
if err != nil {
t.Fatalf("unexpected error from REST storage: %v", err)
}
return clusterCIDRConfigStorage, server
}
var (
namespace = metav1.NamespaceNone
name = "foo-clustercidrconfig"
)
func newClusterCIDRConfig() *networking.ClusterCIDRConfig {
return &networking.ClusterCIDRConfig{
ObjectMeta: metav1.ObjectMeta{
Name: name,
Namespace: namespace,
},
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"},
},
},
},
},
},
},
}
}
func validClusterCIDRConfig() *networking.ClusterCIDRConfig {
return newClusterCIDRConfig()
}
func TestCreate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
test = test.ClusterScope()
validCCC := validClusterCIDRConfig()
noCIDRCCC := validClusterCIDRConfig()
noCIDRCCC.Spec.IPv4CIDR = ""
noCIDRCCC.Spec.IPv6CIDR = ""
invalidCCCPerNodeHostBits := validClusterCIDRConfig()
invalidCCCPerNodeHostBits.Spec.PerNodeHostBits = 100
invalidCCCCIDR := validClusterCIDRConfig()
invalidCCCCIDR.Spec.IPv6CIDR = "10.1.0.0/16"
test.TestCreate(
// valid
validCCC,
//invalid
noCIDRCCC,
invalidCCCPerNodeHostBits,
invalidCCCCIDR,
)
}
func TestUpdate(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
test = test.ClusterScope()
test.TestUpdate(
// valid
validClusterCIDRConfig(),
// updateFunc
func(obj runtime.Object) runtime.Object {
object := obj.(*networking.ClusterCIDRConfig)
object.Finalizers = []string{"test.k8s.io/test-finalizer"}
return object
},
// invalid updateFunc: ObjectMeta is not to be tampered with.
func(obj runtime.Object) runtime.Object {
object := obj.(*networking.ClusterCIDRConfig)
object.Name = ""
return object
},
)
}
func TestDelete(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
test = test.ClusterScope()
test.TestDelete(validClusterCIDRConfig())
}
func TestGet(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
test = test.ClusterScope()
test.TestGet(validClusterCIDRConfig())
}
func TestList(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
test = test.ClusterScope()
test.TestList(validClusterCIDRConfig())
}
func TestWatch(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
test := genericregistrytest.New(t, storage.Store)
test = test.ClusterScope()
test.TestWatch(
validClusterCIDRConfig(),
// matching labels
[]labels.Set{},
// not matching labels
[]labels.Set{
{"a": "c"},
{"foo": "bar"},
},
// matching fields
[]fields.Set{
{"metadata.name": name},
},
// not matching fields
[]fields.Set{
{"metadata.name": "bar"},
{"name": name},
},
)
}
func TestShortNames(t *testing.T) {
storage, server := newStorage(t)
defer server.Terminate(t)
defer storage.Store.DestroyFunc()
expected := []string{"ccc"}
registrytest.AssertShortNames(t, storage, expected)
}

View File

@@ -0,0 +1,84 @@
/*
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 clustercidrconfig
import (
"context"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/validation/field"
"k8s.io/apiserver/pkg/storage/names"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/networking"
"k8s.io/kubernetes/pkg/apis/networking/validation"
)
// clusterCIDRConfigStrategy implements verification logic for ClusterCIDRConfigs.
type clusterCIDRConfigStrategy struct {
runtime.ObjectTyper
names.NameGenerator
}
// Strategy is the default logic that applies when creating and updating clusterCIDRConfig objects.
var Strategy = clusterCIDRConfigStrategy{legacyscheme.Scheme, names.SimpleNameGenerator}
// NamespaceScoped returns false because all clusterCIDRConfigs do not need to be within a namespace.
func (clusterCIDRConfigStrategy) NamespaceScoped() bool {
return false
}
// PrepareForCreate clears the status of a ClusterCIDRConfig before creation.
func (clusterCIDRConfigStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) {}
// PrepareForUpdate clears fields that are not allowed to be set by end users on update.
func (clusterCIDRConfigStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) {}
// Validate validates a new ClusterCIDRConfig.
func (clusterCIDRConfigStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
clusterCIDRConfig := obj.(*networking.ClusterCIDRConfig)
return validation.ValidateClusterCIDRConfig(clusterCIDRConfig)
}
// WarningsOnCreate returns warnings for the creation of the given object.
func (clusterCIDRConfigStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string {
return nil
}
// Canonicalize normalizes the object after validation.
func (clusterCIDRConfigStrategy) Canonicalize(obj runtime.Object) {}
// AllowCreateOnUpdate is false for ClusterCIDRConfig; this means POST is needed to create one.
func (clusterCIDRConfigStrategy) AllowCreateOnUpdate() bool {
return false
}
// ValidateUpdate is the default update validation for an end user.
func (clusterCIDRConfigStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList {
validationErrorList := validation.ValidateClusterCIDRConfig(obj.(*networking.ClusterCIDRConfig))
updateErrorList := validation.ValidateClusterCIDRConfigUpdate(obj.(*networking.ClusterCIDRConfig), old.(*networking.ClusterCIDRConfig))
return append(validationErrorList, updateErrorList...)
}
// WarningsOnUpdate returns warnings for the given update.
func (clusterCIDRConfigStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string {
return nil
}
// AllowUnconditionalUpdate is the default update policy for ClusterCIDRConfig objects.
func (clusterCIDRConfigStrategy) AllowUnconditionalUpdate() bool {
return true
}

View File

@@ -0,0 +1,86 @@
/*
Copyright 2015 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 clustercidrconfig
import (
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
api "k8s.io/kubernetes/pkg/apis/core"
"k8s.io/kubernetes/pkg/apis/networking"
)
func newClusterCIDRConfig() networking.ClusterCIDRConfig {
return networking.ClusterCIDRConfig{
ObjectMeta: metav1.ObjectMeta{
Name: "foo",
},
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"},
},
},
},
},
},
},
}
}
func TestClusterCIDRConfigStrategy(t *testing.T) {
ctx := genericapirequest.NewDefaultContext()
apiRequest := genericapirequest.RequestInfo{APIGroup: "networking.k8s.io",
APIVersion: "v1alpha1",
Resource: "clustercidrconfigs",
}
ctx = genericapirequest.WithRequestInfo(ctx, &apiRequest)
if Strategy.NamespaceScoped() {
t.Errorf("ClusterCIDRConfigs must be cluster scoped")
}
if Strategy.AllowCreateOnUpdate() {
t.Errorf("ClusterCIDRConfigs should not allow create on update")
}
ccc := newClusterCIDRConfig()
Strategy.PrepareForCreate(ctx, &ccc)
errs := Strategy.Validate(ctx, &ccc)
if len(errs) != 0 {
t.Errorf("Unexpected error validating %v", errs)
}
invalidCCC := newClusterCIDRConfig()
invalidCCC.ResourceVersion = "4"
invalidCCC.Spec = networking.ClusterCIDRConfigSpec{}
Strategy.PrepareForUpdate(ctx, &invalidCCC, &ccc)
errs = Strategy.ValidateUpdate(ctx, &invalidCCC, &ccc)
if len(errs) == 0 {
t.Errorf("Expected a validation error")
}
if invalidCCC.ResourceVersion != "4" {
t.Errorf("Incoming resource version on update should not be mutated")
}
}

View File

@@ -18,12 +18,14 @@ package rest
import (
networkingapiv1 "k8s.io/api/networking/v1"
networkingapiv1alpha1 "k8s.io/api/networking/v1alpha1"
"k8s.io/apiserver/pkg/registry/generic"
"k8s.io/apiserver/pkg/registry/rest"
genericapiserver "k8s.io/apiserver/pkg/server"
serverstorage "k8s.io/apiserver/pkg/server/storage"
"k8s.io/kubernetes/pkg/api/legacyscheme"
"k8s.io/kubernetes/pkg/apis/networking"
clustercidrconfigstore "k8s.io/kubernetes/pkg/registry/networking/clustercidrconfig/storage"
ingressstore "k8s.io/kubernetes/pkg/registry/networking/ingress/storage"
ingressclassstore "k8s.io/kubernetes/pkg/registry/networking/ingressclass/storage"
networkpolicystore "k8s.io/kubernetes/pkg/registry/networking/networkpolicy/storage"
@@ -36,6 +38,12 @@ func (p RESTStorageProvider) NewRESTStorage(apiResourceConfigSource serverstorag
// If you add a version here, be sure to add an entry in `k8s.io/kubernetes/cmd/kube-apiserver/app/aggregator.go with specific priorities.
// TODO refactor the plumbing to provide the information in the APIGroupInfo
if storageMap, err := p.v1alpha1Storage(apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, err
} else if len(storageMap) > 0 {
apiGroupInfo.VersionedResourcesStorageMap[networkingapiv1alpha1.SchemeGroupVersion.Version] = storageMap
}
if storageMap, err := p.v1Storage(apiResourceConfigSource, restOptionsGetter); err != nil {
return genericapiserver.APIGroupInfo{}, err
} else if len(storageMap) > 0 {
@@ -80,6 +88,20 @@ func (p RESTStorageProvider) v1Storage(apiResourceConfigSource serverstorage.API
return storage, nil
}
func (p RESTStorageProvider) v1alpha1Storage(apiResourceConfigSource serverstorage.APIResourceConfigSource, restOptionsGetter generic.RESTOptionsGetter) (map[string]rest.Storage, error) {
storage := map[string]rest.Storage{}
// clustercidrconfigs
if resource := "clustercidrconfigs"; apiResourceConfigSource.ResourceEnabled(networkingapiv1alpha1.SchemeGroupVersion.WithResource(resource)) {
clusterCIDRConfigStorage, err := clustercidrconfigstore.NewREST(restOptionsGetter)
if err != nil {
return storage, err
}
storage[resource] = clusterCIDRConfigStorage
}
return storage, nil
}
func (p RESTStorageProvider) GroupName() string {
return networking.GroupName
}