KEP-5491: change ResourceSliceMaxDevicesWithTaintsOrConsumesCounters to ResourceSliceMaxDevicesWithAdvancedFeatures and add testcases with max devices with list attributes

This commit is contained in:
Shingo Omura
2026-03-20 18:35:34 +09:00
parent f44e1fbbb0
commit b613ed56a2
7 changed files with 128 additions and 14 deletions

View File

@@ -254,7 +254,14 @@ type ResourcePool struct {
const ResourceSliceMaxSharedCapacity = 128
const ResourceSliceMaxDevices = 128
const ResourceSliceMaxDevicesWithTaintsOrConsumesCounters = 64
// ResourceSliceMaxDevicesWithAdvancedFeatures defines the maximum number of devices in a ResourceSlice
// if any of those devices uses advanced features:
// - device taints (DRADeviceTaints feature gate)
// - consuming counters (DRAPartitionableDevices feature gate)
// - list attributes (DRAListTypeAttributes feature gate)
const ResourceSliceMaxDevicesWithAdvancedFeatures = 64
const PoolNameMaxLength = validation.DNS1123SubdomainMaxLength // Same as for a single node name.
const BindingConditionsMaxSize = 4
const BindingFailureConditionsMaxSize = 4

View File

@@ -711,8 +711,8 @@ func validateResourceSliceSpec(spec, oldSpec *resource.ResourceSliceSpec, fldPat
}
maxDevices := resource.ResourceSliceMaxDevices
if haveDeviceTaints(spec) || haveConsumesCounters(spec) {
maxDevices = resource.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters
if haveDeviceTaints(spec) || haveConsumesCounters(spec) || haveListAttributes(spec) {
maxDevices = resource.ResourceSliceMaxDevicesWithAdvancedFeatures
}
allErrs = append(allErrs, validateSet(spec.Devices, maxDevices,
func(device resource.Device, fldPath *field.Path) field.ErrorList {
@@ -732,6 +732,24 @@ func validateResourceSliceSpec(spec, oldSpec *resource.ResourceSliceSpec, fldPat
return allErrs
}
func haveListAttributes(spec *resource.ResourceSliceSpec) bool {
if spec == nil {
return false
}
for _, device := range spec.Devices {
for _, attribute := range device.Attributes {
if attribute.BoolValues != nil ||
attribute.IntValues != nil ||
attribute.StringValues != nil ||
attribute.VersionValues != nil {
return true
}
}
}
return false
}
func haveDeviceTaints(spec *resource.ResourceSliceSpec) bool {
if spec == nil {
return false

View File

@@ -197,7 +197,7 @@ func TestValidateResourceSlice(t *testing.T) {
},
"good-taints": {
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters)
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures)
for i := range slice.Spec.Devices {
slice.Spec.Devices[i].Taints = []resourceapi.DeviceTaint{{Key: "example.com/taint", Effect: resourceapi.DeviceTaintEffectNoExecute}}
}
@@ -205,9 +205,9 @@ func TestValidateResourceSlice(t *testing.T) {
}(),
},
"too-large-taints": {
wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters+1, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters)},
wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures+1, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures)},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters+1)
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures+1)
for i := range slice.Spec.Devices {
slice.Spec.Devices[i].Taints = []resourceapi.DeviceTaint{{Key: "example.com/taint", Effect: resourceapi.DeviceTaintEffectNoExecute}}
}
@@ -544,6 +544,74 @@ func TestValidateResourceSlice(t *testing.T) {
return slice
}(),
},
"max-devices-with-list-of-int-attributes": {
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures)
for i := range slice.Spec.Devices {
slice.Spec.Devices[i].Attributes["ints"] = resourceapi.DeviceAttribute{IntValues: []int64{1, 2, 3}}
}
return slice
}(),
},
"too-many-devices-with-list-of-int-attributes": {
wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures+1, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures)},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures+1)
slice.Spec.Devices[0].Attributes["ints"] = resourceapi.DeviceAttribute{IntValues: []int64{1, 2, 3}}
return slice
}(),
},
"max-devices-with-list-of-bool-attributes": {
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures)
for i := range slice.Spec.Devices {
slice.Spec.Devices[i].Attributes["bools"] = resourceapi.DeviceAttribute{BoolValues: []bool{true, false, true}}
}
return slice
}(),
},
"too-many-devices-with-list-of-bool-attributes": {
wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures+1, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures)},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures+1)
slice.Spec.Devices[0].Attributes["bools"] = resourceapi.DeviceAttribute{BoolValues: []bool{true, false, true}}
return slice
}(),
},
"max-devices-with-list-of-string-attributes": {
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures)
for i := range slice.Spec.Devices {
slice.Spec.Devices[i].Attributes["strings"] = resourceapi.DeviceAttribute{StringValues: []string{"a", "b", "c"}}
}
return slice
}(),
},
"too-many-devices-with-list-of-string-attributes": {
wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures+1, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures)},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures+1)
slice.Spec.Devices[0].Attributes["strings"] = resourceapi.DeviceAttribute{StringValues: []string{"a", "b", "c"}}
return slice
}(),
},
"max-devices-with-list-of-version-attributes": {
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures)
for i := range slice.Spec.Devices {
slice.Spec.Devices[i].Attributes["versions"] = resourceapi.DeviceAttribute{VersionValues: []string{"1.0.0", "2.0.0", "3.0.0"}}
}
return slice
}(),
},
"too-many-devices-with-list-of-version-attributes": {
wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures+1, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures)},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures+1)
slice.Spec.Devices[0].Attributes["versions"] = resourceapi.DeviceAttribute{VersionValues: []string{"1.0.0", "2.0.0", "3.0.0"}}
return slice
}(),
},
"good-attribute-names": {
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, 2)
@@ -1170,7 +1238,7 @@ func TestValidateResourceSlice(t *testing.T) {
},
"max-number-of-devices-with-consumes-counters": {
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters)
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures)
for i := range slice.Spec.Devices {
slice.Spec.Devices[i].ConsumesCounters = []resourceapi.DeviceCounterConsumption{
{
@@ -1183,9 +1251,9 @@ func TestValidateResourceSlice(t *testing.T) {
}(),
},
"too-many-devices-with-consumes-counters": {
wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters+1, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters)},
wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures+1, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures)},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters+1)
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithAdvancedFeatures+1)
slice.Spec.Devices[0].ConsumesCounters = []resourceapi.DeviceCounterConsumption{
{
CounterSet: "counterset-0",