diff --git a/pkg/apis/resource/validation/validation.go b/pkg/apis/resource/validation/validation.go index 7dc902dd354..ca7611cc66c 100644 --- a/pkg/apis/resource/validation/validation.go +++ b/pkg/apis/resource/validation/validation.go @@ -256,7 +256,7 @@ func validateDeviceConfiguration(config resource.DeviceConfiguration, fldPath *f func validateOpaqueConfiguration(config resource.OpaqueDeviceConfiguration, fldPath *field.Path, stored bool) field.ErrorList { var allErrs field.ErrorList allErrs = append(allErrs, validateDriverName(config.Driver, fldPath.Child("driver"))...) - allErrs = append(allErrs, validateRawExtension(config.Parameters, fldPath.Child("parameters"))...) + allErrs = append(allErrs, validateRawExtension(config.Parameters, fldPath.Child("parameters"), stored)...) return allErrs } @@ -750,18 +750,24 @@ func validateDeviceStatus(device resource.AllocatedDeviceStatus, fldPath *field. } allErrs = append(allErrs, metav1validation.ValidateConditions(device.Conditions, fldPath.Child("conditions"))...) if len(device.Data.Raw) > 0 { // Data is an optional field. - allErrs = append(allErrs, validateRawExtension(device.Data, fldPath.Child("data"))...) + allErrs = append(allErrs, validateRawExtension(device.Data, fldPath.Child("data"), false)...) } allErrs = append(allErrs, validateNetworkDeviceData(device.NetworkData, fldPath.Child("networkData"))...) return allErrs } // validateRawExtension validates RawExtension as in https://github.com/kubernetes/kubernetes/pull/125549/ -func validateRawExtension(rawExtension runtime.RawExtension, fldPath *field.Path) field.ErrorList { +func validateRawExtension(rawExtension runtime.RawExtension, fldPath *field.Path, stored bool) field.ErrorList { var allErrs field.ErrorList var v any if len(rawExtension.Raw) == 0 { allErrs = append(allErrs, field.Required(fldPath, "")) + } else if !stored && len(rawExtension.Raw) > resource.OpaqueParametersMaxLength { + // Don't even bother with parsing when too large. + // Only applies on create. Existing parameters are grand-fathered in + // because the limit was introduced in 1.32. This also means that it + // can be changed in the future. + allErrs = append(allErrs, field.TooLong(fldPath, "" /* unused */, resource.OpaqueParametersMaxLength)) } else if err := json.Unmarshal(rawExtension.Raw, &v); err != nil { allErrs = append(allErrs, field.Invalid(fldPath, "", fmt.Sprintf("error parsing data as JSON: %v", err.Error()))) } else if v == nil { diff --git a/pkg/apis/resource/validation/validation_resourceclaim_test.go b/pkg/apis/resource/validation/validation_resourceclaim_test.go index 7089acc7b9b..50b32312677 100644 --- a/pkg/apis/resource/validation/validation_resourceclaim_test.go +++ b/pkg/apis/resource/validation/validation_resourceclaim_test.go @@ -1096,6 +1096,24 @@ func TestValidateClaimStatusUpdate(t *testing.T) { }, deviceStatusFeatureGate: true, }, + "invalid-data-device-status-too-long": { + wantFailures: field.ErrorList{ + field.TooLong(field.NewPath("status", "devices").Index(0).Child("data"), "" /* unused */, resource.OpaqueParametersMaxLength), + }, + oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(), + update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { + claim.Status.Devices = []resource.AllocatedDeviceStatus{ + { + Driver: goodName, + Pool: goodName, + Device: goodName, + Data: runtime.RawExtension{Raw: []byte(`{"str": "` + strings.Repeat("x", resource.OpaqueParametersMaxLength-9-2+1 /* too large by one */) + `"}`)}, + }, + } + return claim + }, + deviceStatusFeatureGate: true, + }, "invalid-device-status-no-device": { wantFailures: field.ErrorList{ field.Invalid(field.NewPath("status", "devices").Index(0), structured.MakeDeviceID("b", "a", "r"), "must be an allocated device in the claim"), @@ -1188,6 +1206,24 @@ func TestValidateClaimStatusUpdate(t *testing.T) { }, deviceStatusFeatureGate: false, }, + "invalid-data-device-status-too-long-feature-gate": { + wantFailures: field.ErrorList{ + field.TooLong(field.NewPath("status", "devices").Index(0).Child("data"), "" /* unused */, resource.OpaqueParametersMaxLength), + }, + oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(), + update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { + claim.Status.Devices = []resource.AllocatedDeviceStatus{ + { + Driver: goodName, + Pool: goodName, + Device: goodName, + Data: runtime.RawExtension{Raw: []byte(`{"str": "` + strings.Repeat("x", resource.OpaqueParametersMaxLength-9-2+1 /* too large by one */) + `"}`)}, + }, + } + return claim + }, + deviceStatusFeatureGate: false, + }, "invalid-device-status-no-device-disabled-feature-gate": { wantFailures: field.ErrorList{ field.Invalid(field.NewPath("status", "devices").Index(0), structured.MakeDeviceID("b", "a", "r"), "must be an allocated device in the claim"),