From 380c4c222eecd4370b34ad27213e9d91e22cf096 Mon Sep 17 00:00:00 2001 From: yongruilin Date: Mon, 8 Sep 2025 22:13:29 +0000 Subject: [PATCH 1/4] feat(validation-gen): Add declarative validation support for ResourceClaim/(v1,v1beta1,v1beta2) --- pkg/api/testing/validation_test.go | 3 + pkg/apis/resource/v1/doc.go | 2 + .../resource/v1/zz_generated.validations.go | 34 ++++ pkg/apis/resource/v1beta1/doc.go | 2 + .../v1beta1/zz_generated.validations.go | 34 ++++ pkg/apis/resource/v1beta2/doc.go | 2 + .../v1beta2/zz_generated.validations.go | 34 ++++ .../declarative_validation_test.go | 155 ++++++++++++++++++ .../resource/resourceclaim/strategy.go | 25 ++- 9 files changed, 289 insertions(+), 2 deletions(-) create mode 100644 pkg/apis/resource/v1/zz_generated.validations.go create mode 100644 pkg/apis/resource/v1beta1/zz_generated.validations.go create mode 100644 pkg/apis/resource/v1beta2/zz_generated.validations.go create mode 100644 pkg/registry/resource/resourceclaim/declarative_validation_test.go diff --git a/pkg/api/testing/validation_test.go b/pkg/api/testing/validation_test.go index 922fb526f76..82f9fcbf106 100644 --- a/pkg/api/testing/validation_test.go +++ b/pkg/api/testing/validation_test.go @@ -35,6 +35,9 @@ func TestVersionedValidationByFuzzing(t *testing.T) { {Group: "certificates.k8s.io", Version: "v1"}, {Group: "certificates.k8s.io", Version: "v1alpha1"}, {Group: "certificates.k8s.io", Version: "v1beta1"}, + {Group: "resource.k8s.io", Version: "v1beta1"}, + {Group: "resource.k8s.io", Version: "v1beta2"}, + {Group: "resource.k8s.io", Version: "v1"}, } for _, gv := range typesWithDeclarativeValidation { diff --git a/pkg/apis/resource/v1/doc.go b/pkg/apis/resource/v1/doc.go index 9166fefdbd6..d608dbef1ef 100644 --- a/pkg/apis/resource/v1/doc.go +++ b/pkg/apis/resource/v1/doc.go @@ -18,6 +18,8 @@ limitations under the License. // +k8s:conversion-gen-external-types=k8s.io/api/resource/v1 // +k8s:defaulter-gen=TypeMeta // +k8s:defaulter-gen-input=k8s.io/api/resource/v1 +// +k8s:validation-gen=TypeMeta +// +k8s:validation-gen-input=k8s.io/api/resource/v1 // Package v1 is the v1 version of the resource API. package v1 diff --git a/pkg/apis/resource/v1/zz_generated.validations.go b/pkg/apis/resource/v1/zz_generated.validations.go new file mode 100644 index 00000000000..73e28035dba --- /dev/null +++ b/pkg/apis/resource/v1/zz_generated.validations.go @@ -0,0 +1,34 @@ +//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 v1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +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 { + return nil +} diff --git a/pkg/apis/resource/v1beta1/doc.go b/pkg/apis/resource/v1beta1/doc.go index f014667f8da..f95eb5f3c9f 100644 --- a/pkg/apis/resource/v1beta1/doc.go +++ b/pkg/apis/resource/v1beta1/doc.go @@ -18,6 +18,8 @@ limitations under the License. // +k8s:conversion-gen-external-types=k8s.io/api/resource/v1beta1 // +k8s:defaulter-gen=TypeMeta // +k8s:defaulter-gen-input=k8s.io/api/resource/v1beta1 +// +k8s:validation-gen=TypeMeta +// +k8s:validation-gen-input=k8s.io/api/resource/v1beta1 // Package v1beta1 is the v1beta1 version of the resource API. package v1beta1 diff --git a/pkg/apis/resource/v1beta1/zz_generated.validations.go b/pkg/apis/resource/v1beta1/zz_generated.validations.go new file mode 100644 index 00000000000..0b9e874b635 --- /dev/null +++ b/pkg/apis/resource/v1beta1/zz_generated.validations.go @@ -0,0 +1,34 @@ +//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 v1beta1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +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 { + return nil +} diff --git a/pkg/apis/resource/v1beta2/doc.go b/pkg/apis/resource/v1beta2/doc.go index beea4f266b9..ca447ac7154 100644 --- a/pkg/apis/resource/v1beta2/doc.go +++ b/pkg/apis/resource/v1beta2/doc.go @@ -18,6 +18,8 @@ limitations under the License. // +k8s:conversion-gen-external-types=k8s.io/api/resource/v1beta2 // +k8s:defaulter-gen=TypeMeta // +k8s:defaulter-gen-input=k8s.io/api/resource/v1beta2 +// +k8s:validation-gen=TypeMeta +// +k8s:validation-gen-input=k8s.io/api/resource/v1beta2 // Package v1beta2 is the v1beta2 version of the resource API. package v1beta2 diff --git a/pkg/apis/resource/v1beta2/zz_generated.validations.go b/pkg/apis/resource/v1beta2/zz_generated.validations.go new file mode 100644 index 00000000000..2b709ed6643 --- /dev/null +++ b/pkg/apis/resource/v1beta2/zz_generated.validations.go @@ -0,0 +1,34 @@ +//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 v1beta2 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +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 { + return nil +} diff --git a/pkg/registry/resource/resourceclaim/declarative_validation_test.go b/pkg/registry/resource/resourceclaim/declarative_validation_test.go new file mode 100644 index 00000000000..85f12b88bc6 --- /dev/null +++ b/pkg/registry/resource/resourceclaim/declarative_validation_test.go @@ -0,0 +1,155 @@ +/* +Copyright 2025 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. +*/ +package resourceclaim + +import ( + "testing" + + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/validation/field" + genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + utilfeature "k8s.io/apiserver/pkg/util/feature" + "k8s.io/client-go/kubernetes/fake" + featuregatetesting "k8s.io/component-base/featuregate/testing" + apitesting "k8s.io/kubernetes/pkg/api/testing" + "k8s.io/kubernetes/pkg/apis/resource" + "k8s.io/kubernetes/pkg/features" +) + +var apiVersions = []string{"v1beta1", "v1beta2", "v1"} // "v1alpha3" is excluded because it doesn't have ResourceClaim + +func TestDeclarativeValidate(t *testing.T) { + for _, apiVersion := range apiVersions { + t.Run(apiVersion, func(t *testing.T) { + testDeclarativeValidate(t, apiVersion) + }) + } +} + +func testDeclarativeValidate(t *testing.T, apiVersion string) { + ctx := genericapirequest.WithRequestInfo(genericapirequest.NewDefaultContext(), &genericapirequest.RequestInfo{ + APIGroup: "resource.k8s.io", + APIVersion: apiVersion, + Resource: "resourceclaims", + }) + fakeClient := fake.NewSimpleClientset() + mockNSClient := fakeClient.CoreV1().Namespaces() + Strategy := NewStrategy(mockNSClient) + testCases := map[string]struct { + input resource.ResourceClaim + expectedErrs field.ErrorList + }{ + "valid": { + input: mkValidResourceClaim(), + }, + // TODO: Add more test cases + } + for k, tc := range testCases { + t.Run(k, func(t *testing.T) { + var declarativeTakeoverErrs field.ErrorList + var imperativeErrs field.ErrorList + for _, gateVal := range []bool{true, false} { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DeclarativeValidation, gateVal) + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DeclarativeValidationTakeover, gateVal) + + errs := Strategy.Validate(ctx, &tc.input) + if gateVal { + declarativeTakeoverErrs = errs + } else { + imperativeErrs = errs + } + } + equivalenceMatcher := field.ErrorMatcher{}.ByType().ByField().ByOrigin() + equivalenceMatcher.Test(t, imperativeErrs, declarativeTakeoverErrs) + + apitesting.VerifyVersionedValidationEquivalence(t, &tc.input, nil) + }) + } +} + +func TestDeclarativeValidateUpdate(t *testing.T) { + for _, apiVersion := range apiVersions { + t.Run(apiVersion, func(t *testing.T) { + testDeclarativeValidateUpdate(t, apiVersion) + }) + } +} + +func testDeclarativeValidateUpdate(t *testing.T, apiVersion string) { + ctx := genericapirequest.WithRequestInfo(genericapirequest.NewDefaultContext(), &genericapirequest.RequestInfo{ + APIGroup: "resource.k8s.io", + APIVersion: apiVersion, + Resource: "resourceclaims", + }) + fakeClient := fake.NewSimpleClientset() + mockNSClient := fakeClient.CoreV1().Namespaces() + Strategy := NewStrategy(mockNSClient) + validClaim := mkValidResourceClaim() + testCases := map[string]struct { + update resource.ResourceClaim + old resource.ResourceClaim + expectedErrs field.ErrorList + }{ + "valid": { + update: validClaim, + old: validClaim, + }, + // TODO: Add more test cases + } + for k, tc := range testCases { + t.Run(k, func(t *testing.T) { + var declarativeTakeoverErrs field.ErrorList + var imperativeErrs field.ErrorList + for _, gateVal := range []bool{true, false} { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DeclarativeValidation, gateVal) + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DeclarativeValidationTakeover, gateVal) + + errs := Strategy.ValidateUpdate(ctx, &tc.update, &tc.old) + if gateVal { + declarativeTakeoverErrs = errs + } else { + imperativeErrs = errs + } + } + equivalenceMatcher := field.ErrorMatcher{}.ByType().ByField().ByOrigin() + equivalenceMatcher.Test(t, imperativeErrs, declarativeTakeoverErrs) + + apitesting.VerifyVersionedValidationEquivalence(t, &tc.update, &tc.old) + }) + } +} + +func mkValidResourceClaim() resource.ResourceClaim { + return resource.ResourceClaim{ + ObjectMeta: v1.ObjectMeta{ + Name: "valid-claim", + Namespace: "default", + }, + Spec: resource.ResourceClaimSpec{ + Devices: resource.DeviceClaim{ + Requests: []resource.DeviceRequest{ + { + Name: "req-0", + Exactly: &resource.ExactDeviceRequest{ + DeviceClassName: "class", + AllocationMode: resource.DeviceAllocationModeAll, + }, + }, + }, + }, + }, + } +} diff --git a/pkg/registry/resource/resourceclaim/strategy.go b/pkg/registry/resource/resourceclaim/strategy.go index 5b57d4c842a..6799bafc149 100644 --- a/pkg/registry/resource/resourceclaim/strategy.go +++ b/pkg/registry/resource/resourceclaim/strategy.go @@ -28,6 +28,7 @@ import ( "k8s.io/apimachinery/pkg/util/sets" "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apiserver/pkg/registry/generic" + "k8s.io/apiserver/pkg/registry/rest" "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" utilfeature "k8s.io/apiserver/pkg/util/feature" @@ -96,7 +97,17 @@ func (s *resourceclaimStrategy) Validate(ctx context.Context, obj runtime.Object claim := obj.(*resource.ResourceClaim) allErrs := resourceutils.AuthorizedForAdmin(ctx, claim.Spec.Devices.Requests, claim.Namespace, s.nsClient) - return append(allErrs, validation.ValidateResourceClaim(claim)...) + allErrs = append(allErrs, validation.ValidateResourceClaim(claim)...) + + if utilfeature.DefaultFeatureGate.Enabled(features.DeclarativeValidation) { + takeover := utilfeature.DefaultFeatureGate.Enabled(features.DeclarativeValidationTakeover) + declarativeErrs := rest.ValidateDeclaratively(ctx, legacyscheme.Scheme, claim, rest.WithTakeover(takeover)) + rest.CompareDeclarativeErrorsAndEmitMismatches(ctx, allErrs, declarativeErrs, takeover) + if takeover { + allErrs = append(allErrs.RemoveCoveredByDeclarative(), declarativeErrs...) + } + } + return allErrs } func (*resourceclaimStrategy) WarningsOnCreate(ctx context.Context, obj runtime.Object) []string { @@ -123,7 +134,17 @@ func (s *resourceclaimStrategy) ValidateUpdate(ctx context.Context, obj, old run oldClaim := old.(*resource.ResourceClaim) // AuthorizedForAdmin isn't needed here because the spec is immutable. errorList := validation.ValidateResourceClaim(newClaim) - return append(errorList, validation.ValidateResourceClaimUpdate(newClaim, oldClaim)...) + errorList = append(errorList, validation.ValidateResourceClaimUpdate(newClaim, oldClaim)...) + + if utilfeature.DefaultFeatureGate.Enabled(features.DeclarativeValidation) { + takeover := utilfeature.DefaultFeatureGate.Enabled(features.DeclarativeValidationTakeover) + declarativeErrs := rest.ValidateUpdateDeclaratively(ctx, legacyscheme.Scheme, newClaim, oldClaim, rest.WithTakeover(takeover)) + rest.CompareDeclarativeErrorsAndEmitMismatches(ctx, errorList, declarativeErrs, takeover) + if takeover { + errorList = append(errorList.RemoveCoveredByDeclarative(), declarativeErrs...) + } + } + return errorList } func (*resourceclaimStrategy) WarningsOnUpdate(ctx context.Context, obj, old runtime.Object) []string { From 7c45b1aa3b74711c8adc5cc08fa75ec64273a490 Mon Sep 17 00:00:00 2001 From: yongruilin Date: Mon, 15 Sep 2025 18:21:58 +0000 Subject: [PATCH 2/4] refactor: simplify declarative validation tests for ResourceClaim --- .../declarative_validation_test.go | 39 +------------------ 1 file changed, 2 insertions(+), 37 deletions(-) diff --git a/pkg/registry/resource/resourceclaim/declarative_validation_test.go b/pkg/registry/resource/resourceclaim/declarative_validation_test.go index 85f12b88bc6..276be9b11ce 100644 --- a/pkg/registry/resource/resourceclaim/declarative_validation_test.go +++ b/pkg/registry/resource/resourceclaim/declarative_validation_test.go @@ -21,12 +21,9 @@ import ( v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/validation/field" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/kubernetes/fake" - featuregatetesting "k8s.io/component-base/featuregate/testing" apitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/apis/resource" - "k8s.io/kubernetes/pkg/features" ) var apiVersions = []string{"v1beta1", "v1beta2", "v1"} // "v1alpha3" is excluded because it doesn't have ResourceClaim @@ -59,23 +56,7 @@ func testDeclarativeValidate(t *testing.T, apiVersion string) { } for k, tc := range testCases { t.Run(k, func(t *testing.T) { - var declarativeTakeoverErrs field.ErrorList - var imperativeErrs field.ErrorList - for _, gateVal := range []bool{true, false} { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DeclarativeValidation, gateVal) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DeclarativeValidationTakeover, gateVal) - - errs := Strategy.Validate(ctx, &tc.input) - if gateVal { - declarativeTakeoverErrs = errs - } else { - imperativeErrs = errs - } - } - equivalenceMatcher := field.ErrorMatcher{}.ByType().ByField().ByOrigin() - equivalenceMatcher.Test(t, imperativeErrs, declarativeTakeoverErrs) - - apitesting.VerifyVersionedValidationEquivalence(t, &tc.input, nil) + apitesting.VerifyValidationEquivalence(t, ctx, &tc.input, Strategy.Validate, tc.expectedErrs) }) } } @@ -111,23 +92,7 @@ func testDeclarativeValidateUpdate(t *testing.T, apiVersion string) { } for k, tc := range testCases { t.Run(k, func(t *testing.T) { - var declarativeTakeoverErrs field.ErrorList - var imperativeErrs field.ErrorList - for _, gateVal := range []bool{true, false} { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DeclarativeValidation, gateVal) - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DeclarativeValidationTakeover, gateVal) - - errs := Strategy.ValidateUpdate(ctx, &tc.update, &tc.old) - if gateVal { - declarativeTakeoverErrs = errs - } else { - imperativeErrs = errs - } - } - equivalenceMatcher := field.ErrorMatcher{}.ByType().ByField().ByOrigin() - equivalenceMatcher.Test(t, imperativeErrs, declarativeTakeoverErrs) - - apitesting.VerifyVersionedValidationEquivalence(t, &tc.update, &tc.old) + apitesting.VerifyUpdateValidationEquivalence(t, ctx, &tc.update, &tc.old, Strategy.ValidateUpdate, tc.expectedErrs) }) } } From c0fcb10acfa16b0ab38ab58866d2e7244296032a Mon Sep 17 00:00:00 2001 From: yongruilin Date: Mon, 15 Sep 2025 19:14:55 +0000 Subject: [PATCH 3/4] fix(tests): update fake client initialization and add resource version handling in validation tests --- .../resourceclaim/declarative_validation_test.go | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/pkg/registry/resource/resourceclaim/declarative_validation_test.go b/pkg/registry/resource/resourceclaim/declarative_validation_test.go index 276be9b11ce..baabe9e9f53 100644 --- a/pkg/registry/resource/resourceclaim/declarative_validation_test.go +++ b/pkg/registry/resource/resourceclaim/declarative_validation_test.go @@ -5,7 +5,7 @@ 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 + 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, @@ -13,6 +13,7 @@ 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. */ + package resourceclaim import ( @@ -42,7 +43,7 @@ func testDeclarativeValidate(t *testing.T, apiVersion string) { APIVersion: apiVersion, Resource: "resourceclaims", }) - fakeClient := fake.NewSimpleClientset() + fakeClient := fake.NewClientset() mockNSClient := fakeClient.CoreV1().Namespaces() Strategy := NewStrategy(mockNSClient) testCases := map[string]struct { @@ -75,7 +76,7 @@ func testDeclarativeValidateUpdate(t *testing.T, apiVersion string) { APIVersion: apiVersion, Resource: "resourceclaims", }) - fakeClient := fake.NewSimpleClientset() + fakeClient := fake.NewClientset() mockNSClient := fakeClient.CoreV1().Namespaces() Strategy := NewStrategy(mockNSClient) validClaim := mkValidResourceClaim() @@ -92,6 +93,8 @@ func testDeclarativeValidateUpdate(t *testing.T, apiVersion string) { } for k, tc := range testCases { t.Run(k, func(t *testing.T) { + tc.old.ResourceVersion = "1" + tc.update.ResourceVersion = "2" apitesting.VerifyUpdateValidationEquivalence(t, ctx, &tc.update, &tc.old, Strategy.ValidateUpdate, tc.expectedErrs) }) } From eca1cfb259f609b0291f375ab9a1765ef20d3403 Mon Sep 17 00:00:00 2001 From: yongruilin Date: Tue, 16 Sep 2025 04:56:14 +0000 Subject: [PATCH 4/4] chore(validation): add validation identifier for declarative validation in ResourceClaim --- pkg/registry/resource/resourceclaim/strategy.go | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/pkg/registry/resource/resourceclaim/strategy.go b/pkg/registry/resource/resourceclaim/strategy.go index 6799bafc149..32aa15fb0a4 100644 --- a/pkg/registry/resource/resourceclaim/strategy.go +++ b/pkg/registry/resource/resourceclaim/strategy.go @@ -101,8 +101,9 @@ func (s *resourceclaimStrategy) Validate(ctx context.Context, obj runtime.Object if utilfeature.DefaultFeatureGate.Enabled(features.DeclarativeValidation) { takeover := utilfeature.DefaultFeatureGate.Enabled(features.DeclarativeValidationTakeover) - declarativeErrs := rest.ValidateDeclaratively(ctx, legacyscheme.Scheme, claim, rest.WithTakeover(takeover)) - rest.CompareDeclarativeErrorsAndEmitMismatches(ctx, allErrs, declarativeErrs, takeover) + const validationIdentifier = "resourceclaim_create" + declarativeErrs := rest.ValidateDeclaratively(ctx, legacyscheme.Scheme, claim, rest.WithTakeover(takeover), rest.WithValidationIdentifier(validationIdentifier)) + rest.CompareDeclarativeErrorsAndEmitMismatches(ctx, allErrs, declarativeErrs, takeover, validationIdentifier) if takeover { allErrs = append(allErrs.RemoveCoveredByDeclarative(), declarativeErrs...) } @@ -138,8 +139,9 @@ func (s *resourceclaimStrategy) ValidateUpdate(ctx context.Context, obj, old run if utilfeature.DefaultFeatureGate.Enabled(features.DeclarativeValidation) { takeover := utilfeature.DefaultFeatureGate.Enabled(features.DeclarativeValidationTakeover) - declarativeErrs := rest.ValidateUpdateDeclaratively(ctx, legacyscheme.Scheme, newClaim, oldClaim, rest.WithTakeover(takeover)) - rest.CompareDeclarativeErrorsAndEmitMismatches(ctx, errorList, declarativeErrs, takeover) + const validationIdentifier = "resourceclaim_update" + declarativeErrs := rest.ValidateUpdateDeclaratively(ctx, legacyscheme.Scheme, newClaim, oldClaim, rest.WithTakeover(takeover), rest.WithValidationIdentifier(validationIdentifier)) + rest.CompareDeclarativeErrorsAndEmitMismatches(ctx, errorList, declarativeErrs, takeover, validationIdentifier) if takeover { errorList = append(errorList.RemoveCoveredByDeclarative(), declarativeErrs...) }