diff --git a/pkg/registry/resource/resourceclaim/strategy.go b/pkg/registry/resource/resourceclaim/strategy.go index 32aa15fb0a4..94887cd4dd4 100644 --- a/pkg/registry/resource/resourceclaim/strategy.go +++ b/pkg/registry/resource/resourceclaim/strategy.go @@ -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,25 @@ 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 + rest.CompareDeclarativeErrorsAndEmitMismatches(ctx, errs, declarativeErrs, takeover, "dc_status_update") + + // Only apply declarative errors if takeover is enabled + if takeover { + errs = append(errs.RemoveCoveredByDeclarative(), declarativeErrs...) + } + } + return errs } // WarningsOnUpdate returns warnings for the given update. diff --git a/staging/src/k8s.io/api/resource/v1/types.go b/staging/src/k8s.io/api/resource/v1/types.go index f29504444ff..feb633d0f98 100644 --- a/staging/src/k8s.io/api/resource/v1/types.go +++ b/staging/src/k8s.io/api/resource/v1/types.go @@ -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 diff --git a/staging/src/k8s.io/api/resource/v1beta1/types.go b/staging/src/k8s.io/api/resource/v1beta1/types.go index 27967f38ec1..b879721b87f 100644 --- a/staging/src/k8s.io/api/resource/v1beta1/types.go +++ b/staging/src/k8s.io/api/resource/v1beta1/types.go @@ -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 diff --git a/staging/src/k8s.io/api/resource/v1beta2/types.go b/staging/src/k8s.io/api/resource/v1beta2/types.go index 9fa98abdf2d..2e5c4505e3f 100644 --- a/staging/src/k8s.io/api/resource/v1beta2/types.go +++ b/staging/src/k8s.io/api/resource/v1beta2/types.go @@ -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