mirror of
https://github.com/k3s-io/kubernetes.git
synced 2026-07-19 15:39:17 +00:00
feat(resource): Add maxItems validation for DeviceClass selectors
This commit introduces a validation rule to limit the number of selectors in a DeviceClass to 32. This is done by adding the `+k8s:maxItems=32` marker to the DeviceClassSpec and running the code generator. The following changes are included: - Updated `staging/src/k8s.io/api/resource/v*/types.go` with the validation marker. - Regenerated validation code in `pkg/apis/resource/v*/zz_generated.validations.go`. - Improved error message in `pkg/apis/resource/validation/validation.go`. - Added tests in `pkg/registry/resource/deviceclass/declarative_validation_test.go` to cover the new validation.
This commit is contained in:
84
pkg/apis/resource/v1/zz_generated.validations.go
generated
84
pkg/apis/resource/v1/zz_generated.validations.go
generated
@@ -39,6 +39,22 @@ 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 DeviceClass
|
||||
scheme.AddValidationFunc((*resourcev1.DeviceClass)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_DeviceClass(ctx, op, nil /* fldPath */, obj.(*resourcev1.DeviceClass), safe.Cast[*resourcev1.DeviceClass](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
// type DeviceClassList
|
||||
scheme.AddValidationFunc((*resourcev1.DeviceClassList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_DeviceClassList(ctx, op, nil /* fldPath */, obj.(*resourcev1.DeviceClassList), safe.Cast[*resourcev1.DeviceClassList](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
// type ResourceClaim
|
||||
scheme.AddValidationFunc((*resourcev1.ResourceClaim)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
@@ -166,6 +182,74 @@ func Validate_DeviceClaim(ctx context.Context, op operation.Operation, fldPath *
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_DeviceClass validates an instance of DeviceClass according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_DeviceClass(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceClass) (errs field.ErrorList) {
|
||||
// field resourcev1.DeviceClass.TypeMeta has no validation
|
||||
// field resourcev1.DeviceClass.ObjectMeta has no validation
|
||||
|
||||
// field resourcev1.DeviceClass.Spec
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *resourcev1.DeviceClassSpec) (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_DeviceClassSpec(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1.DeviceClass) *resourcev1.DeviceClassSpec { return &oldObj.Spec }))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_DeviceClassList validates an instance of DeviceClassList according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_DeviceClassList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceClassList) (errs field.ErrorList) {
|
||||
// field resourcev1.DeviceClassList.TypeMeta has no validation
|
||||
// field resourcev1.DeviceClassList.ListMeta has no validation
|
||||
|
||||
// field resourcev1.DeviceClassList.Items
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceClass) (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_DeviceClass)...)
|
||||
return
|
||||
}(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1.DeviceClassList) []resourcev1.DeviceClass { return oldObj.Items }))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_DeviceClassSpec validates an instance of DeviceClassSpec according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_DeviceClassSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1.DeviceClassSpec) (errs field.ErrorList) {
|
||||
// field resourcev1.DeviceClassSpec.Selectors
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1.DeviceSelector) (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.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
return // do not proceed
|
||||
}
|
||||
if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("selectors"), obj.Selectors, safe.Field(oldObj, func(oldObj *resourcev1.DeviceClassSpec) []resourcev1.DeviceSelector { return oldObj.Selectors }))...)
|
||||
|
||||
// field resourcev1.DeviceClassSpec.Config has no validation
|
||||
// field resourcev1.DeviceClassSpec.ExtendedResourceName 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) {
|
||||
|
||||
@@ -39,6 +39,22 @@ 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 DeviceClass
|
||||
scheme.AddValidationFunc((*resourcev1beta1.DeviceClass)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_DeviceClass(ctx, op, nil /* fldPath */, obj.(*resourcev1beta1.DeviceClass), safe.Cast[*resourcev1beta1.DeviceClass](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
// type DeviceClassList
|
||||
scheme.AddValidationFunc((*resourcev1beta1.DeviceClassList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_DeviceClassList(ctx, op, nil /* fldPath */, obj.(*resourcev1beta1.DeviceClassList), safe.Cast[*resourcev1beta1.DeviceClassList](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
// type ResourceClaim
|
||||
scheme.AddValidationFunc((*resourcev1beta1.ResourceClaim)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
@@ -172,6 +188,76 @@ func Validate_DeviceClaim(ctx context.Context, op operation.Operation, fldPath *
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_DeviceClass validates an instance of DeviceClass according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_DeviceClass(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceClass) (errs field.ErrorList) {
|
||||
// field resourcev1beta1.DeviceClass.TypeMeta has no validation
|
||||
// field resourcev1beta1.DeviceClass.ObjectMeta has no validation
|
||||
|
||||
// field resourcev1beta1.DeviceClass.Spec
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceClassSpec) (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_DeviceClassSpec(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceClass) *resourcev1beta1.DeviceClassSpec { return &oldObj.Spec }))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_DeviceClassList validates an instance of DeviceClassList according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_DeviceClassList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceClassList) (errs field.ErrorList) {
|
||||
// field resourcev1beta1.DeviceClassList.TypeMeta has no validation
|
||||
// field resourcev1beta1.DeviceClassList.ListMeta has no validation
|
||||
|
||||
// field resourcev1beta1.DeviceClassList.Items
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceClass) (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_DeviceClass)...)
|
||||
return
|
||||
}(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceClassList) []resourcev1beta1.DeviceClass { return oldObj.Items }))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_DeviceClassSpec validates an instance of DeviceClassSpec according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_DeviceClassSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta1.DeviceClassSpec) (errs field.ErrorList) {
|
||||
// field resourcev1beta1.DeviceClassSpec.Selectors
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1beta1.DeviceSelector) (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.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
return // do not proceed
|
||||
}
|
||||
if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("selectors"), obj.Selectors, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceClassSpec) []resourcev1beta1.DeviceSelector {
|
||||
return oldObj.Selectors
|
||||
}))...)
|
||||
|
||||
// field resourcev1beta1.DeviceClassSpec.Config has no validation
|
||||
// field resourcev1beta1.DeviceClassSpec.ExtendedResourceName 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) {
|
||||
|
||||
@@ -39,6 +39,22 @@ 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 DeviceClass
|
||||
scheme.AddValidationFunc((*resourcev1beta2.DeviceClass)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_DeviceClass(ctx, op, nil /* fldPath */, obj.(*resourcev1beta2.DeviceClass), safe.Cast[*resourcev1beta2.DeviceClass](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
// type DeviceClassList
|
||||
scheme.AddValidationFunc((*resourcev1beta2.DeviceClassList)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
case "/":
|
||||
return Validate_DeviceClassList(ctx, op, nil /* fldPath */, obj.(*resourcev1beta2.DeviceClassList), safe.Cast[*resourcev1beta2.DeviceClassList](oldObj))
|
||||
}
|
||||
return field.ErrorList{field.InternalError(nil, fmt.Errorf("no validation found for %T, subresource: %v", obj, op.Request.SubresourcePath()))}
|
||||
})
|
||||
// type ResourceClaim
|
||||
scheme.AddValidationFunc((*resourcev1beta2.ResourceClaim)(nil), func(ctx context.Context, op operation.Operation, obj, oldObj interface{}) field.ErrorList {
|
||||
switch op.Request.SubresourcePath() {
|
||||
@@ -172,6 +188,76 @@ func Validate_DeviceClaim(ctx context.Context, op operation.Operation, fldPath *
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_DeviceClass validates an instance of DeviceClass according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_DeviceClass(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceClass) (errs field.ErrorList) {
|
||||
// field resourcev1beta2.DeviceClass.TypeMeta has no validation
|
||||
// field resourcev1beta2.DeviceClass.ObjectMeta has no validation
|
||||
|
||||
// field resourcev1beta2.DeviceClass.Spec
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceClassSpec) (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_DeviceClassSpec(ctx, op, fldPath, obj, oldObj)...)
|
||||
return
|
||||
}(fldPath.Child("spec"), &obj.Spec, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceClass) *resourcev1beta2.DeviceClassSpec { return &oldObj.Spec }))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_DeviceClassList validates an instance of DeviceClassList according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_DeviceClassList(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceClassList) (errs field.ErrorList) {
|
||||
// field resourcev1beta2.DeviceClassList.TypeMeta has no validation
|
||||
// field resourcev1beta2.DeviceClassList.ListMeta has no validation
|
||||
|
||||
// field resourcev1beta2.DeviceClassList.Items
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceClass) (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_DeviceClass)...)
|
||||
return
|
||||
}(fldPath.Child("items"), obj.Items, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceClassList) []resourcev1beta2.DeviceClass { return oldObj.Items }))...)
|
||||
|
||||
return errs
|
||||
}
|
||||
|
||||
// Validate_DeviceClassSpec validates an instance of DeviceClassSpec according
|
||||
// to declarative validation rules in the API schema.
|
||||
func Validate_DeviceClassSpec(ctx context.Context, op operation.Operation, fldPath *field.Path, obj, oldObj *resourcev1beta2.DeviceClassSpec) (errs field.ErrorList) {
|
||||
// field resourcev1beta2.DeviceClassSpec.Selectors
|
||||
errs = append(errs,
|
||||
func(fldPath *field.Path, obj, oldObj []resourcev1beta2.DeviceSelector) (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.MaxItems(ctx, op, fldPath, obj, oldObj, 32); len(e) != 0 {
|
||||
errs = append(errs, e...)
|
||||
return // do not proceed
|
||||
}
|
||||
if e := validate.OptionalSlice(ctx, op, fldPath, obj, oldObj); len(e) != 0 {
|
||||
return // do not proceed
|
||||
}
|
||||
return
|
||||
}(fldPath.Child("selectors"), obj.Selectors, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceClassSpec) []resourcev1beta2.DeviceSelector {
|
||||
return oldObj.Selectors
|
||||
}))...)
|
||||
|
||||
// field resourcev1beta2.DeviceClassSpec.Config has no validation
|
||||
// field resourcev1beta2.DeviceClassSpec.ExtendedResourceName 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) {
|
||||
|
||||
@@ -544,7 +544,7 @@ func validateDeviceClassSpec(spec, oldSpec *resource.DeviceClassSpec, fldPath *f
|
||||
func(selector resource.DeviceSelector, fldPath *field.Path) field.ErrorList {
|
||||
return validateSelector(selector, fldPath, stored)
|
||||
},
|
||||
fldPath.Child("selectors"))...)
|
||||
fldPath.Child("selectors"), sizeCovered)...)
|
||||
// Same logic as above for configs.
|
||||
if oldSpec != nil {
|
||||
stored = apiequality.Semantic.DeepEqual(spec.Config, oldSpec.Config)
|
||||
@@ -1128,7 +1128,7 @@ func validateSlice[T any](slice []T, maxSize int, validateItem func(T, *field.Pa
|
||||
// just shows the number of entries.
|
||||
err := field.TooMany(fldPath, len(slice), maxSize).WithOrigin("maxItems")
|
||||
if slices.Contains(opts, sizeCovered) {
|
||||
err = err.MarkCoveredByDeclarative()
|
||||
err = err.MarkCoveredByDeclarative().WithOrigin("maxItems")
|
||||
}
|
||||
allErrs = append(allErrs, err)
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ func TestValidateClass(t *testing.T) {
|
||||
},
|
||||
"too-many-selectors": {
|
||||
wantFailures: field.ErrorList{
|
||||
field.TooMany(field.NewPath("spec", "selectors"), resource.DeviceSelectorsMaxSize+1, resource.DeviceSelectorsMaxSize),
|
||||
field.TooMany(field.NewPath("spec", "selectors"), resource.DeviceSelectorsMaxSize+1, resource.DeviceSelectorsMaxSize).WithOrigin("maxItems").MarkCoveredByDeclarative(),
|
||||
},
|
||||
class: func() *resource.DeviceClass {
|
||||
class := testClass(goodName)
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package deviceclass
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -48,6 +49,12 @@ func TestDeclarativeValidate(t *testing.T) {
|
||||
"valid": {
|
||||
input: mkDeviceClass(),
|
||||
},
|
||||
"too many selectors": {
|
||||
input: mkDeviceClass(tweakSelectors(33)),
|
||||
expectedErrs: field.ErrorList{
|
||||
field.TooMany(field.NewPath("spec", "selectors"), 33, 32).WithOrigin("maxItems"),
|
||||
},
|
||||
},
|
||||
// TODO: Add more test cases
|
||||
}
|
||||
|
||||
@@ -80,6 +87,13 @@ func TestDeclarativeValidateUpdate(t *testing.T) {
|
||||
old: mkDeviceClass(),
|
||||
update: mkDeviceClass(),
|
||||
},
|
||||
"update with too many selectors": {
|
||||
old: mkDeviceClass(),
|
||||
update: mkDeviceClass(tweakSelectors(33)),
|
||||
expectedErrs: field.ErrorList{
|
||||
field.TooMany(field.NewPath("spec", "selectors"), 33, 32).WithOrigin("maxItems"),
|
||||
},
|
||||
},
|
||||
// TODO: Add more test cases
|
||||
}
|
||||
|
||||
@@ -102,7 +116,7 @@ func mkDeviceClass(mutators ...func(*resource.DeviceClass)) resource.DeviceClass
|
||||
},
|
||||
Spec: resource.DeviceClassSpec{
|
||||
Selectors: []resource.DeviceSelector{
|
||||
{
|
||||
resource.DeviceSelector{
|
||||
CEL: &resource.CELDeviceSelector{
|
||||
Expression: "device.driver == \"test.driver.io\"",
|
||||
},
|
||||
@@ -127,3 +141,15 @@ func mkDeviceClass(mutators ...func(*resource.DeviceClass)) resource.DeviceClass
|
||||
}
|
||||
return dc
|
||||
}
|
||||
|
||||
func tweakSelectors(count int) func(*resource.DeviceClass) {
|
||||
return func(dc *resource.DeviceClass) {
|
||||
for i := 0; i < count; i++ {
|
||||
dc.Spec.Selectors = append(dc.Spec.Selectors, resource.DeviceSelector{
|
||||
CEL: &resource.CELDeviceSelector{
|
||||
Expression: fmt.Sprintf("device.driver == \"test.driver.io%d\"", i),
|
||||
},
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -642,6 +642,8 @@ message DeviceClassSpec {
|
||||
//
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
// +k8s:optional
|
||||
// +k8s:maxItems=32
|
||||
repeated DeviceSelector selectors = 1;
|
||||
|
||||
// Config defines configuration parameters that apply to each device that is claimed via this class.
|
||||
|
||||
@@ -1672,6 +1672,8 @@ type DeviceClassSpec struct {
|
||||
//
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
// +k8s:optional
|
||||
// +k8s:maxItems=32
|
||||
Selectors []DeviceSelector `json:"selectors,omitempty" protobuf:"bytes,1,opt,name=selectors"`
|
||||
|
||||
// Config defines configuration parameters that apply to each device that is claimed via this class.
|
||||
|
||||
@@ -650,6 +650,8 @@ message DeviceClassSpec {
|
||||
//
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
// +k8s:optional
|
||||
// +k8s:maxItems=32
|
||||
repeated DeviceSelector selectors = 1;
|
||||
|
||||
// Config defines configuration parameters that apply to each device that is claimed via this class.
|
||||
|
||||
@@ -1680,6 +1680,8 @@ type DeviceClassSpec struct {
|
||||
//
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
// +k8s:optional
|
||||
// +k8s:maxItems=32
|
||||
Selectors []DeviceSelector `json:"selectors,omitempty" protobuf:"bytes,1,opt,name=selectors"`
|
||||
|
||||
// Config defines configuration parameters that apply to each device that is claimed via this class.
|
||||
|
||||
@@ -642,6 +642,8 @@ message DeviceClassSpec {
|
||||
//
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
// +k8s:optional
|
||||
// +k8s:maxItems=32
|
||||
repeated DeviceSelector selectors = 1;
|
||||
|
||||
// Config defines configuration parameters that apply to each device that is claimed via this class.
|
||||
|
||||
@@ -1672,6 +1672,8 @@ type DeviceClassSpec struct {
|
||||
//
|
||||
// +optional
|
||||
// +listType=atomic
|
||||
// +k8s:optional
|
||||
// +k8s:maxItems=32
|
||||
Selectors []DeviceSelector `json:"selectors,omitempty" protobuf:"bytes,1,opt,name=selectors"`
|
||||
|
||||
// Config defines configuration parameters that apply to each device that is claimed via this class.
|
||||
|
||||
Reference in New Issue
Block a user