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 <bpalmer@redhat.com>
This commit is contained in:
Bryce Palmer
2026-02-27 15:10:36 -05:00
parent 3deb264c91
commit 430b5d884a
7 changed files with 87 additions and 18 deletions

View File

@@ -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)...)

View File

@@ -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)...)

View File

@@ -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)...)

View File

@@ -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"), "<value omitted>", "error parsing data as JSON: invalid character 'o' in literal false (expecting 'a')"),

View File

@@ -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"`
}

View File

@@ -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"`
}

View File

@@ -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"`
}