mirror of
https://github.com/kubernetes/kubernetes.git
synced 2026-06-09 08:55:55 -04:00
Merge pull request #134113 from jpbetz/resource-claim-status-pool
Enable Declarative Validation Pool status field of ResourceClaim
This commit is contained in:
commit
4f72f0fdbf
19 changed files with 1141 additions and 8 deletions
193
pkg/apis/resource/v1/zz_generated.validations.go
generated
193
pkg/apis/resource/v1/zz_generated.validations.go
generated
|
|
@ -22,7 +22,16 @@ limitations under the License.
|
|||
package v1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
resourcev1 "k8s.io/api/resource/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) }
|
||||
|
|
@ -30,5 +39,189 @@ 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 ResourceClaim
|
||||
scheme.AddValidationFunc((*resourcev1.ResourceClaim)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/", "/status":
|
||||
return Validate_ResourceClaim(ctx, op, nil /* fldPath */, obj.(*resourcev1.ResourceClaim), safe.Cast[*resourcev1.ResourceClaim](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
// type ResourceClaimList
|
||||
scheme.AddValidationFunc((*resourcev1.ResourceClaimList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_ResourceClaimList(ctx, op, nil /* fldPath */, obj.(*resourcev1.ResourceClaimList), safe.Cast[*resourcev1.ResourceClaimList](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_AllocationResult validates an instance of AllocationResult according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_AllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.AllocationResult) (errs field.ErrorList) {
|
||||
// field resourcev1.AllocationResult.Devices
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *resourcev1.DeviceAllocationResult) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call the type's validation function
|
||||
errs = append(errs, Validate_DeviceAllocationResult(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("devices"), &obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1.AllocationResult) *resourcev1.DeviceAllocationResult { return &oldObj.Devices }))...)
|
||||
|
||||
// field resourcev1.AllocationResult.NodeSelector has no validation
|
||||
// field resourcev1.AllocationResult.AllocationTimestamp has no validation
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_DeviceAllocationResult validates an instance of DeviceAllocationResult according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_DeviceAllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceAllocationResult) (errs field.ErrorList) {
|
||||
// field resourcev1.DeviceAllocationResult.Results
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceRequestAllocationResult) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if 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_DeviceRequestAllocationResult)...)
|
||||
return
|
||||
}(fldPath.Child("results"), obj.Results, safe.Field(oldObj, func(oldObj *resourcev1.DeviceAllocationResult) []resourcev1.DeviceRequestAllocationResult {
|
||||
return oldObj.Results
|
||||
}))...)
|
||||
|
||||
// field resourcev1.DeviceAllocationResult.Config has no validation
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_DeviceRequestAllocationResult validates an instance of DeviceRequestAllocationResult according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_DeviceRequestAllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceRequestAllocationResult) (errs field.ErrorList) {
|
||||
// field resourcev1.DeviceRequestAllocationResult.Request has no validation
|
||||
// field resourcev1.DeviceRequestAllocationResult.Driver has no validation
|
||||
|
||||
// field resourcev1.DeviceRequestAllocationResult.Pool
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
return // do not proceed
|
||||
}
|
||||
errs = append(errs, validate.ResourcePoolName(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("pool"), &obj.Pool, safe.Field(oldObj, func(oldObj *resourcev1.DeviceRequestAllocationResult) *string { return &oldObj.Pool }))...)
|
||||
|
||||
// field resourcev1.DeviceRequestAllocationResult.Device has no validation
|
||||
// field resourcev1.DeviceRequestAllocationResult.AdminAccess has no validation
|
||||
// field resourcev1.DeviceRequestAllocationResult.Tolerations has no validation
|
||||
// field resourcev1.DeviceRequestAllocationResult.BindingConditions has no validation
|
||||
// field resourcev1.DeviceRequestAllocationResult.BindingFailureConditions has no validation
|
||||
// field resourcev1.DeviceRequestAllocationResult.ShareID has no validation
|
||||
// field resourcev1.DeviceRequestAllocationResult.ConsumedCapacity has no validation
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_ResourceClaim validates an instance of ResourceClaim according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_ResourceClaim(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.ResourceClaim) (errs field.ErrorList) {
|
||||
// field resourcev1.ResourceClaim.TypeMeta has no validation
|
||||
// field resourcev1.ResourceClaim.ObjectMeta has no validation
|
||||
// field resourcev1.ResourceClaim.Spec has no validation
|
||||
|
||||
// field resourcev1.ResourceClaim.Status
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *resourcev1.ResourceClaimStatus) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call the type's validation function
|
||||
errs = append(errs, Validate_ResourceClaimStatus(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("status"), &obj.Status, safe.Field(oldObj, func(oldObj *resourcev1.ResourceClaim) *resourcev1.ResourceClaimStatus { return &oldObj.Status }))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_ResourceClaimList validates an instance of ResourceClaimList according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_ResourceClaimList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.ResourceClaimList) (errs field.ErrorList) {
|
||||
// field resourcev1.ResourceClaimList.TypeMeta has no validation
|
||||
// field resourcev1.ResourceClaimList.ListMeta has no validation
|
||||
|
||||
// field resourcev1.ResourceClaimList.Items
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1.ResourceClaim) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if 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_ResourceClaim)...)
|
||||
return
|
||||
}(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1.ResourceClaimList) []resourcev1.ResourceClaim { return oldObj.Items }))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_ResourceClaimStatus validates an instance of ResourceClaimStatus according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_ResourceClaimStatus(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.ResourceClaimStatus) (errs field.ErrorList) {
|
||||
// field resourcev1.ResourceClaimStatus.Allocation
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *resourcev1.AllocationResult) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
return // do not proceed
|
||||
}
|
||||
// call the type's validation function
|
||||
errs = append(errs, Validate_AllocationResult(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("allocation"), obj.Allocation, safe.Field(oldObj, func(oldObj *resourcev1.ResourceClaimStatus) *resourcev1.AllocationResult { return oldObj.Allocation }))...)
|
||||
|
||||
// field resourcev1.ResourceClaimStatus.ReservedFor
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1.ResourceClaimConsumerReference) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("reservedFor"), obj.ReservedFor, safe.Field(oldObj, func(oldObj *resourcev1.ResourceClaimStatus) []resourcev1.ResourceClaimConsumerReference {
|
||||
return oldObj.ReservedFor
|
||||
}))...)
|
||||
|
||||
// field resourcev1.ResourceClaimStatus.Devices
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1.AllocatedDeviceStatus) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("devices"), obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1.ResourceClaimStatus) []resourcev1.AllocatedDeviceStatus { return oldObj.Devices }))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
|
|
|||
201
pkg/apis/resource/v1beta1/zz_generated.validations.go
generated
201
pkg/apis/resource/v1beta1/zz_generated.validations.go
generated
|
|
@ -22,7 +22,16 @@ limitations under the License.
|
|||
package v1beta1
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
resourcev1beta1 "k8s.io/api/resource/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) }
|
||||
|
|
@ -30,5 +39,197 @@ 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 ResourceClaim
|
||||
scheme.AddValidationFunc((*resourcev1beta1.ResourceClaim)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/", "/status":
|
||||
return Validate_ResourceClaim(ctx, op, nil /* fldPath */, obj.(*resourcev1beta1.ResourceClaim), safe.Cast[*resourcev1beta1.ResourceClaim](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
// type ResourceClaimList
|
||||
scheme.AddValidationFunc((*resourcev1beta1.ResourceClaimList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_ResourceClaimList(ctx, op, nil /* fldPath */, obj.(*resourcev1beta1.ResourceClaimList), safe.Cast[*resourcev1beta1.ResourceClaimList](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_AllocationResult validates an instance of AllocationResult according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_AllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.AllocationResult) (errs field.ErrorList) {
|
||||
// field resourcev1beta1.AllocationResult.Devices
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceAllocationResult) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call the type's validation function
|
||||
errs = append(errs, Validate_DeviceAllocationResult(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("devices"), &obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1beta1.AllocationResult) *resourcev1beta1.DeviceAllocationResult {
|
||||
return &oldObj.Devices
|
||||
}))...)
|
||||
|
||||
// field resourcev1beta1.AllocationResult.NodeSelector has no validation
|
||||
// field resourcev1beta1.AllocationResult.AllocationTimestamp has no validation
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_DeviceAllocationResult validates an instance of DeviceAllocationResult according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_DeviceAllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceAllocationResult) (errs field.ErrorList) {
|
||||
// field resourcev1beta1.DeviceAllocationResult.Results
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceRequestAllocationResult) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if 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_DeviceRequestAllocationResult)...)
|
||||
return
|
||||
}(fldPath.Child("results"), obj.Results, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceAllocationResult) []resourcev1beta1.DeviceRequestAllocationResult {
|
||||
return oldObj.Results
|
||||
}))...)
|
||||
|
||||
// field resourcev1beta1.DeviceAllocationResult.Config has no validation
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_DeviceRequestAllocationResult validates an instance of DeviceRequestAllocationResult according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_DeviceRequestAllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceRequestAllocationResult) (errs field.ErrorList) {
|
||||
// field resourcev1beta1.DeviceRequestAllocationResult.Request has no validation
|
||||
// field resourcev1beta1.DeviceRequestAllocationResult.Driver has no validation
|
||||
|
||||
// field resourcev1beta1.DeviceRequestAllocationResult.Pool
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
return // do not proceed
|
||||
}
|
||||
errs = append(errs, validate.ResourcePoolName(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("pool"), &obj.Pool, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceRequestAllocationResult) *string { return &oldObj.Pool }))...)
|
||||
|
||||
// field resourcev1beta1.DeviceRequestAllocationResult.Device has no validation
|
||||
// field resourcev1beta1.DeviceRequestAllocationResult.AdminAccess has no validation
|
||||
// field resourcev1beta1.DeviceRequestAllocationResult.Tolerations has no validation
|
||||
// field resourcev1beta1.DeviceRequestAllocationResult.BindingConditions has no validation
|
||||
// field resourcev1beta1.DeviceRequestAllocationResult.BindingFailureConditions has no validation
|
||||
// field resourcev1beta1.DeviceRequestAllocationResult.ShareID has no validation
|
||||
// field resourcev1beta1.DeviceRequestAllocationResult.ConsumedCapacity has no validation
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_ResourceClaim validates an instance of ResourceClaim according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_ResourceClaim(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceClaim) (errs field.ErrorList) {
|
||||
// field resourcev1beta1.ResourceClaim.TypeMeta has no validation
|
||||
// field resourcev1beta1.ResourceClaim.ObjectMeta has no validation
|
||||
// field resourcev1beta1.ResourceClaim.Spec has no validation
|
||||
|
||||
// field resourcev1beta1.ResourceClaim.Status
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceClaimStatus) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call the type's validation function
|
||||
errs = append(errs, Validate_ResourceClaimStatus(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("status"), &obj.Status, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceClaim) *resourcev1beta1.ResourceClaimStatus {
|
||||
return &oldObj.Status
|
||||
}))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_ResourceClaimList validates an instance of ResourceClaimList according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_ResourceClaimList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceClaimList) (errs field.ErrorList) {
|
||||
// field resourcev1beta1.ResourceClaimList.TypeMeta has no validation
|
||||
// field resourcev1beta1.ResourceClaimList.ListMeta has no validation
|
||||
|
||||
// field resourcev1beta1.ResourceClaimList.Items
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1beta1.ResourceClaim) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if 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_ResourceClaim)...)
|
||||
return
|
||||
}(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceClaimList) []resourcev1beta1.ResourceClaim { return oldObj.Items }))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_ResourceClaimStatus validates an instance of ResourceClaimStatus according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_ResourceClaimStatus(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.ResourceClaimStatus) (errs field.ErrorList) {
|
||||
// field resourcev1beta1.ResourceClaimStatus.Allocation
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *resourcev1beta1.AllocationResult) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
return // do not proceed
|
||||
}
|
||||
// call the type's validation function
|
||||
errs = append(errs, Validate_AllocationResult(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("allocation"), obj.Allocation, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceClaimStatus) *resourcev1beta1.AllocationResult {
|
||||
return oldObj.Allocation
|
||||
}))...)
|
||||
|
||||
// field resourcev1beta1.ResourceClaimStatus.ReservedFor
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1beta1.ResourceClaimConsumerReference) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("reservedFor"), obj.ReservedFor, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceClaimStatus) []resourcev1beta1.ResourceClaimConsumerReference {
|
||||
return oldObj.ReservedFor
|
||||
}))...)
|
||||
|
||||
// field resourcev1beta1.ResourceClaimStatus.Devices
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1beta1.AllocatedDeviceStatus) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("devices"), obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1beta1.ResourceClaimStatus) []resourcev1beta1.AllocatedDeviceStatus {
|
||||
return oldObj.Devices
|
||||
}))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
|
|
|||
201
pkg/apis/resource/v1beta2/zz_generated.validations.go
generated
201
pkg/apis/resource/v1beta2/zz_generated.validations.go
generated
|
|
@ -22,7 +22,16 @@ limitations under the License.
|
|||
package v1beta2
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
resourcev1beta2 "k8s.io/api/resource/v1beta2"
|
||||
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) }
|
||||
|
|
@ -30,5 +39,197 @@ 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 ResourceClaim
|
||||
scheme.AddValidationFunc((*resourcev1beta2.ResourceClaim)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/", "/status":
|
||||
return Validate_ResourceClaim(ctx, op, nil /* fldPath */, obj.(*resourcev1beta2.ResourceClaim), safe.Cast[*resourcev1beta2.ResourceClaim](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
// type ResourceClaimList
|
||||
scheme.AddValidationFunc((*resourcev1beta2.ResourceClaimList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_ResourceClaimList(ctx, op, nil /* fldPath */, obj.(*resourcev1beta2.ResourceClaimList), safe.Cast[*resourcev1beta2.ResourceClaimList](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_AllocationResult validates an instance of AllocationResult according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_AllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.AllocationResult) (errs field.ErrorList) {
|
||||
// field resourcev1beta2.AllocationResult.Devices
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceAllocationResult) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call the type's validation function
|
||||
errs = append(errs, Validate_DeviceAllocationResult(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("devices"), &obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1beta2.AllocationResult) *resourcev1beta2.DeviceAllocationResult {
|
||||
return &oldObj.Devices
|
||||
}))...)
|
||||
|
||||
// field resourcev1beta2.AllocationResult.NodeSelector has no validation
|
||||
// field resourcev1beta2.AllocationResult.AllocationTimestamp has no validation
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_DeviceAllocationResult validates an instance of DeviceAllocationResult according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_DeviceAllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceAllocationResult) (errs field.ErrorList) {
|
||||
// field resourcev1beta2.DeviceAllocationResult.Results
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceRequestAllocationResult) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if 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_DeviceRequestAllocationResult)...)
|
||||
return
|
||||
}(fldPath.Child("results"), obj.Results, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceAllocationResult) []resourcev1beta2.DeviceRequestAllocationResult {
|
||||
return oldObj.Results
|
||||
}))...)
|
||||
|
||||
// field resourcev1beta2.DeviceAllocationResult.Config has no validation
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_DeviceRequestAllocationResult validates an instance of DeviceRequestAllocationResult according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_DeviceRequestAllocationResult(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceRequestAllocationResult) (errs field.ErrorList) {
|
||||
// field resourcev1beta2.DeviceRequestAllocationResult.Request has no validation
|
||||
// field resourcev1beta2.DeviceRequestAllocationResult.Driver has no validation
|
||||
|
||||
// field resourcev1beta2.DeviceRequestAllocationResult.Pool
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
if e := validate.RequiredValue(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
return // do not proceed
|
||||
}
|
||||
errs = append(errs, validate.ResourcePoolName(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("pool"), &obj.Pool, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceRequestAllocationResult) *string { return &oldObj.Pool }))...)
|
||||
|
||||
// field resourcev1beta2.DeviceRequestAllocationResult.Device has no validation
|
||||
// field resourcev1beta2.DeviceRequestAllocationResult.AdminAccess has no validation
|
||||
// field resourcev1beta2.DeviceRequestAllocationResult.Tolerations has no validation
|
||||
// field resourcev1beta2.DeviceRequestAllocationResult.BindingConditions has no validation
|
||||
// field resourcev1beta2.DeviceRequestAllocationResult.BindingFailureConditions has no validation
|
||||
// field resourcev1beta2.DeviceRequestAllocationResult.ShareID has no validation
|
||||
// field resourcev1beta2.DeviceRequestAllocationResult.ConsumedCapacity has no validation
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_ResourceClaim validates an instance of ResourceClaim according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_ResourceClaim(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceClaim) (errs field.ErrorList) {
|
||||
// field resourcev1beta2.ResourceClaim.TypeMeta has no validation
|
||||
// field resourcev1beta2.ResourceClaim.ObjectMeta has no validation
|
||||
// field resourcev1beta2.ResourceClaim.Spec has no validation
|
||||
|
||||
// field resourcev1beta2.ResourceClaim.Status
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceClaimStatus) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call the type's validation function
|
||||
errs = append(errs, Validate_ResourceClaimStatus(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("status"), &obj.Status, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceClaim) *resourcev1beta2.ResourceClaimStatus {
|
||||
return &oldObj.Status
|
||||
}))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_ResourceClaimList validates an instance of ResourceClaimList according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_ResourceClaimList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceClaimList) (errs field.ErrorList) {
|
||||
// field resourcev1beta2.ResourceClaimList.TypeMeta has no validation
|
||||
// field resourcev1beta2.ResourceClaimList.ListMeta has no validation
|
||||
|
||||
// field resourcev1beta2.ResourceClaimList.Items
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1beta2.ResourceClaim) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if 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_ResourceClaim)...)
|
||||
return
|
||||
}(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceClaimList) []resourcev1beta2.ResourceClaim { return oldObj.Items }))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_ResourceClaimStatus validates an instance of ResourceClaimStatus according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_ResourceClaimStatus(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.ResourceClaimStatus) (errs field.ErrorList) {
|
||||
// field resourcev1beta2.ResourceClaimStatus.Allocation
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *resourcev1beta2.AllocationResult) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
if e := validate.OptionalPointer(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
return // do not proceed
|
||||
}
|
||||
// call the type's validation function
|
||||
errs = append(errs, Validate_AllocationResult(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("allocation"), obj.Allocation, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceClaimStatus) *resourcev1beta2.AllocationResult {
|
||||
return oldObj.Allocation
|
||||
}))...)
|
||||
|
||||
// field resourcev1beta2.ResourceClaimStatus.ReservedFor
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1beta2.ResourceClaimConsumerReference) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("reservedFor"), obj.ReservedFor, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceClaimStatus) []resourcev1beta2.ResourceClaimConsumerReference {
|
||||
return oldObj.ReservedFor
|
||||
}))...)
|
||||
|
||||
// field resourcev1beta2.ResourceClaimStatus.Devices
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1beta2.AllocatedDeviceStatus) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && equality.Semantic.DeepEqual(obj, oldObj) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("devices"), obj.Devices, safe.Field(oldObj, func(oldObj *resourcev1beta2.ResourceClaimStatus) []resourcev1beta2.AllocatedDeviceStatus {
|
||||
return oldObj.Devices
|
||||
}))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
apiequality "k8s.io/apimachinery/pkg/api/equality"
|
||||
apiresource "k8s.io/apimachinery/pkg/api/resource"
|
||||
|
|
@ -66,11 +67,11 @@ func validatePoolName(name string, fldPath *field.Path) field.ErrorList {
|
|||
allErrs = append(allErrs, field.Required(fldPath, ""))
|
||||
} else {
|
||||
if len(name) > resource.PoolNameMaxLength {
|
||||
allErrs = append(allErrs, field.TooLong(fldPath, "" /*unused*/, resource.PoolNameMaxLength))
|
||||
allErrs = append(allErrs, field.TooLong(fldPath, "" /*unused*/, resource.PoolNameMaxLength).WithOrigin("format=k8s-resource-pool-name"))
|
||||
}
|
||||
parts := strings.Split(name, "/")
|
||||
for _, part := range parts {
|
||||
allErrs = append(allErrs, corevalidation.ValidateDNS1123Subdomain(part, fldPath)...)
|
||||
allErrs = append(allErrs, corevalidation.ValidateDNS1123Subdomain(part, fldPath).WithOrigin("format=k8s-resource-pool-name")...)
|
||||
}
|
||||
}
|
||||
return allErrs
|
||||
|
|
@ -1207,7 +1208,7 @@ func truncateIfTooLong(str string, maxLen int) string {
|
|||
func validateDeviceStatus(device resource.AllocatedDeviceStatus, fldPath *field.Path, allocatedDevices sets.Set[structured.SharedDeviceID]) field.ErrorList {
|
||||
var allErrs field.ErrorList
|
||||
allErrs = append(allErrs, validateDriverName(device.Driver, fldPath.Child("driver"))...)
|
||||
allErrs = append(allErrs, validatePoolName(device.Pool, fldPath.Child("pool"))...)
|
||||
allErrs = append(allErrs, validatePoolName(device.Pool, fldPath.Child("pool")).MarkCoveredByDeclarative()...)
|
||||
allErrs = append(allErrs, validateDeviceName(device.Device, fldPath.Child("device"))...)
|
||||
if device.ShareID != nil {
|
||||
allErrs = append(allErrs, validateUID(*device.ShareID, fldPath.Child("shareID"))...)
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ limitations under the License.
|
|||
package resourceclaim
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
|
|
@ -100,11 +101,87 @@ func testDeclarativeValidateUpdate(t *testing.T, apiVersion string) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestValidateStatusUpdateForDeclarative(t *testing.T) {
|
||||
fakeClient := fake.NewClientset()
|
||||
mockNSClient := fakeClient.CoreV1().Namespaces()
|
||||
Strategy := NewStrategy(mockNSClient)
|
||||
strategy := NewStatusStrategy(Strategy)
|
||||
|
||||
ctx := genericapirequest.WithRequestInfo(genericapirequest.NewDefaultContext(), &genericapirequest.RequestInfo{
|
||||
APIGroup: "resource.k8s.io",
|
||||
APIVersion: "v1",
|
||||
Subresource: "status",
|
||||
})
|
||||
poolPath := field.NewPath("status", "allocation", "devices", "results").Index(0).Child("pool")
|
||||
testCases := map[string]struct {
|
||||
old resource.ResourceClaim
|
||||
update resource.ResourceClaim
|
||||
expectedErrs field.ErrorList
|
||||
}{
|
||||
"valid pool name": {
|
||||
old: mkValidResourceClaim(),
|
||||
update: tweakStatusDeviceRequestAllocationResultPool(mkResourceClaimWithStatus(), "dra.example.com/pool-a"),
|
||||
},
|
||||
"valid pool name, max length": {
|
||||
old: mkValidResourceClaim(),
|
||||
update: tweakStatusDeviceRequestAllocationResultPool(mkResourceClaimWithStatus(), strings.Repeat("a", 63)+"."+strings.Repeat("b", 63)+"."+strings.Repeat("c", 63)+"."+strings.Repeat("d", 55)),
|
||||
},
|
||||
"invalid pool name, required": {
|
||||
old: mkValidResourceClaim(),
|
||||
update: tweakStatusDeviceRequestAllocationResultPool(mkResourceClaimWithStatus(), ""),
|
||||
expectedErrs: field.ErrorList{
|
||||
field.Required(poolPath, ""),
|
||||
},
|
||||
},
|
||||
"invalid pool name, too long": {
|
||||
old: mkValidResourceClaim(),
|
||||
update: tweakStatusDeviceRequestAllocationResultPool(mkResourceClaimWithStatus(), strings.Repeat("a", 253)+"/"+strings.Repeat("a", 253)),
|
||||
expectedErrs: field.ErrorList{
|
||||
field.TooLong(poolPath, "", 253).WithOrigin("format=k8s-resource-pool-name"),
|
||||
},
|
||||
},
|
||||
"invalid pool name, format": {
|
||||
old: mkValidResourceClaim(),
|
||||
update: tweakStatusDeviceRequestAllocationResultPool(mkResourceClaimWithStatus(), "a/Not_Valid"),
|
||||
expectedErrs: field.ErrorList{
|
||||
field.Invalid(poolPath, "Not_Valid", "").WithOrigin("format=k8s-resource-pool-name"),
|
||||
},
|
||||
},
|
||||
"invalid pool name, leading slash": {
|
||||
old: mkValidResourceClaim(),
|
||||
update: tweakStatusDeviceRequestAllocationResultPool(mkResourceClaimWithStatus(), "/a"),
|
||||
expectedErrs: field.ErrorList{
|
||||
field.Invalid(poolPath, "", "").WithOrigin("format=k8s-resource-pool-name"),
|
||||
},
|
||||
},
|
||||
"invalid pool name, trailing slash": {
|
||||
old: mkValidResourceClaim(),
|
||||
update: tweakStatusDeviceRequestAllocationResultPool(mkResourceClaimWithStatus(), "a/"),
|
||||
expectedErrs: field.ErrorList{
|
||||
field.Invalid(poolPath, "", "").WithOrigin("format=k8s-resource-pool-name"),
|
||||
},
|
||||
},
|
||||
"invalid pool name, double slash": {
|
||||
old: mkValidResourceClaim(),
|
||||
update: tweakStatusDeviceRequestAllocationResultPool(mkResourceClaimWithStatus(), "a//b"),
|
||||
expectedErrs: field.ErrorList{
|
||||
field.Invalid(poolPath, "", "").WithOrigin("format=k8s-resource-pool-name"),
|
||||
},
|
||||
},
|
||||
}
|
||||
for k, tc := range testCases {
|
||||
t.Run(k, func(t *testing.T) {
|
||||
apitesting.VerifyUpdateValidationEquivalence(t, ctx, &tc.update, &tc.old, strategy.ValidateUpdate, tc.expectedErrs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func mkValidResourceClaim() resource.ResourceClaim {
|
||||
return resource.ResourceClaim{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "valid-claim",
|
||||
Namespace: "default",
|
||||
Name: "valid-claim",
|
||||
Namespace: "default",
|
||||
ResourceVersion: "0",
|
||||
},
|
||||
Spec: resource.ResourceClaimSpec{
|
||||
Devices: resource.DeviceClaim{
|
||||
|
|
@ -121,3 +198,47 @@ func mkValidResourceClaim() resource.ResourceClaim {
|
|||
},
|
||||
}
|
||||
}
|
||||
|
||||
func mkResourceClaimWithStatus() resource.ResourceClaim {
|
||||
return resource.ResourceClaim{
|
||||
ObjectMeta: v1.ObjectMeta{
|
||||
Name: "valid-claim",
|
||||
Namespace: "default",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Spec: resource.ResourceClaimSpec{
|
||||
Devices: resource.DeviceClaim{
|
||||
Requests: []resource.DeviceRequest{
|
||||
{
|
||||
Name: "req-0",
|
||||
Exactly: &resource.ExactDeviceRequest{
|
||||
DeviceClassName: "class",
|
||||
AllocationMode: resource.DeviceAllocationModeAll,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Status: resource.ResourceClaimStatus{
|
||||
Allocation: &resource.AllocationResult{
|
||||
Devices: resource.DeviceAllocationResult{
|
||||
Results: []resource.DeviceRequestAllocationResult{
|
||||
{
|
||||
Request: "req-0",
|
||||
Driver: "dra.example.com",
|
||||
Pool: "pool-0",
|
||||
Device: "device-0",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func tweakStatusDeviceRequestAllocationResultPool(obj resource.ResourceClaim, pool string) resource.ResourceClaim {
|
||||
for i := range obj.Status.Allocation.Devices.Results {
|
||||
obj.Status.Allocation.Devices.Results[i].Pool = pool
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
|
||||
"sigs.k8s.io/structured-merge-diff/v6/fieldpath"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/fields"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
|
|
@ -40,7 +42,6 @@ import (
|
|||
"k8s.io/kubernetes/pkg/features"
|
||||
resourceutils "k8s.io/kubernetes/pkg/registry/resource"
|
||||
"k8s.io/utils/ptr"
|
||||
"sigs.k8s.io/structured-merge-diff/v6/fieldpath"
|
||||
)
|
||||
|
||||
// resourceclaimStrategy implements behavior for ResourceClaim objects
|
||||
|
|
@ -207,8 +208,26 @@ func (r *resourceclaimStatusStrategy) ValidateUpdate(ctx context.Context, obj, o
|
|||
if oldClaim.Status.Allocation != nil {
|
||||
oldAllocationResult = oldClaim.Status.Allocation.Devices.Results
|
||||
}
|
||||
allErrs := resourceutils.AuthorizedForAdminStatus(ctx, newAllocationResult, oldAllocationResult, newClaim.Namespace, r.nsClient)
|
||||
return append(allErrs, validation.ValidateResourceClaimStatusUpdate(newClaim, oldClaim)...)
|
||||
errs := resourceutils.AuthorizedForAdminStatus(ctx, newAllocationResult, oldAllocationResult, newClaim.Namespace, r.nsClient)
|
||||
errs = append(errs, validation.ValidateResourceClaimStatusUpdate(newClaim, oldClaim)...)
|
||||
|
||||
// If DeclarativeValidation feature gate is enabled, also run declarative validation
|
||||
if utilfeature.DefaultFeatureGate.Enabled(features.DeclarativeValidation) {
|
||||
// Determine if takeover is enabled
|
||||
takeover := utilfeature.DefaultFeatureGate.Enabled(features.DeclarativeValidationTakeover)
|
||||
|
||||
// Run declarative update validation with panic recovery
|
||||
declarativeErrs := rest.ValidateUpdateDeclaratively(ctx, legacyscheme.Scheme, newClaim, oldClaim, rest.WithTakeover(takeover))
|
||||
// Compare imperative and declarative errors and emit metric if there's a mismatch
|
||||
const validationIdentifier = "resourceclaim_status_update"
|
||||
rest.CompareDeclarativeErrorsAndEmitMismatches(ctx, errs, declarativeErrs, takeover, validationIdentifier)
|
||||
|
||||
// Only apply declarative errors if takeover is enabled
|
||||
if takeover {
|
||||
errs = append(errs.RemoveCoveredByDeclarative(), declarativeErrs...)
|
||||
}
|
||||
}
|
||||
return errs
|
||||
}
|
||||
|
||||
// WarningsOnUpdate returns warnings for the given update.
|
||||
|
|
|
|||
|
|
@ -826,6 +826,8 @@ message DeviceRequestAllocationResult {
|
|||
// DNS sub-domains separated by slashes.
|
||||
//
|
||||
// +required
|
||||
// +k8s:required
|
||||
// +k8s:format=k8s-resource-pool-name
|
||||
optional string pool = 3;
|
||||
|
||||
// Device references one device instance via its name in the driver's
|
||||
|
|
@ -1336,6 +1338,7 @@ message ResourceClaimStatus {
|
|||
// Allocation is set once the claim has been allocated successfully.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
optional AllocationResult allocation = 1;
|
||||
|
||||
// ReservedFor indicates which entities are currently allowed to use
|
||||
|
|
@ -1359,6 +1362,7 @@ message ResourceClaimStatus {
|
|||
// the future, but not reduced.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
// +listType=map
|
||||
// +listMapKey=uid
|
||||
// +patchStrategy=merge
|
||||
|
|
@ -1370,6 +1374,7 @@ message ResourceClaimStatus {
|
|||
// information. Entries are owned by their respective drivers.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
// +listType=map
|
||||
// +listMapKey=driver
|
||||
// +listMapKey=device
|
||||
|
|
|
|||
|
|
@ -678,6 +678,7 @@ type ResourceSliceList struct {
|
|||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +k8s:prerelease-lifecycle-gen:introduced=1.34
|
||||
// +k8s:supportsSubresource=/status
|
||||
|
||||
// ResourceClaim describes a request for access to resources in the cluster,
|
||||
// for use by workloads. For example, if a workload needs an accelerator device
|
||||
|
|
@ -1346,6 +1347,7 @@ type ResourceClaimStatus struct {
|
|||
// Allocation is set once the claim has been allocated successfully.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
Allocation *AllocationResult `json:"allocation,omitempty" protobuf:"bytes,1,opt,name=allocation"`
|
||||
|
||||
// ReservedFor indicates which entities are currently allowed to use
|
||||
|
|
@ -1369,6 +1371,7 @@ type ResourceClaimStatus struct {
|
|||
// the future, but not reduced.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
// +listType=map
|
||||
// +listMapKey=uid
|
||||
// +patchStrategy=merge
|
||||
|
|
@ -1385,6 +1388,7 @@ type ResourceClaimStatus struct {
|
|||
// information. Entries are owned by their respective drivers.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
// +listType=map
|
||||
// +listMapKey=driver
|
||||
// +listMapKey=device
|
||||
|
|
@ -1502,6 +1506,8 @@ type DeviceRequestAllocationResult struct {
|
|||
// DNS sub-domains separated by slashes.
|
||||
//
|
||||
// +required
|
||||
// +k8s:required
|
||||
// +k8s:format=k8s-resource-pool-name
|
||||
Pool string `json:"pool" protobuf:"bytes,3,name=pool"`
|
||||
|
||||
// Device references one device instance via its name in the driver's
|
||||
|
|
|
|||
|
|
@ -947,6 +947,8 @@ message DeviceRequestAllocationResult {
|
|||
// DNS sub-domains separated by slashes.
|
||||
//
|
||||
// +required
|
||||
// +k8s:required
|
||||
// +k8s:format=k8s-resource-pool-name
|
||||
optional string pool = 3;
|
||||
|
||||
// Device references one device instance via its name in the driver's
|
||||
|
|
@ -1350,6 +1352,7 @@ message ResourceClaimStatus {
|
|||
// Allocation is set once the claim has been allocated successfully.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
optional AllocationResult allocation = 1;
|
||||
|
||||
// ReservedFor indicates which entities are currently allowed to use
|
||||
|
|
@ -1373,6 +1376,7 @@ message ResourceClaimStatus {
|
|||
// the future, but not reduced.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
// +listType=map
|
||||
// +listMapKey=uid
|
||||
// +patchStrategy=merge
|
||||
|
|
@ -1384,6 +1388,7 @@ message ResourceClaimStatus {
|
|||
// information. Entries are owned by their respective drivers.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
// +listType=map
|
||||
// +listMapKey=driver
|
||||
// +listMapKey=device
|
||||
|
|
|
|||
|
|
@ -682,6 +682,7 @@ type ResourceSliceList struct {
|
|||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +k8s:prerelease-lifecycle-gen:introduced=1.32
|
||||
// +k8s:supportsSubresource=/status
|
||||
|
||||
// ResourceClaim describes a request for access to resources in the cluster,
|
||||
// for use by workloads. For example, if a workload needs an accelerator device
|
||||
|
|
@ -1354,6 +1355,7 @@ type ResourceClaimStatus struct {
|
|||
// Allocation is set once the claim has been allocated successfully.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
Allocation *AllocationResult `json:"allocation,omitempty" protobuf:"bytes,1,opt,name=allocation"`
|
||||
|
||||
// ReservedFor indicates which entities are currently allowed to use
|
||||
|
|
@ -1377,6 +1379,7 @@ type ResourceClaimStatus struct {
|
|||
// the future, but not reduced.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
// +listType=map
|
||||
// +listMapKey=uid
|
||||
// +patchStrategy=merge
|
||||
|
|
@ -1393,6 +1396,7 @@ type ResourceClaimStatus struct {
|
|||
// information. Entries are owned by their respective drivers.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
// +listType=map
|
||||
// +listMapKey=driver
|
||||
// +listMapKey=device
|
||||
|
|
@ -1510,6 +1514,8 @@ type DeviceRequestAllocationResult struct {
|
|||
// DNS sub-domains separated by slashes.
|
||||
//
|
||||
// +required
|
||||
// +k8s:required
|
||||
// +k8s:format=k8s-resource-pool-name
|
||||
Pool string `json:"pool" protobuf:"bytes,3,name=pool"`
|
||||
|
||||
// Device references one device instance via its name in the driver's
|
||||
|
|
|
|||
|
|
@ -826,6 +826,8 @@ message DeviceRequestAllocationResult {
|
|||
// DNS sub-domains separated by slashes.
|
||||
//
|
||||
// +required
|
||||
// +k8s:required
|
||||
// +k8s:format=k8s-resource-pool-name
|
||||
optional string pool = 3;
|
||||
|
||||
// Device references one device instance via its name in the driver's
|
||||
|
|
@ -1336,6 +1338,7 @@ message ResourceClaimStatus {
|
|||
// Allocation is set once the claim has been allocated successfully.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
optional AllocationResult allocation = 1;
|
||||
|
||||
// ReservedFor indicates which entities are currently allowed to use
|
||||
|
|
@ -1359,6 +1362,7 @@ message ResourceClaimStatus {
|
|||
// the future, but not reduced.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
// +listType=map
|
||||
// +listMapKey=uid
|
||||
// +patchStrategy=merge
|
||||
|
|
@ -1370,6 +1374,7 @@ message ResourceClaimStatus {
|
|||
// information. Entries are owned by their respective drivers.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
// +listType=map
|
||||
// +listMapKey=driver
|
||||
// +listMapKey=device
|
||||
|
|
|
|||
|
|
@ -678,6 +678,7 @@ type ResourceSliceList struct {
|
|||
// +genclient
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
// +k8s:prerelease-lifecycle-gen:introduced=1.33
|
||||
// +k8s:supportsSubresource=/status
|
||||
|
||||
// ResourceClaim describes a request for access to resources in the cluster,
|
||||
// for use by workloads. For example, if a workload needs an accelerator device
|
||||
|
|
@ -1346,6 +1347,7 @@ type ResourceClaimStatus struct {
|
|||
// Allocation is set once the claim has been allocated successfully.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
Allocation *AllocationResult `json:"allocation,omitempty" protobuf:"bytes,1,opt,name=allocation"`
|
||||
|
||||
// ReservedFor indicates which entities are currently allowed to use
|
||||
|
|
@ -1369,6 +1371,7 @@ type ResourceClaimStatus struct {
|
|||
// the future, but not reduced.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
// +listType=map
|
||||
// +listMapKey=uid
|
||||
// +patchStrategy=merge
|
||||
|
|
@ -1385,6 +1388,7 @@ type ResourceClaimStatus struct {
|
|||
// information. Entries are owned by their respective drivers.
|
||||
//
|
||||
// +optional
|
||||
// +k8s:optional
|
||||
// +listType=map
|
||||
// +listMapKey=driver
|
||||
// +listMapKey=device
|
||||
|
|
@ -1502,6 +1506,8 @@ type DeviceRequestAllocationResult struct {
|
|||
// DNS sub-domains separated by slashes.
|
||||
//
|
||||
// +required
|
||||
// +k8s:required
|
||||
// +k8s:format=k8s-resource-pool-name
|
||||
Pool string `json:"pool" protobuf:"bytes,3,name=pool"`
|
||||
|
||||
// Device references one device instance via its name in the driver's
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ package validate
|
|||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/api/operation"
|
||||
"k8s.io/apimachinery/pkg/api/validate/content"
|
||||
|
|
@ -155,3 +157,26 @@ func UUID[T ~string](_ context.Context, op operation.Operation, fldPath *field.P
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ResourcePoolName verifies that the specified value is one or more valid "long name"
|
||||
// parts separated by a '/' and no longer than 253 characters.
|
||||
func ResourcePoolName[T ~string](ctx context.Context, op operation.Operation, fldPath *field.Path, value, _ *T) field.ErrorList {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
val := (string)(*value)
|
||||
var allErrs field.ErrorList
|
||||
if len(val) > 253 {
|
||||
allErrs = append(allErrs, field.TooLong(fldPath, val, 253))
|
||||
}
|
||||
parts := strings.Split(val, "/")
|
||||
for i, part := range parts {
|
||||
if len(part) == 0 {
|
||||
allErrs = append(allErrs, field.Invalid(fldPath, val, fmt.Sprintf("segment %d: must not be empty", i)))
|
||||
continue
|
||||
}
|
||||
// Note that we are overwriting the origin from the underlying LongName validation.
|
||||
allErrs = append(allErrs, LongName(ctx, op, fldPath, &part, nil).PrefixDetail(fmt.Sprintf("segment %d: ", i))...)
|
||||
}
|
||||
return allErrs.WithOrigin("format=k8s-resource-pool-name")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -469,3 +469,106 @@ func TestLongNameCaseless(t *testing.T) {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResourcePoolName(t *testing.T) {
|
||||
ctx := context.Background()
|
||||
fldPath := field.NewPath("test")
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
input string
|
||||
wantErrs field.ErrorList
|
||||
}{{
|
||||
name: "valid: single segment",
|
||||
input: "a.valid.long-name",
|
||||
}, {
|
||||
name: "valid: two segments",
|
||||
input: "a.valid.long-name/another.one",
|
||||
}, {
|
||||
name: "valid: multiple segments",
|
||||
input: "a/b/c.d.e",
|
||||
}, {
|
||||
name: "valid: segments with numbers",
|
||||
input: "1.2.3/4.5.6",
|
||||
}, {
|
||||
name: "invalid: empty string",
|
||||
input: "",
|
||||
wantErrs: field.ErrorList{
|
||||
field.Invalid(fldPath, "", "segment 0: must not be empty").WithOrigin("format=k8s-resource-pool-name"),
|
||||
},
|
||||
}, {
|
||||
name: "invalid: leading slash",
|
||||
input: "/a.b.c",
|
||||
wantErrs: field.ErrorList{
|
||||
field.Invalid(fldPath, nil, "").WithOrigin("format=k8s-resource-pool-name"),
|
||||
},
|
||||
}, {
|
||||
name: "invalid: trailing slash",
|
||||
input: "a.b.c/",
|
||||
wantErrs: field.ErrorList{
|
||||
field.Invalid(fldPath, nil, "").WithOrigin("format=k8s-resource-pool-name"),
|
||||
},
|
||||
}, {
|
||||
name: "invalid: double slash",
|
||||
input: "a.b.c//d.e.f",
|
||||
wantErrs: field.ErrorList{
|
||||
field.Invalid(fldPath, nil, "").WithOrigin("format=k8s-resource-pool-name"),
|
||||
},
|
||||
}, {
|
||||
name: "invalid: one segment has uppercase",
|
||||
input: "a.valid.name/Not.Valid",
|
||||
wantErrs: field.ErrorList{
|
||||
field.Invalid(fldPath, nil, "segment 1: a lowercase RFC 1123").WithOrigin("format=k8s-resource-pool-name"),
|
||||
},
|
||||
}, {
|
||||
name: "invalid: one segment starts with dash",
|
||||
input: "a.valid.name/-not-valid",
|
||||
wantErrs: field.ErrorList{
|
||||
field.Invalid(fldPath, nil, "segment 1: a lowercase RFC 1123").WithOrigin("format=k8s-resource-pool-name"),
|
||||
},
|
||||
}, {
|
||||
name: "invalid: one segment has special characters",
|
||||
input: "a.valid.name/not_valid",
|
||||
wantErrs: field.ErrorList{
|
||||
field.Invalid(fldPath, nil, "segment 1: a lowercase RFC 1123").WithOrigin("format=k8s-resource-pool-name"),
|
||||
},
|
||||
}, {
|
||||
name: "invalid: too long",
|
||||
input: "a.valid.name/" + strings.Repeat("b", 253),
|
||||
wantErrs: field.ErrorList{
|
||||
field.TooLong(fldPath, nil, 253).WithOrigin("format=k8s-resource-pool-name"),
|
||||
},
|
||||
}, {
|
||||
name: "invalid: segment too long",
|
||||
input: strings.Repeat("b", 254),
|
||||
wantErrs: field.ErrorList{
|
||||
field.TooLong(fldPath, nil, 253).WithOrigin("format=k8s-resource-pool-name"),
|
||||
field.Invalid(fldPath, nil, "segment 0: must be no more than 253 bytes").WithOrigin("format=k8s-resource-pool-name"),
|
||||
},
|
||||
}, {
|
||||
name: "invalid: multiple invalid segments",
|
||||
input: "Not/Valid/Either",
|
||||
wantErrs: field.ErrorList{
|
||||
field.Invalid(fldPath, nil, "segment 0: a lowercase RFC 1123").WithOrigin("format=k8s-resource-pool-name"),
|
||||
field.Invalid(fldPath, nil, "segment 1: a lowercase RFC 1123").WithOrigin("format=k8s-resource-pool-name"),
|
||||
field.Invalid(fldPath, nil, "segment 2: a lowercase RFC 1123").WithOrigin("format=k8s-resource-pool-name"),
|
||||
},
|
||||
}, {
|
||||
name: "invalid: just a slash",
|
||||
input: "/",
|
||||
wantErrs: field.ErrorList{
|
||||
field.Invalid(fldPath, nil, "segment 0: must not be empty").WithOrigin("format=k8s-resource-pool-name"),
|
||||
field.Invalid(fldPath, nil, "segment 1: must not be empty").WithOrigin("format=k8s-resource-pool-name"),
|
||||
},
|
||||
}}
|
||||
|
||||
exactMatcher := field.ErrorMatcher{}.ByType().ByField().ByOrigin().ByDetailSubstring()
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
value := &tc.input
|
||||
gotErrs := ResourcePoolName(ctx, operation.Operation{}, fldPath, value, nil)
|
||||
exactMatcher.Test(t, tc.wantErrs, gotErrs)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -341,6 +341,14 @@ func (list ErrorList) MarkCoveredByDeclarative() ErrorList {
|
|||
return list
|
||||
}
|
||||
|
||||
// PrefixDetail adds a prefix to the Detail for all errors in the list and returns the updated list.
|
||||
func (list ErrorList) PrefixDetail(prefix string) ErrorList {
|
||||
for _, err := range list {
|
||||
err.Detail = prefix + err.Detail
|
||||
}
|
||||
return list
|
||||
}
|
||||
|
||||
// ToAggregate converts the ErrorList into an errors.Aggregate.
|
||||
func (list ErrorList) ToAggregate() utilerrors.Aggregate {
|
||||
if len(list) == 0 {
|
||||
|
|
|
|||
|
|
@ -0,0 +1,41 @@
|
|||
/*
|
||||
Copyright 2024 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:validation-gen=TypeMeta
|
||||
// +k8s:validation-gen-scheme-registry=k8s.io/code-generator/cmd/validation-gen/testscheme.Scheme
|
||||
|
||||
// This is a test package.
|
||||
package format
|
||||
|
||||
import "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
|
||||
var localSchemeBuilder = testscheme.New()
|
||||
|
||||
type Struct struct {
|
||||
TypeMeta int
|
||||
|
||||
// +k8s:format=k8s-resource-pool-name
|
||||
ResourcePoolNameField string `json:"resourcePoolNameField"`
|
||||
|
||||
// +k8s:format=k8s-resource-pool-name
|
||||
ResourcePoolNamePtrField *string `json:"resourcePoolNamePtrField"`
|
||||
|
||||
// Note: no validation here
|
||||
ResourcePoolNameTypedefField ResourcePoolNameStringType `json:"resourcePoolNameTypedefField"`
|
||||
}
|
||||
|
||||
// +k8s:format=k8s-resource-pool-name
|
||||
type ResourcePoolNameStringType string
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
/*
|
||||
Copyright 2024 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.
|
||||
*/
|
||||
|
||||
// +output_tests
|
||||
package format
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/utils/ptr"
|
||||
)
|
||||
|
||||
func Test(t *testing.T) {
|
||||
st := localSchemeBuilder.Test(t)
|
||||
|
||||
st.Value(&Struct{
|
||||
ResourcePoolNameField: "foo.bar",
|
||||
ResourcePoolNamePtrField: ptr.To("foo.bar"),
|
||||
ResourcePoolNameTypedefField: "foo.bar",
|
||||
}).ExpectValid()
|
||||
|
||||
st.Value(&Struct{
|
||||
ResourcePoolNameField: "1.2.3.4",
|
||||
ResourcePoolNamePtrField: ptr.To("1.2.3.4"),
|
||||
ResourcePoolNameTypedefField: "1.2.3.4",
|
||||
}).ExpectValid()
|
||||
|
||||
invalidStruct := &Struct{
|
||||
ResourcePoolNameField: "",
|
||||
ResourcePoolNamePtrField: ptr.To(""),
|
||||
ResourcePoolNameTypedefField: "",
|
||||
}
|
||||
st.Value(invalidStruct).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin(), field.ErrorList{
|
||||
field.Invalid(field.NewPath("resourcePoolNameField"), nil, "").WithOrigin("format=k8s-resource-pool-name"),
|
||||
field.Invalid(field.NewPath("resourcePoolNamePtrField"), nil, "").WithOrigin("format=k8s-resource-pool-name"),
|
||||
field.Invalid(field.NewPath("resourcePoolNameTypedefField"), nil, "").WithOrigin("format=k8s-resource-pool-name"),
|
||||
})
|
||||
// Test validation ratcheting
|
||||
st.Value(invalidStruct).OldValue(invalidStruct).ExpectValid()
|
||||
|
||||
invalidStruct = &Struct{
|
||||
ResourcePoolNameField: "Not a ResourcePoolName",
|
||||
ResourcePoolNamePtrField: ptr.To("Not a ResourcePoolName"),
|
||||
ResourcePoolNameTypedefField: "Not a ResourcePoolName",
|
||||
}
|
||||
st.Value(invalidStruct).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin(), field.ErrorList{
|
||||
field.Invalid(field.NewPath("resourcePoolNameField"), nil, "").WithOrigin("format=k8s-resource-pool-name"),
|
||||
field.Invalid(field.NewPath("resourcePoolNamePtrField"), nil, "").WithOrigin("format=k8s-resource-pool-name"),
|
||||
field.Invalid(field.NewPath("resourcePoolNameTypedefField"), nil, "").WithOrigin("format=k8s-resource-pool-name"),
|
||||
})
|
||||
// Test validation ratcheting
|
||||
st.Value(invalidStruct).OldValue(invalidStruct).ExpectValid()
|
||||
|
||||
invalidStruct = &Struct{
|
||||
ResourcePoolNameField: "a..b",
|
||||
ResourcePoolNamePtrField: ptr.To("a..b"),
|
||||
ResourcePoolNameTypedefField: "a..b",
|
||||
}
|
||||
st.Value(invalidStruct).ExpectMatches(field.ErrorMatcher{}.ByType().ByField().ByOrigin(), field.ErrorList{
|
||||
field.Invalid(field.NewPath("resourcePoolNameField"), nil, "").WithOrigin("format=k8s-resource-pool-name"),
|
||||
field.Invalid(field.NewPath("resourcePoolNamePtrField"), nil, "").WithOrigin("format=k8s-resource-pool-name"),
|
||||
field.Invalid(field.NewPath("resourcePoolNameTypedefField"), nil, "").WithOrigin("format=k8s-resource-pool-name"),
|
||||
})
|
||||
// Test validation ratcheting
|
||||
st.Value(invalidStruct).OldValue(invalidStruct).ExpectValid()
|
||||
}
|
||||
|
|
@ -0,0 +1,101 @@
|
|||
//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 format
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
|
||||
operation "k8s.io/apimachinery/pkg/api/operation"
|
||||
safe "k8s.io/apimachinery/pkg/api/safe"
|
||||
validate "k8s.io/apimachinery/pkg/api/validate"
|
||||
field "k8s.io/apimachinery/pkg/util/validation/field"
|
||||
testscheme "k8s.io/code-generator/cmd/validation-gen/testscheme"
|
||||
)
|
||||
|
||||
func init() { localSchemeBuilder.Register(RegisterValidations) }
|
||||
|
||||
// RegisterValidations adds validation functions to the given scheme.
|
||||
// Public to allow building arbitrary schemes.
|
||||
func RegisterValidations(scheme *testscheme.Scheme) error {
|
||||
// type Struct
|
||||
scheme.AddValidationFunc((*Struct)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_Struct(ctx, op, nil /* fldPath */, obj.(*Struct), safe.Cast[*Struct](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate_ResourcePoolNameStringType validates an instance of ResourcePoolNameStringType according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_ResourcePoolNameStringType(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *ResourcePoolNameStringType) (errs field.ErrorList) {
|
||||
errs = append(errs, validate.ResourcePoolName(ctx, op, fldPath, obj, oldObj)...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_Struct validates an instance of Struct according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_Struct(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *Struct) (errs field.ErrorList) {
|
||||
// field Struct.TypeMeta has no validation
|
||||
|
||||
// field Struct.ResourcePoolNameField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.ResourcePoolName(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("resourcePoolNameField"), &obj.ResourcePoolNameField, safe.Field(oldObj, func(oldObj *Struct) *string { return &oldObj.ResourcePoolNameField }))...)
|
||||
|
||||
// field Struct.ResourcePoolNamePtrField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *string) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
|
||||
return nil
|
||||
}
|
||||
// call field-attached validations
|
||||
errs = append(errs, validate.ResourcePoolName(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("resourcePoolNamePtrField"), obj.ResourcePoolNamePtrField, safe.Field(oldObj, func(oldObj *Struct) *string { return oldObj.ResourcePoolNamePtrField }))...)
|
||||
|
||||
// field Struct.ResourcePoolNameTypedefField
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *ResourcePoolNameStringType) (errs field.ErrorList) {
|
||||
// don't revalidate unchanged data
|
||||
if op.Type == operation.Update && (obj == oldObj || (obj != nil && oldObj != nil && *obj == *oldObj)) {
|
||||
return nil
|
||||
}
|
||||
// call the type's validation function
|
||||
errs = append(errs, Validate_ResourcePoolNameStringType(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("resourcePoolNameTypedefField"), &obj.ResourcePoolNameTypedefField, safe.Field(oldObj, func(oldObj *Struct) *ResourcePoolNameStringType { return &oldObj.ResourcePoolNameTypedefField }))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
|
@ -54,6 +54,7 @@ var (
|
|||
labelValueValidator = types.Name{Package: libValidationPkg, Name: "LabelValue"}
|
||||
longNameCaselessValidator = types.Name{Package: libValidationPkg, Name: "LongNameCaseless"}
|
||||
longNameValidator = types.Name{Package: libValidationPkg, Name: "LongName"}
|
||||
resourcePoolNameValidator = types.Name{Package: libValidationPkg, Name: "ResourcePoolName"}
|
||||
shortNameValidator = types.Name{Package: libValidationPkg, Name: "ShortName"}
|
||||
uuidValidator = types.Name{Package: libValidationPkg, Name: "UUID"}
|
||||
)
|
||||
|
|
@ -92,6 +93,8 @@ func getFormatValidationFunction(format string) (FunctionGen, error) {
|
|||
return Function(formatTagName, DefaultFlags, longNameValidator), nil
|
||||
case "k8s-long-name-caseless":
|
||||
return Function(formatTagName, DefaultFlags, longNameCaselessValidator), nil
|
||||
case "k8s-resource-pool-name":
|
||||
return Function(formatTagName, DefaultFlags, resourcePoolNameValidator), nil
|
||||
case "k8s-short-name":
|
||||
return Function(formatTagName, DefaultFlags, shortNameValidator), nil
|
||||
case "k8s-uuid":
|
||||
|
|
@ -122,6 +125,9 @@ func (ftv formatTagValidator) Docs() TagDoc {
|
|||
}, {
|
||||
Description: "k8s-long-name-caseless",
|
||||
Docs: "Deprecated: This field holds a case-insensitive Kubernetes \"long name\", aka a \"DNS subdomain\" value.",
|
||||
}, {
|
||||
Description: "k8s-resource-pool-name",
|
||||
Docs: "This field holds value with one or more Kubernetes \"long name\" parts separated by `/` and no longer than 253 characters.",
|
||||
}, {
|
||||
Description: "k8s-short-name",
|
||||
Docs: "This field holds a Kubernetes \"short name\", aka a \"DNS label\" value.",
|
||||
|
|
|
|||
Loading…
Reference in a new issue