From ca9e09b7d307ada4909923373abf8ad5abb4424d Mon Sep 17 00:00:00 2001 From: yongruilin Date: Thu, 9 Oct 2025 04:48:51 +0000 Subject: [PATCH] feat: migrate DeviceClaim.Requests uniqueness to declarative validation This change migrates the validation for unique device request names for the DeviceClaim.Requests field to a declarative format. The `+k8s:unique=map` and `+k8s:listMapKey=name` tags have been added to the `Requests` field within the `DeviceClaim` API type. This ensures the generated validation code enforces that all entries in the `requests` slice have a unique `name`. --- pkg/apis/resource/v1/zz_generated.validations.go | 4 +++- pkg/apis/resource/v1beta1/zz_generated.validations.go | 4 +++- pkg/apis/resource/v1beta2/zz_generated.validations.go | 4 +++- pkg/apis/resource/validation/validation.go | 2 +- staging/src/k8s.io/api/resource/v1/generated.proto | 3 +++ staging/src/k8s.io/api/resource/v1/types.go | 3 +++ staging/src/k8s.io/api/resource/v1beta1/generated.proto | 3 +++ staging/src/k8s.io/api/resource/v1beta1/types.go | 3 +++ staging/src/k8s.io/api/resource/v1beta2/generated.proto | 3 +++ staging/src/k8s.io/api/resource/v1beta2/types.go | 3 +++ 10 files changed, 28 insertions(+), 4 deletions(-) diff --git a/pkg/apis/resource/v1/zz_generated.validations.go b/pkg/apis/resource/v1/zz_generated.validations.go index 23201095866..ad997b8678f 100644 --- a/pkg/apis/resource/v1/zz_generated.validations.go +++ b/pkg/apis/resource/v1/zz_generated.validations.go @@ -219,8 +219,10 @@ func Validate_DeviceClaim(ctx context.Context, op operation.Operation, fldPath * if earlyReturn { return // do not proceed } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1.DeviceRequest, b resourcev1.DeviceRequest) bool { return a.Name == b.Name })...) // iterate the list and call the type's validation function - errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceRequest)...) + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1.DeviceRequest, b resourcev1.DeviceRequest) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_DeviceRequest)...) return }(fldPath.Child("requests"), obj.Requests, safe.Field(oldObj, func(oldObj *resourcev1.DeviceClaim) []resourcev1.DeviceRequest { return oldObj.Requests }))...) diff --git a/pkg/apis/resource/v1beta1/zz_generated.validations.go b/pkg/apis/resource/v1beta1/zz_generated.validations.go index d3390a1b8bf..5816529d29e 100644 --- a/pkg/apis/resource/v1beta1/zz_generated.validations.go +++ b/pkg/apis/resource/v1beta1/zz_generated.validations.go @@ -221,8 +221,10 @@ func Validate_DeviceClaim(ctx context.Context, op operation.Operation, fldPath * if earlyReturn { return // do not proceed } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta1.DeviceRequest, b resourcev1beta1.DeviceRequest) bool { return a.Name == b.Name })...) // iterate the list and call the type's validation function - errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceRequest)...) + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta1.DeviceRequest, b resourcev1beta1.DeviceRequest) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_DeviceRequest)...) return }(fldPath.Child("requests"), obj.Requests, safe.Field(oldObj, func(oldObj *resourcev1beta1.DeviceClaim) []resourcev1beta1.DeviceRequest { return oldObj.Requests }))...) diff --git a/pkg/apis/resource/v1beta2/zz_generated.validations.go b/pkg/apis/resource/v1beta2/zz_generated.validations.go index 96648bfd3d4..07e4c9d00af 100644 --- a/pkg/apis/resource/v1beta2/zz_generated.validations.go +++ b/pkg/apis/resource/v1beta2/zz_generated.validations.go @@ -221,8 +221,10 @@ func Validate_DeviceClaim(ctx context.Context, op operation.Operation, fldPath * if earlyReturn { return // do not proceed } + // lists with map semantics require unique keys + errs = append(errs, validate.Unique(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta2.DeviceRequest, b resourcev1beta2.DeviceRequest) bool { return a.Name == b.Name })...) // iterate the list and call the type's validation function - errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, nil, nil, Validate_DeviceRequest)...) + errs = append(errs, validate.EachSliceVal(ctx, op, fldPath, obj, oldObj, func(a resourcev1beta2.DeviceRequest, b resourcev1beta2.DeviceRequest) bool { return a.Name == b.Name }, validate.SemanticDeepEqual, Validate_DeviceRequest)...) return }(fldPath.Child("requests"), obj.Requests, safe.Field(oldObj, func(oldObj *resourcev1beta2.DeviceClaim) []resourcev1beta2.DeviceRequest { return oldObj.Requests }))...) diff --git a/pkg/apis/resource/validation/validation.go b/pkg/apis/resource/validation/validation.go index a0123b400a5..b6be86bb08e 100644 --- a/pkg/apis/resource/validation/validation.go +++ b/pkg/apis/resource/validation/validation.go @@ -129,7 +129,7 @@ func validateDeviceClaim(deviceClaim *resource.DeviceClaim, fldPath *field.Path, func(request resource.DeviceRequest) string { return request.Name }, - fldPath.Child("requests"), sizeCovered)...) + fldPath.Child("requests"), sizeCovered, uniquenessCovered)...) allErrs = append(allErrs, validateSlice(deviceClaim.Constraints, resource.DeviceConstraintsMaxSize, func(constraint resource.DeviceConstraint, fldPath *field.Path) field.ErrorList { return validateDeviceConstraint(constraint, fldPath, requestNames) diff --git a/staging/src/k8s.io/api/resource/v1/generated.proto b/staging/src/k8s.io/api/resource/v1/generated.proto index 19e57c92979..4805ccd4c9d 100644 --- a/staging/src/k8s.io/api/resource/v1/generated.proto +++ b/staging/src/k8s.io/api/resource/v1/generated.proto @@ -562,6 +562,9 @@ message DeviceClaim { // // +optional // +listType=atomic + // +k8s:listType=atomic + // +k8s:unique=map + // +k8s:listMapKey=name // +k8s:maxItems=32 repeated DeviceRequest requests = 1; diff --git a/staging/src/k8s.io/api/resource/v1/types.go b/staging/src/k8s.io/api/resource/v1/types.go index b9ab2d7d4a4..3d491259a73 100644 --- a/staging/src/k8s.io/api/resource/v1/types.go +++ b/staging/src/k8s.io/api/resource/v1/types.go @@ -725,6 +725,9 @@ type DeviceClaim struct { // // +optional // +listType=atomic + // +k8s:listType=atomic + // +k8s:unique=map + // +k8s:listMapKey=name // +k8s:maxItems=32 Requests []DeviceRequest `json:"requests" protobuf:"bytes,1,name=requests"` diff --git a/staging/src/k8s.io/api/resource/v1beta1/generated.proto b/staging/src/k8s.io/api/resource/v1beta1/generated.proto index 6eb0de25fad..5947aee5e2f 100644 --- a/staging/src/k8s.io/api/resource/v1beta1/generated.proto +++ b/staging/src/k8s.io/api/resource/v1beta1/generated.proto @@ -570,6 +570,9 @@ message DeviceClaim { // // +optional // +listType=atomic + // +k8s:listType=atomic + // +k8s:unique=map + // +k8s:listMapKey=name // +k8s:maxItems=32 repeated DeviceRequest requests = 1; diff --git a/staging/src/k8s.io/api/resource/v1beta1/types.go b/staging/src/k8s.io/api/resource/v1beta1/types.go index 2b3809b6eea..71387df02e3 100644 --- a/staging/src/k8s.io/api/resource/v1beta1/types.go +++ b/staging/src/k8s.io/api/resource/v1beta1/types.go @@ -729,6 +729,9 @@ type DeviceClaim struct { // // +optional // +listType=atomic + // +k8s:listType=atomic + // +k8s:unique=map + // +k8s:listMapKey=name // +k8s:maxItems=32 Requests []DeviceRequest `json:"requests" protobuf:"bytes,1,name=requests"` diff --git a/staging/src/k8s.io/api/resource/v1beta2/generated.proto b/staging/src/k8s.io/api/resource/v1beta2/generated.proto index 44a601c5718..39a27df5665 100644 --- a/staging/src/k8s.io/api/resource/v1beta2/generated.proto +++ b/staging/src/k8s.io/api/resource/v1beta2/generated.proto @@ -562,6 +562,9 @@ message DeviceClaim { // // +optional // +listType=atomic + // +k8s:listType=atomic + // +k8s:unique=map + // +k8s:listMapKey=name // +k8s:maxItems=32 repeated DeviceRequest requests = 1; diff --git a/staging/src/k8s.io/api/resource/v1beta2/types.go b/staging/src/k8s.io/api/resource/v1beta2/types.go index 81359f0d5b9..e8a82fb3616 100644 --- a/staging/src/k8s.io/api/resource/v1beta2/types.go +++ b/staging/src/k8s.io/api/resource/v1beta2/types.go @@ -725,6 +725,9 @@ type DeviceClaim struct { // // +optional // +listType=atomic + // +k8s:listType=atomic + // +k8s:unique=map + // +k8s:listMapKey=name // +k8s:maxItems=32 Requests []DeviceRequest `json:"requests" protobuf:"bytes,1,name=requests"`