Add declarative validation tests for EndpointSlice addressType required

This commit is contained in:
CLBRITTON2
2026-01-09 08:48:53 -05:00
parent a6618afc29
commit 1ee5d304a9
8 changed files with 59 additions and 3 deletions

View File

@@ -91,7 +91,25 @@ func Validate_Endpoint(ctx context.Context, op operation.Operation, fldPath *fie
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.AddressType
errs = append(errs,
func(fldPath *field.Path, obj, oldObj *discoveryv1.AddressType, oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
return nil
}
// call field-attached validations
earlyReturn := false
if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
return
}(fldPath.Child("addressType"), &obj.AddressType, safe.Field(oldObj, func(oldObj *discoveryv1.EndpointSlice) *discoveryv1.AddressType { return &oldObj.AddressType }), oldObj != nil)...)
// field discoveryv1.EndpointSlice.Endpoints
errs = append(errs,

View File

@@ -90,7 +90,25 @@ func Validate_Endpoint(ctx context.Context, op operation.Operation, fldPath *fie
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.AddressType
errs = append(errs,
func(fldPath *field.Path, obj, oldObj *discoveryv1beta1.AddressType, oldValueCorrelated bool) (errs field.ErrorList) {
// don't revalidate unchanged data
if oldValueCorrelated && op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
return nil
}
// call field-attached validations
earlyReturn := false
if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
errs = append(errs, e...)
earlyReturn = true
}
if earlyReturn {
return // do not proceed
}
return
}(fldPath.Child("addressType"), &obj.AddressType, safe.Field(oldObj, func(oldObj *discoveryv1beta1.EndpointSlice) *discoveryv1beta1.AddressType { return &oldObj.AddressType }), oldObj != nil)...)
// field discoveryv1beta1.EndpointSlice.Endpoints
errs = append(errs,

View File

@@ -207,7 +207,7 @@ func validateAddressType(addressType discovery.AddressType) field.ErrorList {
allErrs := field.ErrorList{}
if addressType == "" {
allErrs = append(allErrs, field.Required(field.NewPath("addressType"), ""))
allErrs = append(allErrs, field.Required(field.NewPath("addressType"), "").MarkCoveredByDeclarative())
} else if !supportedAddressTypes.Has(addressType) {
allErrs = append(allErrs, field.NotSupported(field.NewPath("addressType"), addressType, sets.List(supportedAddressTypes)))
}

View File

@@ -70,6 +70,12 @@ func testDeclarativeValidate(t *testing.T, apiVersion string) {
field.TooMany(field.NewPath("endpoints").Index(0).Child("addresses"), 101, 100).WithOrigin("maxItems"),
},
},
"invalid missing addressType": {
input: mkValidEndpointSlice(tweakAddressType("")),
expectedErrs: field.ErrorList{
field.Required(field.NewPath("addressType"), ""),
},
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
@@ -164,3 +170,9 @@ func tweakAddresses(count int) func(*discovery.EndpointSlice) {
obj.Endpoints[0].Addresses = addrs
}
}
func tweakAddressType(addrType discovery.AddressType) func(*discovery.EndpointSlice) {
return func(obj *discovery.EndpointSlice) {
obj.AddressType = addrType
}
}

View File

@@ -184,6 +184,8 @@ message EndpointSlice {
// The EndpointSlice controller only generates, and kube-proxy only processes,
// slices of addressType "IPv4" and "IPv6". No semantics are defined for
// the "FQDN" type.
// +required
// +k8s:required
optional string addressType = 4;
// endpoints is a list of unique endpoints in this slice. Each slice may

View File

@@ -48,6 +48,8 @@ type EndpointSlice struct {
// The EndpointSlice controller only generates, and kube-proxy only processes,
// slices of addressType "IPv4" and "IPv6". No semantics are defined for
// the "FQDN" type.
// +required
// +k8s:required
AddressType AddressType `json:"addressType" protobuf:"bytes,4,rep,name=addressType"`
// endpoints is a list of unique endpoints in this slice. Each slice may

View File

@@ -170,6 +170,8 @@ message EndpointSlice {
// * IPv4: Represents an IPv4 Address.
// * IPv6: Represents an IPv6 Address.
// * FQDN: Represents a Fully Qualified Domain Name.
// +required
// +k8s:required
optional string addressType = 4;
// endpoints is a list of unique endpoints in this slice. Each slice may

View File

@@ -45,6 +45,8 @@ type EndpointSlice struct {
// * IPv4: Represents an IPv4 Address.
// * IPv6: Represents an IPv6 Address.
// * FQDN: Represents a Fully Qualified Domain Name.
// +required
// +k8s:required
AddressType AddressType `json:"addressType" protobuf:"bytes,4,rep,name=addressType"`
// endpoints is a list of unique endpoints in this slice. Each slice may