From e693da729066f1f773973f02a2da4f9475562aab Mon Sep 17 00:00:00 2001 From: Lalit Chauhan Date: Fri, 26 Sep 2025 23:08:17 +0000 Subject: [PATCH] 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. --- .../resource/v1/zz_generated.validations.go | 84 ++++++++++++++++++ .../v1beta1/zz_generated.validations.go | 86 +++++++++++++++++++ .../v1beta2/zz_generated.validations.go | 86 +++++++++++++++++++ pkg/apis/resource/validation/validation.go | 4 +- .../validation/validation_deviceclass_test.go | 2 +- .../declarative_validation_test.go | 28 +++++- .../k8s.io/api/resource/v1/generated.proto | 2 + staging/src/k8s.io/api/resource/v1/types.go | 2 + .../api/resource/v1beta1/generated.proto | 2 + .../src/k8s.io/api/resource/v1beta1/types.go | 2 + .../api/resource/v1beta2/generated.proto | 2 + .../src/k8s.io/api/resource/v1beta2/types.go | 2 + 12 files changed, 298 insertions(+), 4 deletions(-) diff --git a/pkg/apis/resource/v1/zz_generated.validations.go b/pkg/apis/resource/v1/zz_generated.validations.go index 053c2ddb815..be7cef47a32 100644 --- a/pkg/apis/resource/v1/zz_generated.validations.go +++ b/pkg/apis/resource/v1/zz_generated.validations.go @@ -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) { diff --git a/pkg/apis/resource/v1beta1/zz_generated.validations.go b/pkg/apis/resource/v1beta1/zz_generated.validations.go index dc47632c819..677a671a965 100644 --- a/pkg/apis/resource/v1beta1/zz_generated.validations.go +++ b/pkg/apis/resource/v1beta1/zz_generated.validations.go @@ -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) { diff --git a/pkg/apis/resource/v1beta2/zz_generated.validations.go b/pkg/apis/resource/v1beta2/zz_generated.validations.go index df1d7265d92..dee0e59dfac 100644 --- a/pkg/apis/resource/v1beta2/zz_generated.validations.go +++ b/pkg/apis/resource/v1beta2/zz_generated.validations.go @@ -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) { diff --git a/pkg/apis/resource/validation/validation.go b/pkg/apis/resource/validation/validation.go index 31ee383698c..8a0020cf4fa 100644 --- a/pkg/apis/resource/validation/validation.go +++ b/pkg/apis/resource/validation/validation.go @@ -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) } diff --git a/pkg/apis/resource/validation/validation_deviceclass_test.go b/pkg/apis/resource/validation/validation_deviceclass_test.go index 7ccb8f634e6..99effb6663e 100644 --- a/pkg/apis/resource/validation/validation_deviceclass_test.go +++ b/pkg/apis/resource/validation/validation_deviceclass_test.go @@ -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) diff --git a/pkg/registry/resource/deviceclass/declarative_validation_test.go b/pkg/registry/resource/deviceclass/declarative_validation_test.go index 8b6f0202f73..364eedc01e2 100644 --- a/pkg/registry/resource/deviceclass/declarative_validation_test.go +++ b/pkg/registry/resource/deviceclass/declarative_validation_test.go @@ -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), + }, + }) + } + } +} diff --git a/staging/src/k8s.io/api/resource/v1/generated.proto b/staging/src/k8s.io/api/resource/v1/generated.proto index 7c3998d895e..40a8816de87 100644 --- a/staging/src/k8s.io/api/resource/v1/generated.proto +++ b/staging/src/k8s.io/api/resource/v1/generated.proto @@ -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. diff --git a/staging/src/k8s.io/api/resource/v1/types.go b/staging/src/k8s.io/api/resource/v1/types.go index 5b47a7ffaed..0261bf012fc 100644 --- a/staging/src/k8s.io/api/resource/v1/types.go +++ b/staging/src/k8s.io/api/resource/v1/types.go @@ -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. diff --git a/staging/src/k8s.io/api/resource/v1beta1/generated.proto b/staging/src/k8s.io/api/resource/v1beta1/generated.proto index cc9469bb646..4ecd7724c2d 100644 --- a/staging/src/k8s.io/api/resource/v1beta1/generated.proto +++ b/staging/src/k8s.io/api/resource/v1beta1/generated.proto @@ -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. diff --git a/staging/src/k8s.io/api/resource/v1beta1/types.go b/staging/src/k8s.io/api/resource/v1beta1/types.go index ee8e1d99bac..470693d30d3 100644 --- a/staging/src/k8s.io/api/resource/v1beta1/types.go +++ b/staging/src/k8s.io/api/resource/v1beta1/types.go @@ -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. diff --git a/staging/src/k8s.io/api/resource/v1beta2/generated.proto b/staging/src/k8s.io/api/resource/v1beta2/generated.proto index 076a43c93ac..a9d036cf9b9 100644 --- a/staging/src/k8s.io/api/resource/v1beta2/generated.proto +++ b/staging/src/k8s.io/api/resource/v1beta2/generated.proto @@ -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. diff --git a/staging/src/k8s.io/api/resource/v1beta2/types.go b/staging/src/k8s.io/api/resource/v1beta2/types.go index 1ade5acf1b4..2e91e9ea8b0 100644 --- a/staging/src/k8s.io/api/resource/v1beta2/types.go +++ b/staging/src/k8s.io/api/resource/v1beta2/types.go @@ -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.