mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-18 04:54:54 +00:00
Mark Endpoint.Addresses required, add test cases
This commit is contained in:
committed by
CLBRITTON2
parent
494e27a059
commit
5e4d5fe599
106
pkg/apis/discovery/v1/zz_generated.validations.go
generated
106
pkg/apis/discovery/v1/zz_generated.validations.go
generated
@@ -0,0 +1,106 @@
|
||||
//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 validation-gen. DO NOT EDIT.
|
||||
|
||||
package v1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
discoveryv1 "k8s.io/api/discovery/v1"
|
||||
equality "k8s.io/apimachinery/pkg/api/equality"
|
||||
operation "k8s.io/apimachinery/pkg/api/operation"
|
||||
safe "k8s.io/apimachinery/pkg/api/safe"
|
||||
validate "k8s.io/apimachinery/pkg/api/validate"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
field "k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func init() { localSchemeBuilder.Register(RegisterValidations) }
|
||||
|
||||
// RegisterValidations adds validation functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
func RegisterValidations(scheme *runtime.Scheme) error {
|
||||
// type EndpointSlice
|
||||
scheme.AddValidationFunc((*discoveryv1.EndpointSlice)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_EndpointSlice(ctx, op, nil /* fldPath */, obj.(*discoveryv1.EndpointSlice), safe.Cast[*discoveryv1.EndpointSlice](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_Endpoint validates an instance of Endpoint according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_Endpoint(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *discoveryv1.Endpoint) (errs field.ErrorList) {
|
||||
// field discoveryv1.Endpoint.Addresses
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.RequiredSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("addresses"), obj.Addresses, safe.Field(oldObj, func(oldObj *discoveryv1.Endpoint) []string { return oldObj.Addresses }), oldObj != nil)...)
|
||||
|
||||
// field discoveryv1.Endpoint.Conditions has no validation
|
||||
// field discoveryv1.Endpoint.Hostname has no validation
|
||||
// field discoveryv1.Endpoint.TargetRef has no validation
|
||||
// field discoveryv1.Endpoint.DeprecatedTopology has no validation
|
||||
// field discoveryv1.Endpoint.NodeName has no validation
|
||||
// field discoveryv1.Endpoint.Zone has no validation
|
||||
// field discoveryv1.Endpoint.Hints has no validation
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_EndpointSlice validates an instance of EndpointSlice according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_EndpointSlice(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *discoveryv1.EndpointSlice) (errs field.ErrorList) {
|
||||
// field discoveryv1.EndpointSlice.TypeMeta has no validation
|
||||
// field discoveryv1.EndpointSlice.ObjectMeta has no validation
|
||||
// field discoveryv1.EndpointSlice.AddressType has no validation
|
||||
|
||||
// field discoveryv1.EndpointSlice.Endpoints
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []discoveryv1.Endpoint, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// iterate the list and call the type's validation function
|
||||
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Endpoint)...)
|
||||
return
|
||||
}(fldPath.Child("endpoints"), obj.Endpoints, safe.Field(oldObj, func(oldObj *discoveryv1.EndpointSlice) []discoveryv1.Endpoint { return oldObj.Endpoints }), oldObj != nil)...)
|
||||
|
||||
// field discoveryv1.EndpointSlice.Ports has no validation
|
||||
return errs
|
||||
}
|
||||
|
||||
105
pkg/apis/discovery/v1beta1/zz_generated.validations.go
generated
105
pkg/apis/discovery/v1beta1/zz_generated.validations.go
generated
@@ -0,0 +1,105 @@
|
||||
//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 validation-gen. DO NOT EDIT.
|
||||
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
discoveryv1beta1 "k8s.io/api/discovery/v1beta1"
|
||||
equality "k8s.io/apimachinery/pkg/api/equality"
|
||||
operation "k8s.io/apimachinery/pkg/api/operation"
|
||||
safe "k8s.io/apimachinery/pkg/api/safe"
|
||||
validate "k8s.io/apimachinery/pkg/api/validate"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
field "k8s.io/apimachinery/pkg/util/validation/field"
|
||||
)
|
||||
|
||||
func init() { localSchemeBuilder.Register(RegisterValidations) }
|
||||
|
||||
// RegisterValidations adds validation functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
func RegisterValidations(scheme *runtime.Scheme) error {
|
||||
// type EndpointSlice
|
||||
scheme.AddValidationFunc((*discoveryv1beta1.EndpointSlice)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_EndpointSlice(ctx, op, nil /* fldPath */, obj.(*discoveryv1beta1.EndpointSlice), safe.Cast[*discoveryv1beta1.EndpointSlice](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_Endpoint validates an instance of Endpoint according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_Endpoint(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *discoveryv1beta1.Endpoint) (errs field.ErrorList) {
|
||||
// field discoveryv1beta1.Endpoint.Addresses
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []string, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.RequiredSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if earlyReturn {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("addresses"), obj.Addresses, safe.Field(oldObj, func(oldObj *discoveryv1beta1.Endpoint) []string { return oldObj.Addresses }), oldObj != nil)...)
|
||||
|
||||
// field discoveryv1beta1.Endpoint.Conditions has no validation
|
||||
// field discoveryv1beta1.Endpoint.Hostname has no validation
|
||||
// field discoveryv1beta1.Endpoint.TargetRef has no validation
|
||||
// field discoveryv1beta1.Endpoint.Topology has no validation
|
||||
// field discoveryv1beta1.Endpoint.NodeName has no validation
|
||||
// field discoveryv1beta1.Endpoint.Hints has no validation
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_EndpointSlice validates an instance of EndpointSlice according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_EndpointSlice(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *discoveryv1beta1.EndpointSlice) (errs field.ErrorList) {
|
||||
// field discoveryv1beta1.EndpointSlice.TypeMeta has no validation
|
||||
// field discoveryv1beta1.EndpointSlice.ObjectMeta has no validation
|
||||
// field discoveryv1beta1.EndpointSlice.AddressType has no validation
|
||||
|
||||
// field discoveryv1beta1.EndpointSlice.Endpoints
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []discoveryv1beta1.Endpoint, oldValueCorrelated bool) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if oldValueCorrelated && op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// iterate the list and call the type's validation function
|
||||
errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_Endpoint)...)
|
||||
return
|
||||
}(fldPath.Child("endpoints"), obj.Endpoints, safe.Field(oldObj, func(oldObj *discoveryv1beta1.EndpointSlice) []discoveryv1beta1.Endpoint { return oldObj.Endpoints }), oldObj != nil)...)
|
||||
|
||||
// field discoveryv1beta1.EndpointSlice.Ports has no validation
|
||||
return errs
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ func validateEndpoints(endpoints []discovery.Endpoint, addrType discovery.Addres
|
||||
addressPath := idxPath.Child("addresses")
|
||||
|
||||
if len(endpoint.Addresses) == 0 {
|
||||
allErrs = append(allErrs, field.Required(addressPath, "must contain at least 1 address"))
|
||||
allErrs = append(allErrs, field.Required(addressPath, "must contain at least 1 address").MarkCoveredByDeclarative())
|
||||
} else if len(endpoint.Addresses) > maxAddresses {
|
||||
allErrs = append(allErrs, field.TooMany(addressPath, len(endpoint.Addresses), maxAddresses))
|
||||
}
|
||||
|
||||
@@ -15,3 +15,129 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
package endpointslice
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
|
||||
apitesting "k8s.io/kubernetes/pkg/api/testing"
|
||||
"k8s.io/kubernetes/pkg/apis/discovery"
|
||||
)
|
||||
|
||||
var apiVersions = []string{"v1", "v1beta1"}
|
||||
|
||||
func TestDeclarativeValidate(t *testing.T) {
|
||||
for _, apiVersion := range apiVersions {
|
||||
t.Run(apiVersion, func(t *testing.T) {
|
||||
testDeclarativeValidate(t, apiVersion)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testDeclarativeValidate(t *testing.T, apiVersion string) {
|
||||
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewDefaultContext(), &genericapirequest.RequestInfo{
|
||||
APIGroup: "discovery.k8s.io",
|
||||
APIVersion: apiVersion,
|
||||
Resource: "endpointslices",
|
||||
IsResourceRequest: true,
|
||||
Verb: "create",
|
||||
})
|
||||
|
||||
testCases := map[string]struct {
|
||||
input discovery.EndpointSlice
|
||||
expectedErrs field.ErrorList
|
||||
}{
|
||||
"valid": {
|
||||
input: mkValidEndpointSlice(),
|
||||
},
|
||||
"invalid missing endpoint addresses": {
|
||||
input: mkValidEndpointSlice(func(obj *discovery.EndpointSlice) {
|
||||
obj.Endpoints[0].Addresses = nil
|
||||
}),
|
||||
expectedErrs: field.ErrorList{
|
||||
field.Required(field.NewPath("endpoints").Index(0).Child("addresses"), ""),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
apitesting.VerifyValidationEquivalence(t, ctx, &tc.input, Strategy.Validate, tc.expectedErrs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeclarativeValidateUpdate(t *testing.T) {
|
||||
for _, apiVersion := range apiVersions {
|
||||
t.Run(apiVersion, func(t *testing.T) {
|
||||
testDeclarativeValidateUpdate(t, apiVersion)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func testDeclarativeValidateUpdate(t *testing.T, apiVersion string) {
|
||||
testCases := map[string]struct {
|
||||
oldObj discovery.EndpointSlice
|
||||
updateObj discovery.EndpointSlice
|
||||
expectedErrs field.ErrorList
|
||||
}{
|
||||
"valid update": {
|
||||
oldObj: mkValidEndpointSlice(func(obj *discovery.EndpointSlice) {
|
||||
obj.ResourceVersion = "1"
|
||||
}),
|
||||
updateObj: mkValidEndpointSlice(func(obj *discovery.EndpointSlice) {
|
||||
obj.ResourceVersion = "1"
|
||||
}),
|
||||
},
|
||||
"invalid update missing addresses": {
|
||||
oldObj: mkValidEndpointSlice(func(obj *discovery.EndpointSlice) {
|
||||
obj.ResourceVersion = "1"
|
||||
}),
|
||||
updateObj: mkValidEndpointSlice(func(obj *discovery.EndpointSlice) {
|
||||
obj.ResourceVersion = "1"
|
||||
obj.Endpoints[0].Addresses = nil
|
||||
}),
|
||||
expectedErrs: field.ErrorList{
|
||||
field.Required(field.NewPath("endpoints").Index(0).Child("addresses"), ""),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewDefaultContext(), &genericapirequest.RequestInfo{
|
||||
APIPrefix: "apis",
|
||||
APIGroup: "discovery.k8s.io",
|
||||
APIVersion: apiVersion,
|
||||
Resource: "endpointslices",
|
||||
Name: "valid-endpointslice",
|
||||
IsResourceRequest: true,
|
||||
Verb: "update",
|
||||
})
|
||||
|
||||
apitesting.VerifyUpdateValidationEquivalence(t, ctx, &tc.updateObj, &tc.oldObj, Strategy.ValidateUpdate, tc.expectedErrs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func mkValidEndpointSlice(tweaks ...func(obj *discovery.EndpointSlice)) discovery.EndpointSlice {
|
||||
obj := discovery.EndpointSlice{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "valid-endpointslice",
|
||||
Namespace: metav1.NamespaceDefault,
|
||||
},
|
||||
AddressType: discovery.AddressTypeIPv4,
|
||||
Endpoints: []discovery.Endpoint{
|
||||
{
|
||||
Addresses: []string{"10.1.2.1"},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tweak := range tweaks {
|
||||
tweak(&obj)
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
@@ -38,6 +38,8 @@ message Endpoint {
|
||||
// controller will always have exactly 1 address. No semantics are defined for
|
||||
// additional addresses beyond the first, and kube-proxy does not look at them.
|
||||
// +listType=set
|
||||
// +required
|
||||
// +k8s:required
|
||||
repeated string addresses = 1;
|
||||
|
||||
// conditions contains information about the current status of the endpoint.
|
||||
|
||||
@@ -91,6 +91,8 @@ type Endpoint struct {
|
||||
// controller will always have exactly 1 address. No semantics are defined for
|
||||
// additional addresses beyond the first, and kube-proxy does not look at them.
|
||||
// +listType=set
|
||||
// +required
|
||||
// +k8s:required
|
||||
Addresses []string `json:"addresses" protobuf:"bytes,1,rep,name=addresses"`
|
||||
|
||||
// conditions contains information about the current status of the endpoint.
|
||||
|
||||
@@ -38,6 +38,8 @@ message Endpoint {
|
||||
// 100. These are all assumed to be fungible and clients may choose to only
|
||||
// use the first element. Refer to: https://issue.k8s.io/106267
|
||||
// +listType=set
|
||||
// +required
|
||||
// +k8s:required
|
||||
repeated string addresses = 1;
|
||||
|
||||
// conditions contains information about the current status of the endpoint.
|
||||
|
||||
@@ -85,6 +85,8 @@ type Endpoint struct {
|
||||
// 100. These are all assumed to be fungible and clients may choose to only
|
||||
// use the first element. Refer to: https://issue.k8s.io/106267
|
||||
// +listType=set
|
||||
// +required
|
||||
// +k8s:required
|
||||
Addresses []string `json:"addresses" protobuf:"bytes,1,rep,name=addresses"`
|
||||
|
||||
// conditions contains information about the current status of the endpoint.
|
||||
|
||||
Reference in New Issue
Block a user