diff --git a/pkg/apis/resource/validation/validation.go b/pkg/apis/resource/validation/validation.go index 866ed2fffe3..8349a1a84b5 100644 --- a/pkg/apis/resource/validation/validation.go +++ b/pkg/apis/resource/validation/validation.go @@ -780,6 +780,7 @@ func validateRawExtension(rawExtension *runtime.RawExtension, fldPath *field.Pat if rawExtension == nil { return allErrs } + // Validation of RawExtension as in https://github.com/kubernetes/kubernetes/pull/125549/ var v any if err := json.Unmarshal(rawExtension.Raw, &v); err != nil { allErrs = append(allErrs, field.Invalid(fldPath, "", fmt.Sprintf("error parsing data: %v", err.Error()))) diff --git a/pkg/apis/resource/validation/validation_resourceclaim_test.go b/pkg/apis/resource/validation/validation_resourceclaim_test.go index 060b701a2c9..72d8b96c29f 100644 --- a/pkg/apis/resource/validation/validation_resourceclaim_test.go +++ b/pkg/apis/resource/validation/validation_resourceclaim_test.go @@ -598,8 +598,6 @@ func TestValidateClaimUpdate(t *testing.T) { } func TestValidateClaimStatusUpdate(t *testing.T) { - featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAResourceClaimDeviceStatus, true) - validAllocatedClaim := validClaim.DeepCopy() validAllocatedClaim.Status = resource.ResourceClaimStatus{ Allocation: &resource.AllocationResult{ @@ -618,10 +616,11 @@ func TestValidateClaimStatusUpdate(t *testing.T) { validAllocatedClaimOld.Status.Allocation.Devices.Results[0].AdminAccess = nil // Not required in 1.31. scenarios := map[string]struct { - adminAccess bool - oldClaim *resource.ResourceClaim - update func(claim *resource.ResourceClaim) *resource.ResourceClaim - wantFailures field.ErrorList + adminAccess bool + deviceStatusFeatureGate bool + oldClaim *resource.ResourceClaim + update func(claim *resource.ResourceClaim) *resource.ResourceClaim + wantFailures field.ErrorList }{ "valid-no-op-update": { oldClaim: validClaim, @@ -1016,6 +1015,7 @@ func TestValidateClaimStatusUpdate(t *testing.T) { } return claim }, + deviceStatusFeatureGate: true, }, "invalid-device-status-duplicate": { wantFailures: field.ErrorList{ @@ -1038,6 +1038,7 @@ func TestValidateClaimStatusUpdate(t *testing.T) { } return claim }, + deviceStatusFeatureGate: true, }, "invalid-network-device-status": { wantFailures: field.ErrorList{ @@ -1060,6 +1061,7 @@ func TestValidateClaimStatusUpdate(t *testing.T) { } return claim }, + deviceStatusFeatureGate: true, }, "invalid-data-device-status": { wantFailures: field.ErrorList{ @@ -1080,6 +1082,7 @@ func TestValidateClaimStatusUpdate(t *testing.T) { } return claim }, + deviceStatusFeatureGate: true, }, "invalid-device-status-no-device": { wantFailures: field.ErrorList{ @@ -1097,12 +1100,92 @@ func TestValidateClaimStatusUpdate(t *testing.T) { } return claim }, + deviceStatusFeatureGate: true, + }, + "invalid-device-status-duplicate-disabled-feature-gate": { + wantFailures: nil, + oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(), + update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { + claim = claim.DeepCopy() + claim.Status.Devices = []resource.AllocatedDeviceStatus{ + { + Driver: goodName, + Pool: goodName, + Device: goodName, + }, + { + Driver: goodName, + Pool: goodName, + Device: goodName, + }, + } + return claim + }, + deviceStatusFeatureGate: false, + }, + "invalid-network-device-status-disabled-feature-gate": { + wantFailures: nil, + oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(), + update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { + claim = claim.DeepCopy() + claim.Status.Devices = []resource.AllocatedDeviceStatus{ + { + Driver: goodName, + Pool: goodName, + Device: goodName, + NetworkData: &resource.NetworkDeviceData{ + Addresses: []string{ + "300.9.8.0/24", + }, + }, + }, + } + return claim + }, + deviceStatusFeatureGate: false, + }, + "invalid-data-device-status-disabled-feature-gate": { + wantFailures: nil, + oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(), + update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { + claim = claim.DeepCopy() + claim.Status.Devices = []resource.AllocatedDeviceStatus{ + { + Driver: goodName, + Pool: goodName, + Device: goodName, + Data: &runtime.RawExtension{ + Raw: []byte(`foo`), + }, + }, + } + return claim + }, + deviceStatusFeatureGate: false, + }, + "invalid-device-status-no-device-disabled-feature-gate": { + wantFailures: nil, + oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(), + update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { + claim = claim.DeepCopy() + claim.Status.Devices = []resource.AllocatedDeviceStatus{ + { + Driver: "b", + Pool: "a", + Device: "r", + }, + } + return claim + }, + deviceStatusFeatureGate: false, }, } for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAAdminAccess, scenario.adminAccess) + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAResourceClaimDeviceStatus, scenario.deviceStatusFeatureGate) + scenario.oldClaim.ResourceVersion = "1" errs := ValidateResourceClaimStatusUpdate(scenario.update(scenario.oldClaim.DeepCopy()), scenario.oldClaim) assertFailures(t, scenario.wantFailures, errs) diff --git a/pkg/registry/resource/resourceclaim/strategy_test.go b/pkg/registry/resource/resourceclaim/strategy_test.go index 3c43558d18f..96741ad79fd 100644 --- a/pkg/registry/resource/resourceclaim/strategy_test.go +++ b/pkg/registry/resource/resourceclaim/strategy_test.go @@ -133,6 +133,79 @@ var objWithAdminAccessStatus = &resource.ResourceClaim{ }, } +var objWithRequestAndStatus = &resource.ResourceClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "valid-claim", + Namespace: "default", + }, + Spec: resource.ResourceClaimSpec{ + Devices: resource.DeviceClaim{ + Requests: []resource.DeviceRequest{ + { + Name: "test-request", + DeviceClassName: "test-device-class", + AllocationMode: resource.DeviceAllocationModeExactCount, + Count: 1, + }, + }, + }, + }, + Status: resource.ResourceClaimStatus{ + Allocation: &resource.AllocationResult{ + Devices: resource.DeviceAllocationResult{ + Results: []resource.DeviceRequestAllocationResult{ + { + Request: "test-request", + Driver: "test-driver", + Pool: "test-pool", + Device: "test-device", + }, + }, + }, + }, + }, +} + +var objWithGatedStatusFields = &resource.ResourceClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "valid-claim", + Namespace: "default", + }, + Spec: resource.ResourceClaimSpec{ + Devices: resource.DeviceClaim{ + Requests: []resource.DeviceRequest{ + { + Name: "test-request", + DeviceClassName: "test-device-class", + AllocationMode: resource.DeviceAllocationModeExactCount, + Count: 1, + }, + }, + }, + }, + Status: resource.ResourceClaimStatus{ + Allocation: &resource.AllocationResult{ + Devices: resource.DeviceAllocationResult{ + Results: []resource.DeviceRequestAllocationResult{ + { + Request: "test-request", + Driver: "test-driver", + Pool: "test-pool", + Device: "test-device", + }, + }, + }, + }, + Devices: []resource.AllocatedDeviceStatus{ + { + Driver: "test-driver", + Pool: "test-pool", + Device: "test-device", + }, + }, + }, +} + func TestStrategy(t *testing.T) { if !Strategy.NamespaceScoped() { t.Errorf("ResourceClaim must be namespace scoped") @@ -275,11 +348,12 @@ func TestStatusStrategyUpdate(t *testing.T) { ctx := genericapirequest.NewDefaultContext() testcases := map[string]struct { - oldObj *resource.ResourceClaim - newObj *resource.ResourceClaim - adminAccess bool - expectValidationError bool - expectObj *resource.ResourceClaim + oldObj *resource.ResourceClaim + newObj *resource.ResourceClaim + adminAccess bool + deviceStatusFeatureGate bool + expectValidationError bool + expectObj *resource.ResourceClaim }{ "no-changes-okay": { oldObj: obj, @@ -347,11 +421,29 @@ func TestStatusStrategyUpdate(t *testing.T) { return oldObj }(), }, + "drop-fields-devices-status": { + oldObj: objWithRequestAndStatus, + newObj: objWithGatedStatusFields, + deviceStatusFeatureGate: false, + expectObj: objWithRequestAndStatus, + }, + "keep-fields-devices-status": { + oldObj: objWithRequestAndStatus, + newObj: objWithGatedStatusFields, + deviceStatusFeatureGate: true, + expectObj: func() *resource.ResourceClaim { + expectObj := objWithGatedStatusFields.DeepCopy() + // Spec remains unchanged. + expectObj.Spec = objWithRequestAndStatus.Spec + return expectObj + }(), + }, } for name, tc := range testcases { t.Run(name, func(t *testing.T) { featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAAdminAccess, tc.adminAccess) + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAResourceClaimDeviceStatus, tc.deviceStatusFeatureGate) oldObj := tc.oldObj.DeepCopy() newObj := tc.newObj.DeepCopy() diff --git a/test/integration/resourceclaim/feature_enable_disable_test.go b/test/integration/resourceclaim/feature_enable_disable_test.go index b980195d033..88213c4674b 100644 --- a/test/integration/resourceclaim/feature_enable_disable_test.go +++ b/test/integration/resourceclaim/feature_enable_disable_test.go @@ -23,10 +23,12 @@ import ( "k8s.io/api/resource/v1alpha3" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" clientset "k8s.io/client-go/kubernetes" kubeapiservertesting "k8s.io/kubernetes/cmd/kube-apiserver/app/testing" "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/test/integration/framework" + "k8s.io/utils/ptr" ) // TestEnableDisableDRAResourceClaimDeviceStatus first test the feature gate disabled @@ -80,6 +82,17 @@ func TestEnableDisableDRAResourceClaimDeviceStatus(t *testing.T) { Driver: "foo", Pool: "foo", Device: "foo", + Data: &runtime.RawExtension{ + Raw: []byte(`{"kind": "foo", "apiVersion": "dra.example.com/v1"}`), + }, + NetworkData: &v1alpha3.NetworkDeviceData{ + InterfaceName: ptr.To("net-1"), + Addresses: []string{ + "10.9.8.0/24", + "2001:db8::/64", + }, + HWAddress: ptr.To("ea:9f:cb:40:b1:7b"), + }, }, }, } @@ -141,6 +154,17 @@ func TestEnableDisableDRAResourceClaimDeviceStatus(t *testing.T) { Driver: "bar", Pool: "bar", Device: "bar", + Data: &runtime.RawExtension{ + Raw: []byte(`{"kind": "foo", "apiVersion": "dra.example.com/v1"}`), + }, + NetworkData: &v1alpha3.NetworkDeviceData{ + InterfaceName: ptr.To("net-1"), + Addresses: []string{ + "10.9.8.0/24", + "2001:db8::/64", + }, + HWAddress: ptr.To("ea:9f:cb:40:b1:7b"), + }, }, }, } @@ -166,6 +190,17 @@ func TestEnableDisableDRAResourceClaimDeviceStatus(t *testing.T) { Driver: "bar", Pool: "bar", Device: "bar", + Data: &runtime.RawExtension{ + Raw: []byte(`{"kind": "foo", "apiVersion": "dra.example.com/v1"}`), + }, + NetworkData: &v1alpha3.NetworkDeviceData{ + InterfaceName: ptr.To("net-1"), + Addresses: []string{ + "10.9.8.0/24", + "2001:db8::/64", + }, + HWAddress: ptr.To("ea:9f:cb:40:b1:7b"), + }, }, }, }