mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-18 04:54:54 +00:00
add declarative validation tests for ipaddress
This commit is contained in:
54
pkg/apis/networking/v1/zz_generated.validations.go
generated
54
pkg/apis/networking/v1/zz_generated.validations.go
generated
@@ -66,32 +66,6 @@ func RegisterValidations(scheme *runtime.Scheme) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_IPBlock validates an instance of IPBlock according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_IPBlock(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *networkingv1.IPBlock) (errs field.ErrorList) {
|
||||
// field networkingv1.IPBlock.CIDR
|
||||
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 && (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("cidr"), &obj.CIDR, safe.Field(oldObj, func(oldObj *networkingv1.IPBlock) *string { return &oldObj.CIDR }), oldObj != nil)...)
|
||||
|
||||
// field networkingv1.IPBlock.Except has no validation
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_IPAddress validates an instance of IPAddress according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_IPAddress(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *networkingv1.IPAddress) (errs field.ErrorList) {
|
||||
@@ -144,6 +118,32 @@ func Validate_IPAddressSpec(ctx context.Context, op operation.Operation, fldPath
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_IPBlock validates an instance of IPBlock according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_IPBlock(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *networkingv1.IPBlock) (errs field.ErrorList) {
|
||||
// field networkingv1.IPBlock.CIDR
|
||||
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 && (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("cidr"), &obj.CIDR, safe.Field(oldObj, func(oldObj *networkingv1.IPBlock) *string { return &oldObj.CIDR }), oldObj != nil)...)
|
||||
|
||||
// field networkingv1.IPBlock.Except has no validation
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_IngressClass validates an instance of IngressClass according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_IngressClass(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *networkingv1.IngressClass) (errs field.ErrorList) {
|
||||
@@ -418,4 +418,4 @@ func Validate_ParentReference(ctx context.Context, op operation.Operation, fldPa
|
||||
}(fldPath.Child("name"), &obj.Name, safe.Field(oldObj, func(oldObj *networkingv1.ParentReference) *string { return &oldObj.Name }), oldObj != nil)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,6 +91,10 @@ func Validate_IPAddressSpec(ctx context.Context, op operation.Operation, fldPath
|
||||
}
|
||||
// call field-attached validations
|
||||
earlyReturn := false
|
||||
if e := validate.Immutable(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
}
|
||||
if e := validate.RequiredPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
earlyReturn = true
|
||||
|
||||
@@ -785,7 +785,7 @@ func validateIPAddressParentReference(params *networking.ParentReference, fldPat
|
||||
allErrs := field.ErrorList{}
|
||||
|
||||
if params == nil {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("parentRef"), ""))
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("parentRef"), "").MarkCoveredByDeclarative())
|
||||
return allErrs
|
||||
}
|
||||
|
||||
@@ -799,7 +799,7 @@ func validateIPAddressParentReference(params *networking.ParentReference, fldPat
|
||||
|
||||
// resource is required
|
||||
if params.Resource == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("resource"), ""))
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("resource"), "").MarkCoveredByDeclarative())
|
||||
} else {
|
||||
for _, msg := range content.IsPathSegmentName(params.Resource) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("resource"), params.Resource, msg))
|
||||
@@ -808,7 +808,7 @@ func validateIPAddressParentReference(params *networking.ParentReference, fldPat
|
||||
|
||||
// name is required
|
||||
if params.Name == "" {
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
|
||||
allErrs = append(allErrs, field.Required(fldPath.Child("name"), "").MarkCoveredByDeclarative())
|
||||
} else {
|
||||
for _, msg := range content.IsPathSegmentName(params.Name) {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), params.Name, msg))
|
||||
@@ -828,7 +828,7 @@ func validateIPAddressParentReference(params *networking.ParentReference, fldPat
|
||||
func ValidateIPAddressUpdate(update, old *networking.IPAddress) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
allErrs = append(allErrs, apivalidation.ValidateObjectMetaUpdate(&update.ObjectMeta, &old.ObjectMeta, field.NewPath("metadata"))...)
|
||||
allErrs = append(allErrs, apivalidation.ValidateImmutableField(update.Spec.ParentRef, old.Spec.ParentRef, field.NewPath("spec").Child("parentRef"))...)
|
||||
allErrs = append(allErrs, apivalidation.ValidateImmutableField(update.Spec.ParentRef, old.Spec.ParentRef, field.NewPath("spec").Child("parentRef")).MarkCoveredByDeclarative().WithOrigin("immutable")...)
|
||||
return allErrs
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
Copyright 2025 The Kubernetes Authors.
|
||||
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.
|
||||
@@ -15,3 +15,208 @@ limitations under the License.
|
||||
*/
|
||||
|
||||
package ipaddress
|
||||
|
||||
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"
|
||||
networking "k8s.io/kubernetes/pkg/apis/networking"
|
||||
)
|
||||
|
||||
var apiVersions = []string{"v1beta1", "v1"}
|
||||
|
||||
func TestDeclarativeValidateIPAddress(t *testing.T) {
|
||||
for _, apiVersion := range apiVersions {
|
||||
t.Run(apiVersion, func(t *testing.T) {
|
||||
ctx := genericapirequest.WithRequestInfo(
|
||||
genericapirequest.NewDefaultContext(),
|
||||
&genericapirequest.RequestInfo{
|
||||
APIGroup: "networking.k8s.io",
|
||||
APIVersion: apiVersion,
|
||||
Resource: "ipaddresses",
|
||||
IsResourceRequest: true,
|
||||
Verb: "create",
|
||||
},
|
||||
)
|
||||
|
||||
testCases := map[string]struct {
|
||||
input networking.IPAddress
|
||||
expectedErrs field.ErrorList
|
||||
}{
|
||||
"valid": {
|
||||
input: mkValidIPAddress(),
|
||||
},
|
||||
"missing parentRef": {
|
||||
input: mkValidIPAddress(withNilParentRef),
|
||||
expectedErrs: field.ErrorList{
|
||||
field.Required(field.NewPath("spec", "parentRef"), ""),
|
||||
},
|
||||
},
|
||||
"missing parentRef resource": {
|
||||
input: mkValidIPAddress(withEmptyParentRefResource),
|
||||
expectedErrs: field.ErrorList{
|
||||
field.Required(field.NewPath("spec", "parentRef", "resource"), ""),
|
||||
},
|
||||
},
|
||||
"missing parentRef name": {
|
||||
input: mkValidIPAddress(withEmptyParentRefName),
|
||||
expectedErrs: field.ErrorList{
|
||||
field.Required(field.NewPath("spec", "parentRef", "name"), ""),
|
||||
},
|
||||
},
|
||||
}
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
apitesting.VerifyValidationEquivalence(
|
||||
t,
|
||||
ctx,
|
||||
&tc.input,
|
||||
Strategy.Validate,
|
||||
tc.expectedErrs,
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDeclarativeValidateIPAddressUpdate(t *testing.T) {
|
||||
for _, apiVersion := range apiVersions {
|
||||
t.Run(apiVersion, func(t *testing.T) {
|
||||
testCases := map[string]struct {
|
||||
oldObj networking.IPAddress
|
||||
updateObj networking.IPAddress
|
||||
expectedErrs field.ErrorList
|
||||
}{
|
||||
"valid update": {
|
||||
oldObj: mkValidIPAddress(withResourceVersion("1")),
|
||||
updateObj: mkValidIPAddress(withResourceVersion("1")),
|
||||
},
|
||||
"immutable parentRef": {
|
||||
oldObj: mkValidIPAddress(withResourceVersion("1")),
|
||||
updateObj: mkValidIPAddress(
|
||||
withResourceVersion("1"),
|
||||
withParentRefName("new-name"),
|
||||
),
|
||||
expectedErrs: field.ErrorList{
|
||||
func() *field.Error {
|
||||
e := field.Invalid(field.NewPath("spec", "parentRef"), &networking.ParentReference{
|
||||
Group: "apps",
|
||||
Resource: "deployments",
|
||||
Name: "new-name",
|
||||
}, "field is immutable")
|
||||
e.Origin = "immutable"
|
||||
return e
|
||||
}(),
|
||||
},
|
||||
},
|
||||
"set parentRef": {
|
||||
oldObj: mkValidIPAddress(withResourceVersion("1"), withNilParentRef),
|
||||
updateObj: mkValidIPAddress(
|
||||
withResourceVersion("2"),
|
||||
withParentRefName("new-name"),
|
||||
),
|
||||
expectedErrs: field.ErrorList{
|
||||
func() *field.Error {
|
||||
e := field.Invalid(field.NewPath("spec", "parentRef"), &networking.ParentReference{
|
||||
Group: "apps",
|
||||
Resource: "deployments",
|
||||
Name: "new-name",
|
||||
}, "field is immutable")
|
||||
e.Origin = "immutable"
|
||||
return e
|
||||
}(),
|
||||
},
|
||||
},
|
||||
"unset parentRef": {
|
||||
oldObj: mkValidIPAddress(withResourceVersion("1")),
|
||||
updateObj: mkValidIPAddress(
|
||||
withResourceVersion("1"),
|
||||
withNilParentRef,
|
||||
),
|
||||
expectedErrs: field.ErrorList{
|
||||
field.Required(field.NewPath("spec", "parentRef"), ""),
|
||||
func() *field.Error {
|
||||
e := field.Invalid(field.NewPath("spec", "parentRef"), nil, "field is immutable")
|
||||
e.Origin = "immutable"
|
||||
return e
|
||||
}(),
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for name, tc := range testCases {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
ctx := genericapirequest.WithRequestInfo(
|
||||
genericapirequest.NewDefaultContext(),
|
||||
&genericapirequest.RequestInfo{
|
||||
APIPrefix: "apis",
|
||||
APIGroup: "networking.k8s.io",
|
||||
APIVersion: apiVersion,
|
||||
Resource: "ipaddresses",
|
||||
Name: "192.168.1.1",
|
||||
IsResourceRequest: true,
|
||||
Verb: "update",
|
||||
},
|
||||
)
|
||||
|
||||
apitesting.VerifyUpdateValidationEquivalence(
|
||||
t,
|
||||
ctx,
|
||||
&tc.updateObj,
|
||||
&tc.oldObj,
|
||||
Strategy.ValidateUpdate,
|
||||
tc.expectedErrs,
|
||||
)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func mkValidIPAddress(tweaks ...func(obj *networking.IPAddress)) networking.IPAddress {
|
||||
obj := networking.IPAddress{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "192.168.1.1",
|
||||
},
|
||||
Spec: networking.IPAddressSpec{
|
||||
ParentRef: &networking.ParentReference{
|
||||
Group: "apps",
|
||||
Resource: "deployments",
|
||||
Name: "my-deployment",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tweak := range tweaks {
|
||||
tweak(&obj)
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
||||
func withNilParentRef(obj *networking.IPAddress) {
|
||||
obj.Spec.ParentRef = nil
|
||||
}
|
||||
|
||||
func withEmptyParentRefResource(obj *networking.IPAddress) {
|
||||
obj.Spec.ParentRef.Resource = ""
|
||||
}
|
||||
|
||||
func withEmptyParentRefName(obj *networking.IPAddress) {
|
||||
obj.Spec.ParentRef.Name = ""
|
||||
}
|
||||
|
||||
func withResourceVersion(rv string) func(*networking.IPAddress) {
|
||||
return func(obj *networking.IPAddress) {
|
||||
obj.ResourceVersion = rv
|
||||
}
|
||||
}
|
||||
|
||||
func withParentRefName(name string) func(*networking.IPAddress) {
|
||||
return func(obj *networking.IPAddress) {
|
||||
obj.Spec.ParentRef.Name = name
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ package ipaddress
|
||||
import (
|
||||
"context"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/operation"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
@@ -73,7 +74,7 @@ func (ipAddressStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.
|
||||
func (ipAddressStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||
ipAddress := obj.(*networking.IPAddress)
|
||||
err := validation.ValidateIPAddress(ipAddress)
|
||||
return err
|
||||
return rest.ValidateDeclarativelyWithMigrationChecks(ctx, legacyscheme.Scheme, ipAddress, nil, err, operation.Create)
|
||||
}
|
||||
|
||||
// Canonicalize normalizes the object after validation.
|
||||
@@ -91,7 +92,7 @@ func (ipAddressStrategy) ValidateUpdate(ctx context.Context, new, old runtime.Ob
|
||||
oldIPAddress := old.(*networking.IPAddress)
|
||||
errList := validation.ValidateIPAddress(newIPAddress)
|
||||
errList = append(errList, validation.ValidateIPAddressUpdate(newIPAddress, oldIPAddress)...)
|
||||
return errList
|
||||
return rest.ValidateDeclarativelyWithMigrationChecks(ctx, legacyscheme.Scheme, newIPAddress, oldIPAddress, errList, operation.Update)
|
||||
}
|
||||
|
||||
// AllowUnconditionalUpdate is the default update policy for IPAddress objects.
|
||||
|
||||
@@ -42,7 +42,17 @@ func newIPAddress() networking.IPAddress {
|
||||
}
|
||||
|
||||
func TestIPAddressStrategy(t *testing.T) {
|
||||
ctx := genericapirequest.NewDefaultContext()
|
||||
ctx := genericapirequest.WithRequestInfo(
|
||||
genericapirequest.NewDefaultContext(),
|
||||
&genericapirequest.RequestInfo{
|
||||
APIGroup: "networking.k8s.io",
|
||||
APIVersion: "v1",
|
||||
Resource: "ipaddresses",
|
||||
Name: "192.168.1.1",
|
||||
IsResourceRequest: true,
|
||||
Verb: "create",
|
||||
},
|
||||
)
|
||||
if Strategy.NamespaceScoped() {
|
||||
t.Errorf("ipAddress must not be namespace scoped")
|
||||
}
|
||||
|
||||
@@ -107,6 +107,8 @@ message IPAddressSpec {
|
||||
// ParentRef references the resource that an IPAddress is attached to.
|
||||
// An IPAddress must reference a parent object.
|
||||
// +required
|
||||
// +k8s:required
|
||||
// +k8s:immutable
|
||||
optional ParentReference parentRef = 1;
|
||||
}
|
||||
|
||||
@@ -595,6 +597,7 @@ message ParentReference {
|
||||
|
||||
// Resource is the resource of the object being referenced.
|
||||
// +required
|
||||
// +k8s:required
|
||||
optional string resource = 2;
|
||||
|
||||
// Namespace is the namespace of the object being referenced.
|
||||
@@ -603,6 +606,7 @@ message ParentReference {
|
||||
|
||||
// Name is the name of the object being referenced.
|
||||
// +required
|
||||
// +k8s:required
|
||||
optional string name = 4;
|
||||
}
|
||||
|
||||
|
||||
@@ -675,6 +675,7 @@ type IPAddressSpec struct {
|
||||
// An IPAddress must reference a parent object.
|
||||
// +required
|
||||
// +k8s:required
|
||||
// +k8s:immutable
|
||||
ParentRef *ParentReference `json:"parentRef,omitempty" protobuf:"bytes,1,opt,name=parentRef"`
|
||||
}
|
||||
|
||||
|
||||
@@ -108,6 +108,8 @@ message IPAddressSpec {
|
||||
// ParentRef references the resource that an IPAddress is attached to.
|
||||
// An IPAddress must reference a parent object.
|
||||
// +required
|
||||
// +k8s:required
|
||||
// +k8s:immutable
|
||||
optional ParentReference parentRef = 1;
|
||||
}
|
||||
|
||||
@@ -402,6 +404,7 @@ message ParentReference {
|
||||
|
||||
// Resource is the resource of the object being referenced.
|
||||
// +required
|
||||
// +k8s:required
|
||||
optional string resource = 2;
|
||||
|
||||
// Namespace is the namespace of the object being referenced.
|
||||
@@ -410,6 +413,7 @@ message ParentReference {
|
||||
|
||||
// Name is the name of the object being referenced.
|
||||
// +required
|
||||
// +k8s:required
|
||||
optional string name = 4;
|
||||
}
|
||||
|
||||
|
||||
@@ -457,6 +457,7 @@ type IPAddressSpec struct {
|
||||
// An IPAddress must reference a parent object.
|
||||
// +required
|
||||
// +k8s:required
|
||||
// +k8s:immutable
|
||||
ParentRef *ParentReference `json:"parentRef,omitempty" protobuf:"bytes,1,opt,name=parentRef"`
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user