From 430b5d884a2ae68591a18cf7eabc8707c72b4ee9 Mon Sep 17 00:00:00 2001 From: Bryce Palmer Date: Fri, 27 Feb 2026 15:10:36 -0500 Subject: [PATCH] apis/resource: migrate maxLength to maxByte tags for the InterfaceName and HardwareAddress fields so that declarative validation and handwritten validation are semantically equivalent. Signed-off-by: Bryce Palmer --- .../resource/v1/zz_generated.validations.go | 4 +- .../v1beta1/zz_generated.validations.go | 4 +- .../v1beta2/zz_generated.validations.go | 4 +- .../validation_resourceclaim_test.go | 69 +++++++++++++++++++ staging/src/k8s.io/api/resource/v1/types.go | 8 +-- .../src/k8s.io/api/resource/v1beta1/types.go | 8 +-- .../src/k8s.io/api/resource/v1beta2/types.go | 8 +-- 7 files changed, 87 insertions(+), 18 deletions(-) diff --git a/pkg/apis/resource/v1/zz_generated.validations.go b/pkg/apis/resource/v1/zz_generated.validations.go index a46ece5c3b3..78860ec8aea 100644 --- a/pkg/apis/resource/v1/zz_generated.validations.go +++ b/pkg/apis/resource/v1/zz_generated.validations.go @@ -1465,7 +1465,7 @@ func Validate_NetworkDeviceData(ctx context.Context, op operation.Operation, fld if earlyReturn { return // do not proceed } - errs = append(errs, validate.MaxLength(ctx, op, fldPath, obj, oldObj, 256).MarkAlpha()...) + errs = append(errs, validate.MaxBytes(ctx, op, fldPath, obj, oldObj, 256).MarkAlpha()...) return }(fldPath.Child("interfaceName"), &obj.InterfaceName, safe.Field(oldObj, func(oldObj *resourcev1.NetworkDeviceData) *string { return &oldObj.InterfaceName }), oldObj != nil)...) @@ -1508,7 +1508,7 @@ func Validate_NetworkDeviceData(ctx context.Context, op operation.Operation, fld if earlyReturn { return // do not proceed } - errs = append(errs, validate.MaxLength(ctx, op, fldPath, obj, oldObj, 128).MarkAlpha()...) + errs = append(errs, validate.MaxBytes(ctx, op, fldPath, obj, oldObj, 128).MarkAlpha()...) return }(fldPath.Child("hardwareAddress"), &obj.HardwareAddress, safe.Field(oldObj, func(oldObj *resourcev1.NetworkDeviceData) *string { return &oldObj.HardwareAddress }), oldObj != nil)...) diff --git a/pkg/apis/resource/v1beta1/zz_generated.validations.go b/pkg/apis/resource/v1beta1/zz_generated.validations.go index 1699f58670c..dfdfafb30e7 100644 --- a/pkg/apis/resource/v1beta1/zz_generated.validations.go +++ b/pkg/apis/resource/v1beta1/zz_generated.validations.go @@ -1496,7 +1496,7 @@ func Validate_NetworkDeviceData(ctx context.Context, op operation.Operation, fld if earlyReturn { return // do not proceed } - errs = append(errs, validate.MaxLength(ctx, op, fldPath, obj, oldObj, 256).MarkAlpha()...) + errs = append(errs, validate.MaxBytes(ctx, op, fldPath, obj, oldObj, 256).MarkAlpha()...) return }(fldPath.Child("interfaceName"), &obj.InterfaceName, safe.Field(oldObj, func(oldObj *resourcev1beta1.NetworkDeviceData) *string { return &oldObj.InterfaceName }), oldObj != nil)...) @@ -1539,7 +1539,7 @@ func Validate_NetworkDeviceData(ctx context.Context, op operation.Operation, fld if earlyReturn { return // do not proceed } - errs = append(errs, validate.MaxLength(ctx, op, fldPath, obj, oldObj, 128).MarkAlpha()...) + errs = append(errs, validate.MaxBytes(ctx, op, fldPath, obj, oldObj, 128).MarkAlpha()...) return }(fldPath.Child("hardwareAddress"), &obj.HardwareAddress, safe.Field(oldObj, func(oldObj *resourcev1beta1.NetworkDeviceData) *string { return &oldObj.HardwareAddress }), oldObj != nil)...) diff --git a/pkg/apis/resource/v1beta2/zz_generated.validations.go b/pkg/apis/resource/v1beta2/zz_generated.validations.go index 0939e739459..934c36458e9 100644 --- a/pkg/apis/resource/v1beta2/zz_generated.validations.go +++ b/pkg/apis/resource/v1beta2/zz_generated.validations.go @@ -1495,7 +1495,7 @@ func Validate_NetworkDeviceData(ctx context.Context, op operation.Operation, fld if earlyReturn { return // do not proceed } - errs = append(errs, validate.MaxLength(ctx, op, fldPath, obj, oldObj, 256).MarkAlpha()...) + errs = append(errs, validate.MaxBytes(ctx, op, fldPath, obj, oldObj, 256).MarkAlpha()...) return }(fldPath.Child("interfaceName"), &obj.InterfaceName, safe.Field(oldObj, func(oldObj *resourcev1beta2.NetworkDeviceData) *string { return &oldObj.InterfaceName }), oldObj != nil)...) @@ -1538,7 +1538,7 @@ func Validate_NetworkDeviceData(ctx context.Context, op operation.Operation, fld if earlyReturn { return // do not proceed } - errs = append(errs, validate.MaxLength(ctx, op, fldPath, obj, oldObj, 128).MarkAlpha()...) + errs = append(errs, validate.MaxBytes(ctx, op, fldPath, obj, oldObj, 128).MarkAlpha()...) return }(fldPath.Child("hardwareAddress"), &obj.HardwareAddress, safe.Field(oldObj, func(oldObj *resourcev1beta2.NetworkDeviceData) *string { return &oldObj.HardwareAddress }), oldObj != nil)...) diff --git a/pkg/apis/resource/validation/validation_resourceclaim_test.go b/pkg/apis/resource/validation/validation_resourceclaim_test.go index d3347754cba..be49d6aed6b 100644 --- a/pkg/apis/resource/validation/validation_resourceclaim_test.go +++ b/pkg/apis/resource/validation/validation_resourceclaim_test.go @@ -1394,6 +1394,45 @@ func TestValidateClaimStatusUpdate(t *testing.T) { }, deviceStatusFeatureGate: true, }, + "valid-network-device-status-with-multi-byte-interface-name-and-hardware-address": { + oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(), + update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { + claim.Status.Devices = []resource.AllocatedDeviceStatus{ + { + Driver: goodName, + Pool: goodName, + Device: goodName, + Conditions: []metav1.Condition{ + {Type: "test-0", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0}, + {Type: "test-1", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0}, + {Type: "test-2", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0}, + {Type: "test-3", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0}, + {Type: "test-4", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0}, + {Type: "test-5", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0}, + {Type: "test-6", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0}, + {Type: "test-7", Status: metav1.ConditionTrue, Reason: "test_reason", LastTransitionTime: metav1.Now(), ObservedGeneration: 0}, + }, + Data: &runtime.RawExtension{ + Raw: []byte(`{"kind": "foo", "apiVersion": "dra.example.com/v1"}`), + }, + NetworkData: &resource.NetworkDeviceData{ + InterfaceName: strings.Repeat("𝄞", 256/4), // the G clef unicode character is exactly 4 bytes so repeating this 256/4 times means that it is exactly 256 bytes worth of length and should be valid. + HardwareAddress: strings.Repeat("𝄞", 128/4), // the G clef unicode character is exactly 4 bytes so repeating this 128/4 times means that it is exactly 128 bytes worth of length and should be valid. + IPs: []string{ + "10.9.8.0/24", + "2001:db8::/64", + "10.9.8.1/24", + "2001:db8::1/64", + "10.9.8.2/24", "10.9.8.3/24", "10.9.8.4/24", "10.9.8.5/24", "10.9.8.6/24", "10.9.8.7/24", + "10.9.8.8/24", "10.9.8.9/24", "10.9.8.10/24", "10.9.8.11/24", "10.9.8.12/24", "10.9.8.13/24", + }, + }, + }, + } + return claim + }, + deviceStatusFeatureGate: true, + }, "invalid-device-status-duplicate": { wantFailures: field.ErrorList{ field.Duplicate(field.NewPath("status", "devices").Index(0).Child("networkData", "ips").Index(1), "2001:db8::1/64").MarkCoveredByDeclarative(), @@ -1483,6 +1522,36 @@ func TestValidateClaimStatusUpdate(t *testing.T) { }, deviceStatusFeatureGate: true, }, + "invalid-network-device-status-with-multi-byte-interface-name-and-hardware-address": { + wantFailures: field.ErrorList{ + field.TooLong(field.NewPath("status", "devices").Index(0).Child("networkData", "interfaceName"), "", resource.NetworkDeviceDataInterfaceNameMaxLength).MarkCoveredByDeclarative(), + field.TooLong(field.NewPath("status", "devices").Index(0).Child("networkData", "hardwareAddress"), "", resource.NetworkDeviceDataHardwareAddressMaxLength).MarkCoveredByDeclarative(), + field.Invalid(field.NewPath("status", "devices").Index(0).Child("networkData", "ips").Index(0), "300.9.8.0/24", "must be a valid address in CIDR form, (e.g. 10.9.8.7/24 or 2001:db8::1/64)"), + field.Invalid(field.NewPath("status", "devices").Index(0).Child("networkData", "ips").Index(1), "010.009.008.000/24", "must be in canonical form (\"10.9.8.0/24\")"), + field.Invalid(field.NewPath("status", "devices").Index(0).Child("networkData", "ips").Index(2), "2001:0db8::1/64", "must be in canonical form (\"2001:db8::1/64\")"), + }, + oldClaim: func() *resource.ResourceClaim { return validAllocatedClaim }(), + update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { + claim.Status.Devices = []resource.AllocatedDeviceStatus{ + { + Driver: goodName, + Pool: goodName, + Device: goodName, + NetworkData: &resource.NetworkDeviceData{ + InterfaceName: strings.Repeat("𝄞", resource.NetworkDeviceDataInterfaceNameMaxLength), // the G clef unicode character is exactly 4 bytes in length and should exceed the byte length limit for this field by a factor of 4 + HardwareAddress: strings.Repeat("𝄞", resource.NetworkDeviceDataHardwareAddressMaxLength), // the G clef unicode character is exactly 4 bytes in length and should exceed the byte length limit for this field by a factor of 4 + IPs: []string{ + "300.9.8.0/24", + "010.009.008.000/24", + "2001:0db8::1/64", + }, + }, + }, + } + return claim + }, + deviceStatusFeatureGate: true, + }, "invalid-data-device-status": { wantFailures: field.ErrorList{ field.Invalid(field.NewPath("status", "devices").Index(0).Child("data"), "", "error parsing data as JSON: invalid character 'o' in literal false (expecting 'a')"), diff --git a/staging/src/k8s.io/api/resource/v1/types.go b/staging/src/k8s.io/api/resource/v1/types.go index c7bca166c53..dee44a16137 100644 --- a/staging/src/k8s.io/api/resource/v1/types.go +++ b/staging/src/k8s.io/api/resource/v1/types.go @@ -1989,11 +1989,11 @@ type NetworkDeviceData struct { // the allocated device. This might be the name of a physical or virtual // network interface being configured in the pod. // - // Must not be longer than 256 characters. + // Must not be longer than 256 bytes. // // +optional // +k8s:alpha(since: "1.36")=+k8s:optional - // +k8s:alpha(since: "1.36")=+k8s:maxLength=256 + // +k8s:alpha(since: "1.36")=+k8s:maxBytes=256 InterfaceName string `json:"interfaceName,omitempty" protobuf:"bytes,1,opt,name=interfaceName"` // IPs lists the network addresses assigned to the device's network interface. @@ -2012,10 +2012,10 @@ type NetworkDeviceData struct { // HardwareAddress represents the hardware address (e.g. MAC Address) of the device's network interface. // - // Must not be longer than 128 characters. + // Must not be longer than 128 bytes. // // +optional // +k8s:alpha(since: "1.36")=+k8s:optional - // +k8s:alpha(since: "1.36")=+k8s:maxLength=128 + // +k8s:alpha(since: "1.36")=+k8s:maxBytes=128 HardwareAddress string `json:"hardwareAddress,omitempty" protobuf:"bytes,3,opt,name=hardwareAddress"` } diff --git a/staging/src/k8s.io/api/resource/v1beta1/types.go b/staging/src/k8s.io/api/resource/v1beta1/types.go index e532784b432..f0c2919b1d0 100644 --- a/staging/src/k8s.io/api/resource/v1beta1/types.go +++ b/staging/src/k8s.io/api/resource/v1beta1/types.go @@ -2014,11 +2014,11 @@ type NetworkDeviceData struct { // the allocated device. This might be the name of a physical or virtual // network interface being configured in the pod. // - // Must not be longer than 256 characters. + // Must not be longer than 256 bytes. // // +optional // +k8s:alpha(since: "1.36")=+k8s:optional - // +k8s:alpha(since: "1.36")=+k8s:maxLength=256 + // +k8s:alpha(since: "1.36")=+k8s:maxBytes=256 InterfaceName string `json:"interfaceName,omitempty" protobuf:"bytes,1,opt,name=interfaceName"` // IPs lists the network addresses assigned to the device's network interface. @@ -2039,10 +2039,10 @@ type NetworkDeviceData struct { // HardwareAddress represents the hardware address (e.g. MAC Address) of the device's network interface. // - // Must not be longer than 128 characters. + // Must not be longer than 128 bytes. // // +optional // +k8s:alpha(since: "1.36")=+k8s:optional - // +k8s:alpha(since: "1.36")=+k8s:maxLength=128 + // +k8s:alpha(since: "1.36")=+k8s:maxBytes=128 HardwareAddress string `json:"hardwareAddress,omitempty" protobuf:"bytes,3,opt,name=hardwareAddress"` } diff --git a/staging/src/k8s.io/api/resource/v1beta2/types.go b/staging/src/k8s.io/api/resource/v1beta2/types.go index fb8f30672c3..e5c04f58d1a 100644 --- a/staging/src/k8s.io/api/resource/v1beta2/types.go +++ b/staging/src/k8s.io/api/resource/v1beta2/types.go @@ -2001,11 +2001,11 @@ type NetworkDeviceData struct { // the allocated device. This might be the name of a physical or virtual // network interface being configured in the pod. // - // Must not be longer than 256 characters. + // Must not be longer than 256 bytes. // // +optional // +k8s:alpha(since: "1.36")=+k8s:optional - // +k8s:alpha(since: "1.36")=+k8s:maxLength=256 + // +k8s:alpha(since: "1.36")=+k8s:maxBytes=256 InterfaceName string `json:"interfaceName,omitempty" protobuf:"bytes,1,opt,name=interfaceName"` // IPs lists the network addresses assigned to the device's network interface. @@ -2024,10 +2024,10 @@ type NetworkDeviceData struct { // HardwareAddress represents the hardware address (e.g. MAC Address) of the device's network interface. // - // Must not be longer than 128 characters. + // Must not be longer than 128 bytes. // // +optional // +k8s:alpha(since: "1.36")=+k8s:optional - // +k8s:alpha(since: "1.36")=+k8s:maxLength=128 + // +k8s:alpha(since: "1.36")=+k8s:maxBytes=128 HardwareAddress string `json:"hardwareAddress,omitempty" protobuf:"bytes,3,opt,name=hardwareAddress"` }