DRA: Update validation for Partitionable Devices

This commit is contained in:
Morten Torkildsen
2025-11-06 16:05:57 +00:00
parent ef3f6a2507
commit 93fc7422ee
8 changed files with 744 additions and 344 deletions

View File

@@ -249,15 +249,11 @@ type ResourcePool struct {
const ResourceSliceMaxSharedCapacity = 128
const ResourceSliceMaxDevices = 128
const ResourceSliceMaxDevicesWithTaints = 64
const ResourceSliceMaxDevicesWithTaintsOrConsumesCounters = 64
const PoolNameMaxLength = validation.DNS1123SubdomainMaxLength // Same as for a single node name.
const BindingConditionsMaxSize = 4
const BindingFailureConditionsMaxSize = 4
// Defines the max number of shared counters that can be specified
// in a ResourceSlice. The number is summed up across all sets.
const ResourceSliceMaxSharedCounters = 32
// Defines the maximum number of counter sets (through the
// SharedCounters field) that can be defined in a ResourceSlice.
const ResourceSliceMaxCounterSets = 8
@@ -275,11 +271,6 @@ const ResourceSliceMaxDeviceCounterConsumptionsPerDevice = 2
// per device counter consumption.
const ResourceSliceMaxCountersPerDeviceCounterConsumption = 32
// Defines the maximum number of counters that can be defined
// in device counter consumptions across all devices in a
// ResourceSlice.
const ResourceSliceMaxConsumedCountersPerResourceSlice = 2048
// Device represents one individual hardware instance that can be selected based
// on its attributes. Besides the name, exactly one field must be set.
type Device struct {
@@ -557,14 +548,6 @@ type CapacityRequestPolicyRange struct {
// Limit for the sum of the number of entries in both attributes and capacity.
const ResourceSliceMaxAttributesAndCapacitiesPerDevice = 32
// Limit for the total number of counters in each device.
const ResourceSliceMaxCountersPerDevice = 32
// Limit for the total number of counters defined in devices in
// a ResourceSlice. We want to allow up to 64 devices to specify
// up to 16 counters, so the limit for the ResourceSlice will be 1024.
const ResourceSliceMaxDeviceCountersPerSlice = 1024 // 64 * 16
// QualifiedName is the name of a device attribute or capacity.
//
// Attributes and capacities are defined either by the owner of the specific

View File

@@ -703,44 +703,28 @@ func validateResourceSliceSpec(spec, oldSpec *resource.ResourceSliceSpec, fldPat
"exactly one of `nodeName`, `nodeSelector`, `allNodes`, `perDeviceNodeSelection` is required, but multiple fields are set"))
}
sharedCounterToCounterNames := gatherSharedCounterCounterNames(spec.SharedCounters)
if spec.SharedCounters != nil && spec.Devices != nil {
allErrs = append(allErrs, field.Invalid(fldPath, "", "only one of `sharedCounters` or `devices` is allowed"))
}
maxDevices := resource.ResourceSliceMaxDevices
if haveDeviceTaints(spec) {
maxDevices = resource.ResourceSliceMaxDevicesWithTaints
if haveDeviceTaints(spec) || haveConsumesCounters(spec) {
maxDevices = resource.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters
}
allErrs = append(allErrs, validateSet(spec.Devices, maxDevices,
func(device resource.Device, fldPath *field.Path) field.ErrorList {
oldDevice := lookupDevice(oldSpec, device.Name)
return validateDevice(device, oldDevice, fldPath, sharedCounterToCounterNames, spec.PerDeviceNodeSelection)
return validateDevice(device, oldDevice, fldPath, spec.PerDeviceNodeSelection)
},
func(device resource.Device) string {
return device.Name
}, fldPath.Child("devices"))...)
// Size limit for total number of counters in devices enforced here.
numDeviceCounters := 0
for _, device := range spec.Devices {
for _, c := range device.ConsumesCounters {
numDeviceCounters += len(c.Counters)
}
}
if numDeviceCounters > resource.ResourceSliceMaxDeviceCountersPerSlice {
allErrs = append(allErrs, field.Invalid(fldPath.Child("devices"), numDeviceCounters, fmt.Sprintf("the total number of counters in devices must not exceed %d", resource.ResourceSliceMaxDeviceCountersPerSlice)))
}
// Size limit for all shared counters enforced here.
numCounters := 0
for _, set := range spec.SharedCounters {
numCounters += len(set.Counters)
}
if numCounters > resource.ResourceSliceMaxSharedCounters {
allErrs = append(allErrs, field.Invalid(fldPath.Child("sharedCounters"), numCounters, fmt.Sprintf("the total number of shared counters must not exceed %d", resource.ResourceSliceMaxSharedCounters)))
}
allErrs = append(allErrs, validateSet(spec.SharedCounters, -1,
allErrs = append(allErrs, validateSet(spec.SharedCounters, resource.ResourceSliceMaxCounterSets,
validateCounterSet,
func(counterSet resource.CounterSet) string {
return counterSet.Name
}, fldPath.Child("sharedCounters"))...)
}, fldPath.Child("sharedCounters"), sizeCovered, uniquenessCovered)...)
return allErrs
}
@@ -758,6 +742,19 @@ func haveDeviceTaints(spec *resource.ResourceSliceSpec) bool {
return false
}
func haveConsumesCounters(spec *resource.ResourceSliceSpec) bool {
if spec == nil {
return false
}
for _, device := range spec.Devices {
if len(device.ConsumesCounters) > 0 {
return true
}
}
return false
}
func lookupDevice(spec *resource.ResourceSliceSpec, deviceName string) *resource.Device {
if spec == nil {
return nil
@@ -774,33 +771,21 @@ func lookupDevice(spec *resource.ResourceSliceSpec, deviceName string) *resource
func validateCounterSet(counterSet resource.CounterSet, fldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList
if counterSet.Name == "" {
allErrs = append(allErrs, field.Required(fldPath.Child("name"), ""))
allErrs = append(allErrs, field.Required(fldPath.Child("name"), "").MarkCoveredByDeclarative())
} else {
allErrs = append(allErrs, validateCounterName(counterSet.Name, fldPath.Child("name"))...)
allErrs = append(allErrs, validateCounterName(counterSet.Name, fldPath.Child("name"))...).MarkCoveredByDeclarative()
}
if len(counterSet.Counters) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("counters"), ""))
} else {
// The size limit is enforced for across all sets by the caller.
allErrs = append(allErrs, validateMap(counterSet.Counters, -1, validation.DNS1123LabelMaxLength,
allErrs = append(allErrs, validateMap(counterSet.Counters, resource.ResourceSliceMaxCountersPerCounterSet, validation.DNS1123LabelMaxLength,
validateCounterName, validateDeviceCounter, fldPath.Child("counters"))...)
}
return allErrs
}
func gatherSharedCounterCounterNames(sharedCounters []resource.CounterSet) map[string]sets.Set[string] {
sharedCounterToCounterMap := make(map[string]sets.Set[string])
for _, counterSet := range sharedCounters {
counterNames := sets.New[string]()
for counterName := range counterSet.Counters {
counterNames.Insert(counterName)
}
sharedCounterToCounterMap[counterSet.Name] = counterNames
}
return sharedCounterToCounterMap
}
func validateResourcePool(pool resource.ResourcePool, fldPath *field.Path) field.ErrorList {
var allErrs field.ErrorList
allErrs = append(allErrs, validatePoolName(pool.Name, fldPath.Child("name"))...)
@@ -813,7 +798,7 @@ func validateResourcePool(pool resource.ResourcePool, fldPath *field.Path) field
return allErrs
}
func validateDevice(device resource.Device, oldDevice *resource.Device, fldPath *field.Path, sharedCounterToCounterNames map[string]sets.Set[string], perDeviceNodeSelection *bool) field.ErrorList {
func validateDevice(device resource.Device, oldDevice *resource.Device, fldPath *field.Path, perDeviceNodeSelection *bool) field.ErrorList {
var allErrs field.ErrorList
allowMultipleAllocations := device.AllowMultipleAllocations != nil && *device.AllowMultipleAllocations
allErrs = append(allErrs, validateDeviceName(device.Name, fldPath.Child("name"))...)
@@ -840,33 +825,11 @@ func validateDevice(device resource.Device, oldDevice *resource.Device, fldPath
fldPath.Child("taints"))...)
}
allErrs = append(allErrs, validateSet(device.ConsumesCounters, -1,
allErrs = append(allErrs, validateSet(device.ConsumesCounters, resource.ResourceSliceMaxDeviceCounterConsumptionsPerDevice,
validateDeviceCounterConsumption,
func(deviceCapacityConsumption resource.DeviceCounterConsumption) string {
return deviceCapacityConsumption.CounterSet
}, fldPath.Child("consumesCounters"))...)
var countersLength int
for _, set := range device.ConsumesCounters {
countersLength += len(set.Counters)
}
if countersLength > resource.ResourceSliceMaxCountersPerDevice {
allErrs = append(allErrs, field.Invalid(fldPath, countersLength, fmt.Sprintf("the total number of counters must not exceed %d", resource.ResourceSliceMaxCountersPerDevice)))
}
for i, deviceCounterConsumption := range device.ConsumesCounters {
if capacityNames, exists := sharedCounterToCounterNames[deviceCounterConsumption.CounterSet]; exists {
for capacityName := range deviceCounterConsumption.Counters {
if !capacityNames.Has(string(capacityName)) {
allErrs = append(allErrs, field.Invalid(fldPath.Child("consumesCounters").Index(i).Child("counters"),
capacityName, "must reference a counter defined in the ResourceSlice sharedCounters"))
}
}
} else {
allErrs = append(allErrs, field.Invalid(fldPath.Child("consumesCounters").Index(i).Child("counterSet"),
deviceCounterConsumption.CounterSet, "must reference a counterSet defined in the ResourceSlice sharedCounters"))
}
}
}, fldPath.Child("consumesCounters"), sizeCovered, uniquenessCovered)...)
if perDeviceNodeSelection != nil && *perDeviceNodeSelection {
setFields := make([]string, 0, 3)
@@ -909,14 +872,15 @@ func validateDeviceCounterConsumption(deviceCounterConsumption resource.DeviceCo
var allErrs field.ErrorList
if len(deviceCounterConsumption.CounterSet) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("counterSet"), ""))
allErrs = append(allErrs, field.Required(fldPath.Child("counterSet"), "").MarkCoveredByDeclarative())
} else {
allErrs = append(allErrs, validateCounterName(deviceCounterConsumption.CounterSet, fldPath.Child("counterSet"))...).MarkCoveredByDeclarative()
}
if len(deviceCounterConsumption.Counters) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("counters"), ""))
} else {
// The size limit is enforced for the entire device.
allErrs = append(allErrs, validateMap(deviceCounterConsumption.Counters, -1, validation.DNS1123LabelMaxLength,
validateCounterName, validateDeviceCounter, fldPath.Child("counters"))...)
allErrs = append(allErrs, validateMap(deviceCounterConsumption.Counters, resource.ResourceSliceMaxCountersPerDeviceCounterConsumption,
validation.DNS1123LabelMaxLength, validateCounterName, validateDeviceCounter, fldPath.Child("counters"))...)
}
return allErrs
}

View File

@@ -55,6 +55,14 @@ func testCounters() map[string]resourceapi.Counter {
}
}
func testMultipleCounters(count int) map[string]resourceapi.Counter {
counters := make(map[string]resourceapi.Counter)
for i := 0; i < count; i++ {
counters[fmt.Sprintf("memory-%d", i)] = resourceapi.Counter{Value: resource.MustParse("1Gi")}
}
return counters
}
func testResourceSlice(name, nodeName, driverName string, numDevices int) *resourceapi.ResourceSlice {
slice := &resourceapi.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
@@ -80,6 +88,19 @@ func testResourceSlice(name, nodeName, driverName string, numDevices int) *resou
return slice
}
func testResourceSliceWithSharedCounters(name, poolName, driverName string, numCounterSets int) *resourceapi.ResourceSlice {
slice := testResourceSlice(name, poolName, driverName, 1)
slice.Spec.Devices = nil
for i := 0; i < numCounterSets; i++ {
counterSet := resourceapi.CounterSet{
Name: fmt.Sprintf("counterset-%d", i),
Counters: testCounters(),
}
slice.Spec.SharedCounters = append(slice.Spec.SharedCounters, counterSet)
}
return slice
}
func testResourceSliceWithBindingConditions(name, nodeName, driverName string, numDevices int, bindingConditions, bindingFailureConditions []string) *resourceapi.ResourceSlice {
slice := testResourceSlice(name, nodeName, driverName, numDevices)
for i := range slice.Spec.Devices {
@@ -110,7 +131,7 @@ func TestValidateResourceSlice(t *testing.T) {
},
"good-taints": {
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithTaints)
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters)
for i := range slice.Spec.Devices {
slice.Spec.Devices[i].Taints = []resourceapi.DeviceTaint{{Key: "example.com/taint", Effect: resourceapi.DeviceTaintEffectNoExecute}}
}
@@ -118,9 +139,9 @@ func TestValidateResourceSlice(t *testing.T) {
}(),
},
"too-large-taints": {
wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDevicesWithTaints+1, resourceapi.ResourceSliceMaxDevicesWithTaints)},
wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters+1, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters)},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithTaints+1)
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters+1)
for i := range slice.Spec.Devices {
slice.Spec.Devices[i].Taints = []resourceapi.DeviceTaint{{Key: "example.com/taint", Effect: resourceapi.DeviceTaintEffectNoExecute}}
}
@@ -342,6 +363,21 @@ func TestValidateResourceSlice(t *testing.T) {
wantFailures: field.ErrorList{field.Invalid(field.NewPath("spec", "driver"), badName, "a lowercase RFC 1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')")},
slice: testResourceSlice(goodName, goodName, badName, 1),
},
"both-shared-counters-and-devices": {
wantFailures: field.ErrorList{
field.Invalid(field.NewPath("spec"), "", "only one of `sharedCounters` or `devices` is allowed"),
},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, driverName, 1)
slice.Spec.SharedCounters = []resourceapi.CounterSet{
{
Name: "counterset",
Counters: testCounters(),
},
}
return slice
}(),
},
"bad-devices": {
wantFailures: field.ErrorList{
field.Invalid(field.NewPath("spec", "devices").Index(1).Child("name"), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')"),
@@ -665,16 +701,44 @@ func TestValidateResourceSlice(t *testing.T) {
return slice
}(),
},
"missing-name-shared-counters": {
wantFailures: field.ErrorList{
field.Required(field.NewPath("spec", "sharedCounters").Index(0).Child("name"), "").MarkCoveredByDeclarative(),
},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 1)
slice.Spec.SharedCounters = []resourceapi.CounterSet{
{
Counters: testCounters(),
},
}
return slice
}(),
},
"bad-name-shared-counters": {
wantFailures: field.ErrorList{
field.Invalid(field.NewPath("spec", "sharedCounters").Index(0).Child("name"), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')"),
field.Invalid(field.NewPath("spec", "sharedCounters").Index(0).Child("name"), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')").MarkCoveredByDeclarative(),
},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 0)
slice.Spec.SharedCounters = []resourceapi.CounterSet{
{
Name: badName,
Counters: testCounters(),
},
}
return slice
}(),
},
"missing-counter-shared-counters": {
wantFailures: field.ErrorList{
field.Required(field.NewPath("spec", "sharedCounters").Index(0).Child("counters"), ""),
},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, driverName, 1)
slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 0)
slice.Spec.SharedCounters = []resourceapi.CounterSet{
{
Name: badName,
Name: goodName,
},
}
return slice
@@ -685,7 +749,7 @@ func TestValidateResourceSlice(t *testing.T) {
field.Invalid(field.NewPath("spec", "sharedCounters").Index(0).Child("counters").Key(badName), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')"),
},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, driverName, 1)
slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 1)
slice.Spec.SharedCounters = []resourceapi.CounterSet{
{
Name: goodName,
@@ -697,26 +761,12 @@ func TestValidateResourceSlice(t *testing.T) {
return slice
}(),
},
"missing-name-shared-counters": {
wantFailures: field.ErrorList{
field.Required(field.NewPath("spec", "sharedCounters").Index(0).Child("name"), ""),
},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, driverName, 1)
slice.Spec.SharedCounters = []resourceapi.CounterSet{
{
Counters: testCounters(),
},
}
return slice
}(),
},
"duplicate-shared-counters": {
wantFailures: field.ErrorList{
field.Duplicate(field.NewPath("spec", "sharedCounters").Index(1), goodName),
field.Duplicate(field.NewPath("spec", "sharedCounters").Index(1), goodName).MarkCoveredByDeclarative(),
},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, driverName, 1)
slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 1)
slice.Spec.SharedCounters = []resourceapi.CounterSet{
{
Name: goodName,
@@ -730,24 +780,46 @@ func TestValidateResourceSlice(t *testing.T) {
return slice
}(),
},
"too-large-shared-counters": {
wantFailures: field.ErrorList{
field.Invalid(field.NewPath("spec", "sharedCounters"), resourceapi.ResourceSliceMaxSharedCounters+1, fmt.Sprintf("the total number of shared counters must not exceed %d", resourceapi.ResourceSliceMaxSharedCounters)),
},
"max-shared-counters": {
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, driverName, 1)
slice.Spec.SharedCounters = createSharedCounters(resourceapi.ResourceSliceMaxSharedCounters + 1)
slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 1)
slice.Spec.SharedCounters = createSharedCounters(resourceapi.ResourceSliceMaxCounterSets)
return slice
}(),
},
"missing-counterset-consumes-counter": {
"too-many-shared-counters": {
wantFailures: field.ErrorList{
field.Required(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counterSet"), ""),
field.Invalid(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counterSet"), "", "must reference a counterSet defined in the ResourceSlice sharedCounters"),
field.TooMany(field.NewPath("spec", "sharedCounters"), resourceapi.ResourceSliceMaxCounterSets+1, resourceapi.ResourceSliceMaxCounterSets).MarkCoveredByDeclarative(),
},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 1)
slice.Spec.SharedCounters = createSharedCounters(resourceapi.ResourceSliceMaxCounterSets + 1)
return slice
}(),
},
"max-counters-in-counter-set": {
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 1)
slice.Spec.SharedCounters[0].Counters = testMultipleCounters(resourceapi.ResourceSliceMaxCountersPerCounterSet)
return slice
}(),
},
"too-many-counters-in-counter-set": {
wantFailures: field.ErrorList{
field.TooMany(field.NewPath("spec", "sharedCounters").Index(0).Child("counters"), resourceapi.ResourceSliceMaxCountersPerCounterSet+1, resourceapi.ResourceSliceMaxCountersPerCounterSet),
},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSliceWithSharedCounters(goodName, goodName, driverName, 1)
slice.Spec.SharedCounters[0].Counters = testMultipleCounters(resourceapi.ResourceSliceMaxCountersPerCounterSet + 1)
return slice
}(),
},
"missing-name-counterset-consumes-counter": {
wantFailures: field.ErrorList{
field.Required(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counterSet"), "").MarkCoveredByDeclarative(),
},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, driverName, 1)
slice.Spec.SharedCounters = createSharedCounters(1)
slice.Spec.Devices[0].ConsumesCounters = []resourceapi.DeviceCounterConsumption{
{
Counters: testCounters(),
@@ -756,13 +828,45 @@ func TestValidateResourceSlice(t *testing.T) {
return slice
}(),
},
"bad-name-counterset-consumes-counter": {
wantFailures: field.ErrorList{
field.Invalid(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counterSet"), badName, "a lowercase RFC 1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character (e.g. 'my-name', or '123-abc', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?')").MarkCoveredByDeclarative(),
},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, driverName, 1)
slice.Spec.Devices[0].ConsumesCounters = []resourceapi.DeviceCounterConsumption{
{
CounterSet: badName,
Counters: testCounters(),
},
}
return slice
}(),
},
"duplicate-counterset-consumes-counter": {
wantFailures: field.ErrorList{
field.Duplicate(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(1), goodName).MarkCoveredByDeclarative()},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, driverName, 1)
slice.Spec.Devices[0].ConsumesCounters = []resourceapi.DeviceCounterConsumption{
{
CounterSet: goodName,
Counters: testCounters(),
},
{
CounterSet: goodName,
Counters: testCounters(),
},
}
return slice
}(),
},
"missing-counter-consumes-counter": {
wantFailures: field.ErrorList{
field.Required(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counters"), ""),
},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, driverName, 1)
slice.Spec.SharedCounters = createSharedCounters(1)
slice.Spec.Devices[0].ConsumesCounters = []resourceapi.DeviceCounterConsumption{
{
CounterSet: "counterset-0",
@@ -771,65 +875,82 @@ func TestValidateResourceSlice(t *testing.T) {
return slice
}(),
},
"wrong-counterref-consumes-counter": {
wantFailures: field.ErrorList{
field.Invalid(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counters"), "fake", "must reference a counter defined in the ResourceSlice sharedCounters"),
},
"max-device-counter-consumption-for-device": {
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, driverName, 1)
slice.Spec.SharedCounters = createSharedCounters(1)
slice.Spec.Devices[0].ConsumesCounters = []resourceapi.DeviceCounterConsumption{
{
Counters: map[string]resourceapi.Counter{
"fake": {Value: resource.MustParse("1Gi")},
},
CounterSet: "counterset-0",
},
}
return slice
}(),
},
"wrong-sharedcounterref-consumes-counter": {
wantFailures: field.ErrorList{
field.Invalid(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counterSet"), "fake", "must reference a counterSet defined in the ResourceSlice sharedCounters"),
},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, driverName, 1)
slice.Spec.SharedCounters = createSharedCounters(1)
slice.Spec.Devices[0].ConsumesCounters = []resourceapi.DeviceCounterConsumption{
{
Counters: testCounters(),
CounterSet: "fake",
},
}
return slice
}(),
},
"too-large-consumes-counter": {
wantFailures: field.ErrorList{
field.Invalid(field.NewPath("spec", "devices").Index(0), resourceapi.ResourceSliceMaxCountersPerDevice+1, fmt.Sprintf("the total number of counters must not exceed %d", resourceapi.ResourceSliceMaxCountersPerDevice)),
field.Invalid(field.NewPath("spec", "sharedCounters"), resourceapi.ResourceSliceMaxSharedCounters+1, fmt.Sprintf("the total number of shared counters must not exceed %d", resourceapi.ResourceSliceMaxSharedCounters)),
},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, driverName, 1)
slice.Spec.SharedCounters = createSharedCounters(resourceapi.ResourceSliceMaxSharedCounters + 1)
slice.Spec.Devices[0].Attributes = nil
slice.Spec.Devices[0].Capacity = nil
slice.Spec.Devices[0].ConsumesCounters = createConsumesCounters(resourceapi.ResourceSliceMaxCountersPerDevice + 1)
slice.Spec.Devices[0].ConsumesCounters = createConsumesCounters(resourceapi.ResourceSliceMaxDeviceCounterConsumptionsPerDevice)
return slice
}(),
},
"too-many-device-counters-in-slice": {
"too-many-device-counter-consumption-for-device": {
wantFailures: field.ErrorList{
field.Invalid(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDeviceCountersPerSlice+1, fmt.Sprintf("the total number of counters in devices must not exceed %d", resourceapi.ResourceSliceMaxDeviceCountersPerSlice)),
field.TooMany(field.NewPath("spec", "devices").Index(0).Child("consumesCounters"), resourceapi.ResourceSliceMaxDeviceCounterConsumptionsPerDevice+1, resourceapi.ResourceSliceMaxDeviceCounterConsumptionsPerDevice).MarkCoveredByDeclarative(),
},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, driverName, 65)
slice.Spec.SharedCounters = createSharedCounters(16)
for i := 0; i < 64; i++ {
slice.Spec.Devices[i].ConsumesCounters = createConsumesCounters(16)
slice := testResourceSlice(goodName, goodName, driverName, 1)
slice.Spec.Devices[0].Attributes = nil
slice.Spec.Devices[0].Capacity = nil
slice.Spec.Devices[0].ConsumesCounters = createConsumesCounters(resourceapi.ResourceSliceMaxDeviceCounterConsumptionsPerDevice + 1)
return slice
}(),
},
"max-counters-in-device-counter-consumption": {
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, driverName, 1)
slice.Spec.Devices[0].Attributes = nil
slice.Spec.Devices[0].Capacity = nil
slice.Spec.Devices[0].ConsumesCounters = []resourceapi.DeviceCounterConsumption{
{
CounterSet: "counterset-0",
Counters: testMultipleCounters(resourceapi.ResourceSliceMaxCountersPerDeviceCounterConsumption),
},
}
return slice
}(),
},
"too-many-counters-in-device-counter-consumption": {
wantFailures: field.ErrorList{
field.TooMany(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counters"), resourceapi.ResourceSliceMaxCountersPerDeviceCounterConsumption+1, resourceapi.ResourceSliceMaxCountersPerDeviceCounterConsumption),
},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, driverName, 1)
slice.Spec.Devices[0].Attributes = nil
slice.Spec.Devices[0].Capacity = nil
slice.Spec.Devices[0].ConsumesCounters = []resourceapi.DeviceCounterConsumption{
{
CounterSet: "counterset-0",
Counters: testMultipleCounters(resourceapi.ResourceSliceMaxCountersPerDeviceCounterConsumption + 1),
},
}
return slice
}(),
},
"max-number-of-devices-with-consumes-counters": {
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters)
for i := range slice.Spec.Devices {
slice.Spec.Devices[i].ConsumesCounters = []resourceapi.DeviceCounterConsumption{
{
CounterSet: "counterset-0",
Counters: testCounters(),
},
}
}
return slice
}(),
},
"too-many-devices-with-consumes-counters": {
wantFailures: field.ErrorList{field.TooMany(field.NewPath("spec", "devices"), resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters+1, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters)},
slice: func() *resourceapi.ResourceSlice {
slice := testResourceSlice(goodName, goodName, goodName, resourceapi.ResourceSliceMaxDevicesWithTaintsOrConsumesCounters+1)
slice.Spec.Devices[0].ConsumesCounters = []resourceapi.DeviceCounterConsumption{
{
CounterSet: "counterset-0",
Counters: testCounters(),
},
}
slice.Spec.Devices[64].ConsumesCounters = createConsumesCounters(1)
return slice
}(),
},

View File

@@ -48,43 +48,43 @@ func TestDeclarativeValidate(t *testing.T) {
expectedErrs field.ErrorList
}{
"valid": {
input: mkResourceSlice(),
input: mkResourceSliceWithDevices(),
},
// spec.devices[%d].bindingConditions
"valid: one binding condition": {
input: mkResourceSlice(tweakBindingConditions(1)),
input: mkResourceSliceWithDevices(tweakBindingConditions(1)),
},
"valid: at limit binding conditions": {
input: mkResourceSlice(tweakBindingConditions(resource.BindingConditionsMaxSize)),
input: mkResourceSliceWithDevices(tweakBindingConditions(resource.BindingConditionsMaxSize)),
},
"invalid: too many binding conditions": {
input: mkResourceSlice(tweakBindingConditions(resource.BindingConditionsMaxSize + 1)),
input: mkResourceSliceWithDevices(tweakBindingConditions(resource.BindingConditionsMaxSize + 1)),
expectedErrs: field.ErrorList{
field.TooMany(field.NewPath("spec", "devices").Index(0).Child("bindingConditions"), resource.BindingConditionsMaxSize+1, resource.BindingConditionsMaxSize).WithOrigin("maxItems"),
},
},
// spec.devices[%d].bindingFailureConditions
"valid: one binding failure conditions": {
input: mkResourceSlice(tweakBindingFailureConditions(1)),
input: mkResourceSliceWithDevices(tweakBindingFailureConditions(1)),
},
"valid: at limit binding failure conditions": {
input: mkResourceSlice(tweakBindingFailureConditions(resource.BindingFailureConditionsMaxSize)),
input: mkResourceSliceWithDevices(tweakBindingFailureConditions(resource.BindingFailureConditionsMaxSize)),
},
"invalid: too many binding failure conditions": {
input: mkResourceSlice(tweakBindingFailureConditions(resource.BindingFailureConditionsMaxSize + 1)),
input: mkResourceSliceWithDevices(tweakBindingFailureConditions(resource.BindingFailureConditionsMaxSize + 1)),
expectedErrs: field.ErrorList{
field.TooMany(field.NewPath("spec", "devices").Index(0).Child("bindingFailureConditions"), resource.BindingFailureConditionsMaxSize+1, resource.BindingFailureConditionsMaxSize).WithOrigin("maxItems"),
},
},
// spec.Devices[%d].Taints[%d].Effect
"valid: taint NoSchedule": {
input: mkResourceSlice(tweakDeviceTaintEffect(string(resource.DeviceTaintEffectNoSchedule))),
input: mkResourceSliceWithDevices(tweakDeviceTaintEffect(string(resource.DeviceTaintEffectNoSchedule))),
},
"valid: taint NoExecute": {
input: mkResourceSlice(tweakDeviceTaintEffect(string(resource.DeviceTaintEffectNoExecute))),
input: mkResourceSliceWithDevices(tweakDeviceTaintEffect(string(resource.DeviceTaintEffectNoExecute))),
},
"invalid: taint Invalid": {
input: mkResourceSlice(tweakDeviceTaintEffect("Invalid")),
input: mkResourceSliceWithDevices(tweakDeviceTaintEffect("Invalid")),
expectedErrs: field.ErrorList{
field.NotSupported(
field.NewPath("spec", "devices").Index(0).Child("taints").Index(0).Child("effect"),
@@ -92,36 +92,108 @@ func TestDeclarativeValidate(t *testing.T) {
},
},
"invalid: taint empty": {
input: mkResourceSlice(tweakDeviceTaintEffect("")),
input: mkResourceSliceWithDevices(tweakDeviceTaintEffect("")),
expectedErrs: field.ErrorList{
field.Required(field.NewPath("spec", "devices").Index(0).Child("taints").Index(0).Child("effect"), ""),
},
},
// spec.Devices[%].attribute
"valid: device attribute int": {
input: mkResourceSlice(tweakDeviceAttribute("test.io/int", resource.DeviceAttribute{IntValue: ptr.To[int64](123)})),
input: mkResourceSliceWithDevices(tweakDeviceAttribute("test.io/int", resource.DeviceAttribute{IntValue: ptr.To[int64](123)})),
},
"valid: device attribute bool": {
input: mkResourceSlice(tweakDeviceAttribute("test.io/bool", resource.DeviceAttribute{BoolValue: ptr.To(true)})),
input: mkResourceSliceWithDevices(tweakDeviceAttribute("test.io/bool", resource.DeviceAttribute{BoolValue: ptr.To(true)})),
},
"valid: device attribute string": {
input: mkResourceSlice(tweakDeviceAttribute("test.io/string", resource.DeviceAttribute{StringValue: ptr.To("value")})),
input: mkResourceSliceWithDevices(tweakDeviceAttribute("test.io/string", resource.DeviceAttribute{StringValue: ptr.To("value")})),
},
"valid: device attribute version": {
input: mkResourceSlice(tweakDeviceAttribute("test.io/version", resource.DeviceAttribute{VersionValue: ptr.To("1.2.3")})),
input: mkResourceSliceWithDevices(tweakDeviceAttribute("test.io/version", resource.DeviceAttribute{VersionValue: ptr.To("1.2.3")})),
},
"invalid: device attribute with multiple values": {
input: mkResourceSlice(tweakDeviceAttribute("test.io/multiple", resource.DeviceAttribute{IntValue: ptr.To[int64](123), BoolValue: ptr.To(true)})),
input: mkResourceSliceWithDevices(tweakDeviceAttribute("test.io/multiple", resource.DeviceAttribute{IntValue: ptr.To[int64](123), BoolValue: ptr.To(true)})),
expectedErrs: field.ErrorList{
field.Invalid(field.NewPath("spec", "devices").Index(0).Child("attributes").Key("test.io/multiple"), "", ""),
},
},
"invalid: device attribute no value": {
input: mkResourceSlice(tweakDeviceAttribute("test.io/multiple", resource.DeviceAttribute{})),
input: mkResourceSliceWithDevices(tweakDeviceAttribute("test.io/multiple", resource.DeviceAttribute{})),
expectedErrs: field.ErrorList{
field.Invalid(field.NewPath("spec", "devices").Index(0).Child("attributes").Key("test.io/multiple"), "", ""),
},
},
// spec.sharedCounters
"valid: at limit shared counters": {
input: mkResourceSliceWithSharedCounters(tweakSharedCounters(resource.ResourceSliceMaxCounterSets)),
},
"invalid: too many shared counters": {
input: mkResourceSliceWithSharedCounters(tweakSharedCounters(resource.ResourceSliceMaxCounterSets + 1)),
expectedErrs: field.ErrorList{
field.TooMany(field.NewPath("spec").Child("sharedCounters"), resource.ResourceSliceMaxCounterSets+1, resource.ResourceSliceMaxCounterSets).WithOrigin("maxItems"),
},
},
// spec.devices.consumesCounters
"valid: at limit device consumes counters": {
input: mkResourceSliceWithDevices(tweakDeviceConsumesCounters(resource.ResourceSliceMaxDeviceCounterConsumptionsPerDevice)),
},
"invalid: too many device consumes counters": {
input: mkResourceSliceWithDevices(tweakDeviceConsumesCounters(resource.ResourceSliceMaxDeviceCounterConsumptionsPerDevice + 1)),
expectedErrs: field.ErrorList{
field.TooMany(field.NewPath("spec", "devices").Index(0).Child("consumesCounters"), resource.ResourceSliceMaxDeviceCounterConsumptionsPerDevice+1, resource.ResourceSliceMaxDeviceCounterConsumptionsPerDevice).WithOrigin("maxItems"),
},
},
// spec.sharedCounters.name
"valid: counter set name": {
input: mkResourceSliceWithSharedCounters(tweakSharedCountersName("valid-key")),
},
"invalid: counter set name": {
input: mkResourceSliceWithSharedCounters(tweakSharedCountersName("InvalidKey")),
expectedErrs: field.ErrorList{
field.Invalid(field.NewPath("spec", "sharedCounters").Index(0).Child("name"), "InvalidKey", "").WithOrigin("format=k8s-short-name"),
},
},
"invalid: counter set name not set": {
input: mkResourceSliceWithSharedCounters(tweakSharedCountersName("")),
expectedErrs: field.ErrorList{
field.Required(field.NewPath("spec", "sharedCounters").Index(0).Child("name"), ""),
},
},
// spec.devices.consumesCounters.counterSet
"valid: device consumes counters counter set name": {
input: mkResourceSliceWithDevices(tweakDeviceConsumesCountersCounterSetName("valid-key")),
},
"invalid: device consumes counters counter set name": {
input: mkResourceSliceWithDevices(tweakDeviceConsumesCountersCounterSetName("InvalidKey")),
expectedErrs: field.ErrorList{
field.Invalid(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counterSet"), "InvalidKey", "").WithOrigin("format=k8s-short-name"),
},
},
"invalid: device consumes counters counter set name not set": {
input: mkResourceSliceWithDevices(tweakDeviceConsumesCountersCounterSetName("")),
expectedErrs: field.ErrorList{
field.Required(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counterSet"), ""),
},
},
// spec.sharedCounters
"valid: distinct names for shared counters": {
input: mkResourceSliceWithSharedCounters(tweakSharedCountersName("valid-key-1", "valid-key-2")),
},
"invalid: duplicate names for shared counters": {
input: mkResourceSliceWithSharedCounters(tweakSharedCountersName("duplicate-key", "duplicate-key")),
expectedErrs: field.ErrorList{
field.Duplicate(field.NewPath("spec").Child("sharedCounters").Index(1), "duplicate-key"),
},
},
// spec.devices.consumesCounters
"valid: distinct names for counter set in device counter consumption": {
input: mkResourceSliceWithDevices(tweakDeviceConsumesCountersCounterSetName("valid-key-1", "valid-key-2")),
},
"invalid: duplicate names for counter set in device counter consumption": {
input: mkResourceSliceWithDevices(tweakDeviceConsumesCountersCounterSetName("duplicate-key", "duplicate-key")),
expectedErrs: field.ErrorList{
field.Duplicate(field.NewPath("spec").Child("devices").Index(0).Child("consumesCounters").Index(1), "duplicate-key"),
},
},
// TODO: Add more test cases
}
@@ -151,67 +223,153 @@ func TestDeclarativeValidateUpdate(t *testing.T) {
expectedErrs field.ErrorList
}{
"valid no changes": {
old: mkResourceSlice(),
update: mkResourceSlice(),
old: mkResourceSliceWithDevices(),
update: mkResourceSliceWithDevices(),
},
// spec.devices[%d].bindingConditions
"valid update: at limit binding conditions": {
old: mkResourceSlice(),
update: mkResourceSlice(tweakBindingConditions(resource.BindingConditionsMaxSize)),
old: mkResourceSliceWithDevices(),
update: mkResourceSliceWithDevices(tweakBindingConditions(resource.BindingConditionsMaxSize)),
},
"invalid update:update with too many binding conditions": {
old: mkResourceSlice(),
update: mkResourceSlice(tweakBindingConditions(resource.BindingConditionsMaxSize + 1)),
old: mkResourceSliceWithDevices(),
update: mkResourceSliceWithDevices(tweakBindingConditions(resource.BindingConditionsMaxSize + 1)),
expectedErrs: field.ErrorList{
field.TooMany(field.NewPath("spec", "devices").Index(0).Child("bindingConditions"), resource.BindingConditionsMaxSize+1, resource.BindingConditionsMaxSize).WithOrigin("maxItems"),
},
},
// spec.devices[%d].bindingFailureConditions
"valid update: at limit binding failure conditions": {
old: mkResourceSlice(),
update: mkResourceSlice(tweakBindingFailureConditions(resource.BindingFailureConditionsMaxSize)),
old: mkResourceSliceWithDevices(),
update: mkResourceSliceWithDevices(tweakBindingFailureConditions(resource.BindingFailureConditionsMaxSize)),
},
"update with too many binding failure conditions": {
old: mkResourceSlice(),
update: mkResourceSlice(tweakBindingFailureConditions(resource.BindingFailureConditionsMaxSize + 1)),
old: mkResourceSliceWithDevices(),
update: mkResourceSliceWithDevices(tweakBindingFailureConditions(resource.BindingFailureConditionsMaxSize + 1)),
expectedErrs: field.ErrorList{
field.TooMany(field.NewPath("spec", "devices").Index(0).Child("bindingFailureConditions"), resource.BindingFailureConditionsMaxSize+1, resource.BindingFailureConditionsMaxSize).WithOrigin("maxItems"),
},
},
// spec.devices.taints.effect
"valid update: NoSchedule taint effect": {
old: mkResourceSlice(),
update: mkResourceSlice(tweakDeviceTaintEffect(string(resource.DeviceTaintEffectNoSchedule))),
old: mkResourceSliceWithDevices(),
update: mkResourceSliceWithDevices(tweakDeviceTaintEffect(string(resource.DeviceTaintEffectNoSchedule))),
},
"valid update: NoExecute taint effect": {
old: mkResourceSlice(),
update: mkResourceSlice(tweakDeviceTaintEffect(string(resource.DeviceTaintEffectNoExecute))),
old: mkResourceSliceWithDevices(),
update: mkResourceSliceWithDevices(tweakDeviceTaintEffect(string(resource.DeviceTaintEffectNoExecute))),
},
"invalid update: unsupported taint effect": {
old: mkResourceSlice(),
update: mkResourceSlice(tweakDeviceTaintEffect("InvalidEffect")),
old: mkResourceSliceWithDevices(),
update: mkResourceSliceWithDevices(tweakDeviceTaintEffect("InvalidEffect")),
expectedErrs: field.ErrorList{
field.NotSupported(field.NewPath("spec", "devices").Index(0).Child("taints").Index(0).Child("effect"), "InvalidEffect", []string{string(resource.DeviceTaintEffectNoSchedule), string(resource.DeviceTaintEffectNoExecute)}),
},
},
"invalid update: empty taint effect": {
old: mkResourceSlice(),
update: mkResourceSlice(tweakDeviceTaintEffect("")),
old: mkResourceSliceWithDevices(),
update: mkResourceSliceWithDevices(tweakDeviceTaintEffect("")),
expectedErrs: field.ErrorList{
field.Required(field.NewPath("spec", "devices").Index(0).Child("taints").Index(0).Child("effect"), ""),
},
},
"valid update: device attribute": {
old: mkResourceSlice(tweakDeviceAttribute("test.io/int", resource.DeviceAttribute{IntValue: ptr.To[int64](123)})),
update: mkResourceSlice(tweakDeviceAttribute("test.io/int", resource.DeviceAttribute{BoolValue: ptr.To(true)})),
old: mkResourceSliceWithDevices(tweakDeviceAttribute("test.io/int", resource.DeviceAttribute{IntValue: ptr.To[int64](123)})),
update: mkResourceSliceWithDevices(tweakDeviceAttribute("test.io/int", resource.DeviceAttribute{BoolValue: ptr.To(true)})),
},
"invalid update: device attribute with multiple values": {
old: mkResourceSlice(),
update: mkResourceSlice(tweakDeviceAttribute("test.io/multiple", resource.DeviceAttribute{IntValue: ptr.To[int64](123), BoolValue: ptr.To(true)})),
old: mkResourceSliceWithDevices(),
update: mkResourceSliceWithDevices(tweakDeviceAttribute("test.io/multiple", resource.DeviceAttribute{IntValue: ptr.To[int64](123), BoolValue: ptr.To(true)})),
expectedErrs: field.ErrorList{
field.Invalid(field.NewPath("spec", "devices").Index(0).Child("attributes").Key("test.io/multiple"), "", "may have only one of the following fields set: bool, int, string, version"),
},
},
// spec.sharedCounters
"valid update: at limit shared counters": {
old: mkResourceSliceWithSharedCounters(),
update: mkResourceSliceWithSharedCounters(tweakSharedCounters(resource.ResourceSliceMaxCounterSets)),
},
"invalid update: too many shared counters": {
old: mkResourceSliceWithSharedCounters(),
update: mkResourceSliceWithSharedCounters(tweakSharedCounters(resource.ResourceSliceMaxCounterSets + 1)),
expectedErrs: field.ErrorList{
field.TooMany(field.NewPath("spec").Child("sharedCounters"), resource.ResourceSliceMaxCounterSets+1, resource.ResourceSliceMaxCounterSets).WithOrigin("maxItems"),
},
},
// spec.devices.consumesCounters
"valid update: at limit device consumes counters": {
old: mkResourceSliceWithDevices(),
update: mkResourceSliceWithDevices(tweakDeviceConsumesCounters(resource.ResourceSliceMaxDeviceCounterConsumptionsPerDevice)),
},
"invalid update: too many device consumes counters": {
old: mkResourceSliceWithDevices(),
update: mkResourceSliceWithDevices(tweakDeviceConsumesCounters(resource.ResourceSliceMaxDeviceCounterConsumptionsPerDevice + 1)),
expectedErrs: field.ErrorList{
field.TooMany(field.NewPath("spec", "devices").Index(0).Child("consumesCounters"), resource.ResourceSliceMaxDeviceCounterConsumptionsPerDevice+1, resource.ResourceSliceMaxDeviceCounterConsumptionsPerDevice).WithOrigin("maxItems"),
},
},
// spec.sharedCounters.name
"valid update: counter set name": {
old: mkResourceSliceWithSharedCounters(),
update: mkResourceSliceWithSharedCounters(tweakSharedCountersName("valid-key")),
},
"invalid update: counter set name": {
old: mkResourceSliceWithSharedCounters(),
update: mkResourceSliceWithSharedCounters(tweakSharedCountersName("InvalidKey")),
expectedErrs: field.ErrorList{
field.Invalid(field.NewPath("spec", "sharedCounters").Index(0).Child("name"), "InvalidKey", "").WithOrigin("format=k8s-short-name"),
},
},
"invalid update: counter set name not set": {
old: mkResourceSliceWithSharedCounters(),
update: mkResourceSliceWithSharedCounters(tweakSharedCountersName("")),
expectedErrs: field.ErrorList{
field.Required(field.NewPath("spec", "sharedCounters").Index(0).Child("name"), ""),
},
},
// spec.devices.consumesCounters.counterSet
"valid update: device consumes counters counter set name": {
old: mkResourceSliceWithDevices(),
update: mkResourceSliceWithDevices(tweakDeviceConsumesCountersCounterSetName("valid-key")),
},
"invalid update: device consumes counters counter set name": {
old: mkResourceSliceWithDevices(),
update: mkResourceSliceWithDevices(tweakDeviceConsumesCountersCounterSetName("InvalidKey")),
expectedErrs: field.ErrorList{
field.Invalid(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counterSet"), "InvalidKey", "").WithOrigin("format=k8s-short-name"),
},
},
"invalidupdate: device consumes counters counter set name not set": {
old: mkResourceSliceWithDevices(),
update: mkResourceSliceWithDevices(tweakDeviceConsumesCountersCounterSetName("")),
expectedErrs: field.ErrorList{
field.Required(field.NewPath("spec", "devices").Index(0).Child("consumesCounters").Index(0).Child("counterSet"), ""),
},
},
// spec.sharedCounters
"valid update: distinct names for shared counters": {
old: mkResourceSliceWithSharedCounters(),
update: mkResourceSliceWithSharedCounters(tweakSharedCountersName("valid-key-1", "valid-key-2")),
},
"invalid update: duplicate names for shared counters": {
old: mkResourceSliceWithSharedCounters(),
update: mkResourceSliceWithSharedCounters(tweakSharedCountersName("duplicate-key", "duplicate-key")),
expectedErrs: field.ErrorList{
field.Duplicate(field.NewPath("spec").Child("sharedCounters").Index(1), "duplicate-key"),
},
},
// spec.devices.consumesCounters
"valid update: distinct names for counter set in device counter consumption": {
old: mkResourceSliceWithDevices(),
update: mkResourceSliceWithDevices(tweakDeviceConsumesCountersCounterSetName("valid-key-1", "valid-key-2")),
},
"invalid update: duplicate names for counter set in device counter consumption": {
old: mkResourceSliceWithDevices(),
update: mkResourceSliceWithDevices(tweakDeviceConsumesCountersCounterSetName("duplicate-key", "duplicate-key")),
expectedErrs: field.ErrorList{
field.Duplicate(field.NewPath("spec").Child("devices").Index(0).Child("consumesCounters").Index(1), "duplicate-key"),
},
},
}
for k, tc := range testCases {
t.Run(k, func(t *testing.T) {
@@ -224,7 +382,7 @@ func TestDeclarativeValidateUpdate(t *testing.T) {
}
}
func mkResourceSlice(mutators ...func(*resource.ResourceSlice)) resource.ResourceSlice {
func mkResourceSliceWithDevices(mutators ...func(*resource.ResourceSlice)) resource.ResourceSlice {
rs := resource.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "test-slice",
@@ -256,6 +414,34 @@ func mkResourceSlice(mutators ...func(*resource.ResourceSlice)) resource.Resourc
return rs
}
func mkResourceSliceWithSharedCounters(mutators ...func(*resource.ResourceSlice)) resource.ResourceSlice {
rs := resource.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "test-slice",
},
Spec: resource.ResourceSliceSpec{
Driver: "test.driver.io",
NodeName: ptr.To("test-node"),
Pool: resource.ResourcePool{
Name: "test-pool",
ResourceSliceCount: 5,
},
SharedCounters: []resource.CounterSet{
{
Name: "shared-counter-set",
Counters: map[string]resource.Counter{
"valid-key": {},
},
},
},
},
}
for _, mutate := range mutators {
mutate(&rs)
}
return rs
}
func tweakBindingFailureConditions(count int) func(*resource.ResourceSlice) {
return func(rs *resource.ResourceSlice) {
if rs.Spec.Devices[0].BindingConditions == nil {
@@ -294,3 +480,63 @@ func tweakDeviceAttribute(name resource.QualifiedName, value resource.DeviceAttr
rs.Spec.Devices[0].Attributes[name] = value
}
}
func tweakSharedCountersName(names ...string) func(*resource.ResourceSlice) {
return func(rs *resource.ResourceSlice) {
var sharedCounters []resource.CounterSet
for _, name := range names {
sharedCounters = append(sharedCounters, resource.CounterSet{
Name: name,
Counters: map[string]resource.Counter{
"valid-key": {},
},
})
}
rs.Spec.SharedCounters = sharedCounters
}
}
func tweakSharedCounters(count int) func(*resource.ResourceSlice) {
return func(rs *resource.ResourceSlice) {
var counterSets []resource.CounterSet
for i := 0; i < count; i++ {
counterSets = append(counterSets, resource.CounterSet{
Name: fmt.Sprintf("shared-counter-set-%d", i),
Counters: map[string]resource.Counter{
"valid-key": {},
},
})
}
rs.Spec.SharedCounters = counterSets
}
}
func tweakDeviceConsumesCounters(count int) func(*resource.ResourceSlice) {
return func(rs *resource.ResourceSlice) {
var consumesCounters []resource.DeviceCounterConsumption
for i := 0; i < count; i++ {
consumesCounters = append(consumesCounters, resource.DeviceCounterConsumption{
CounterSet: fmt.Sprintf("shared-counter-set-%d", i),
Counters: map[string]resource.Counter{
"valid-key": {},
},
})
}
rs.Spec.Devices[0].ConsumesCounters = consumesCounters
}
}
func tweakDeviceConsumesCountersCounterSetName(counterSets ...string) func(*resource.ResourceSlice) {
return func(rs *resource.ResourceSlice) {
var consumesCounters []resource.DeviceCounterConsumption
for _, counterSet := range counterSets {
consumesCounters = append(consumesCounters, resource.DeviceCounterConsumption{
CounterSet: counterSet,
Counters: map[string]resource.Counter{
"valid-key": {},
},
})
}
rs.Spec.Devices[0].ConsumesCounters = consumesCounters
}
}

View File

@@ -66,7 +66,7 @@ var sliceWithBindingConditions = func() *resource.ResourceSlice {
return slice
}()
var sliceWithPartitionableDevices = &resource.ResourceSlice{
var sliceWithPartitionableDevicesPerDeviceNodeSelection = &resource.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "valid-resource-slice",
},
@@ -81,16 +81,30 @@ var sliceWithPartitionableDevices = &resource.ResourceSlice{
ResourceSliceCount: 1,
Generation: 1,
},
SharedCounters: []resource.CounterSet{
Devices: []resource.Device{
{
Name: "pool-1",
Counters: map[string]resource.Counter{
"memory": {
Value: k8sresource.MustParse("40Gi"),
},
},
Name: "device",
NodeName: func() *string {
r := "valid-node-name"
return &r
}(),
},
},
},
}
var sliceWithPartitionableDevicesConsumesCounters = &resource.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "valid-resource-slice",
},
Spec: resource.ResourceSliceSpec{
NodeName: ptr.To("valid-node-name"),
Driver: "testdriver.example.com",
Pool: resource.ResourcePool{
Name: "valid-pool-name",
ResourceSliceCount: 1,
Generation: 1,
},
Devices: []resource.Device{
{
Name: "device",
@@ -104,10 +118,6 @@ var sliceWithPartitionableDevices = &resource.ResourceSlice{
},
},
},
NodeName: func() *string {
r := "valid-node-name"
return &r
}(),
Attributes: map[resource.QualifiedName]resource.DeviceAttribute{
resource.QualifiedName("version"): {
StringValue: func() *string {
@@ -126,6 +136,31 @@ var sliceWithPartitionableDevices = &resource.ResourceSlice{
},
}
var sliceWithPartitionableDevicesSharedCounters = &resource.ResourceSlice{
ObjectMeta: metav1.ObjectMeta{
Name: "valid-resource-slice",
},
Spec: resource.ResourceSliceSpec{
NodeName: ptr.To("valid-node-name"),
Driver: "testdriver.example.com",
Pool: resource.ResourcePool{
Name: "valid-pool-name",
ResourceSliceCount: 1,
Generation: 1,
},
SharedCounters: []resource.CounterSet{
{
Name: "pool-1",
Counters: map[string]resource.Counter{
"memory": {
Value: k8sresource.MustParse("40Gi"),
},
},
},
},
},
}
var sliceWithCapacity = func() *resource.ResourceSlice {
obj := slice.DeepCopy()
obj.Spec.Devices[0].Capacity = map[resource.QualifiedName]resource.DeviceCapacity{
@@ -207,26 +242,15 @@ func TestResourceSliceStrategyCreate(t *testing.T) {
return obj
}(),
},
"drop-fields-partitionable-devices": {
"drop-fields-partitionable-devices-with-consumes-counters": {
obj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevices.DeepCopy()
obj.Spec.PerDeviceNodeSelection = func() *bool {
r := false
return &r
}()
obj.Spec.NodeName = ptr.To("valid-node-name")
obj := sliceWithPartitionableDevicesConsumesCounters.DeepCopy()
return obj
}(),
partitionableDevices: false,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevices.DeepCopy()
obj := sliceWithPartitionableDevicesConsumesCounters.DeepCopy()
obj.ObjectMeta.Generation = 1
obj.Spec.SharedCounters = nil
obj.Spec.PerDeviceNodeSelection = nil
obj.Spec.NodeName = ptr.To("valid-node-name")
obj.Spec.Devices[0].NodeName = nil
obj.Spec.Devices[0].NodeSelector = nil
obj.Spec.Devices[0].AllNodes = nil
obj.Spec.Devices[0].ConsumesCounters = nil
return obj
}(),
@@ -235,15 +259,46 @@ func TestResourceSliceStrategyCreate(t *testing.T) {
// have a node selector after the perDeviceNodeSelection field got
// dropped.
"drop-fields-partitionable-devices-with-per-device-node-selection": {
obj: sliceWithPartitionableDevices,
obj: sliceWithPartitionableDevicesPerDeviceNodeSelection,
partitionableDevices: false,
expectedValidationError: true,
},
"keep-fields-partitionable-devices": {
obj: sliceWithPartitionableDevices,
"drop-fields-partitionable-devices-with-shared-counters": {
obj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesSharedCounters.DeepCopy()
return obj
}(),
partitionableDevices: false,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesSharedCounters.DeepCopy()
obj.ObjectMeta.Generation = 1
obj.Spec.SharedCounters = nil
return obj
}(),
},
"keep-fields-partitionable-devices-with-consumes-counters": {
obj: sliceWithPartitionableDevicesConsumesCounters,
partitionableDevices: true,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevices.DeepCopy()
obj := sliceWithPartitionableDevicesConsumesCounters.DeepCopy()
obj.Generation = 1
return obj
}(),
},
"keep-fields-partitionable-devices-with-per-device-node-selection": {
obj: sliceWithPartitionableDevicesPerDeviceNodeSelection,
partitionableDevices: true,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesPerDeviceNodeSelection.DeepCopy()
obj.Generation = 1
return obj
}(),
},
"keep-fields-partitionable-devices-with-shared-counters": {
obj: sliceWithPartitionableDevicesSharedCounters,
partitionableDevices: true,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesSharedCounters.DeepCopy()
obj.Generation = 1
return obj
}(),
@@ -420,86 +475,168 @@ func TestResourceSliceStrategyUpdate(t *testing.T) {
return obj
}(),
},
"drop-fields-partitionable-devices": {
"drop-fields-partitionable-devices-with-consumes-counters": {
oldObj: slice,
newObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevices.DeepCopy()
obj := sliceWithPartitionableDevicesConsumesCounters.DeepCopy()
obj.ResourceVersion = "4"
obj.Spec.PerDeviceNodeSelection = func() *bool {
r := false
return &r
}()
obj.Spec.NodeName = ptr.To("valid-node-name")
return obj
}(),
partitionableDevices: false,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevices.DeepCopy()
obj := sliceWithPartitionableDevicesConsumesCounters.DeepCopy()
obj.ResourceVersion = "4"
obj.Generation = 1
obj.Spec.SharedCounters = nil
obj.Spec.PerDeviceNodeSelection = nil
obj.Spec.NodeName = ptr.To("valid-node-name")
obj.Spec.Devices[0].ConsumesCounters = nil
obj.Spec.Devices[0].NodeName = nil
return obj
}(),
},
"drop-fields-partitionable-devices-with-per-device-node-selection": {
oldObj: slice,
newObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevices.DeepCopy()
obj := sliceWithPartitionableDevicesPerDeviceNodeSelection.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
partitionableDevices: false,
expectValidationError: true,
},
"keep-fields-partitionable-devices": {
"drop-fields-partitionable-devices-with-shared-counters": {
oldObj: slice,
newObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevices.DeepCopy()
obj.ResourceVersion = "4"
obj.Spec.NodeName = ptr.To("valid-node-name")
obj.Spec.PerDeviceNodeSelection = nil
obj.Spec.Devices[0].NodeName = nil
return obj
}(),
partitionableDevices: true,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevices.DeepCopy()
obj.ResourceVersion = "4"
obj.Generation = 1
obj.Spec.NodeName = ptr.To("valid-node-name")
obj.Spec.PerDeviceNodeSelection = nil
obj.Spec.Devices[0].NodeName = nil
return obj
}(),
},
"keep-existing-fields-partitionable-devices": {
oldObj: sliceWithPartitionableDevices,
newObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevices.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
partitionableDevices: true,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevices.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
},
"keep-existing-fields-partitionable-devices-disabled-feature": {
oldObj: sliceWithPartitionableDevices,
newObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevices.DeepCopy()
obj := sliceWithPartitionableDevicesSharedCounters.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
partitionableDevices: false,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevices.DeepCopy()
obj := sliceWithPartitionableDevicesSharedCounters.DeepCopy()
obj.ResourceVersion = "4"
obj.Generation = 1
obj.Spec.SharedCounters = nil
return obj
}(),
},
"keep-fields-partitionable-devices-with-consumes-counters": {
oldObj: slice,
newObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesConsumesCounters.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
partitionableDevices: true,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesConsumesCounters.DeepCopy()
obj.ResourceVersion = "4"
obj.Generation = 1
return obj
}(),
},
"keep-fields-partitionable-devices-with-per-device-node-selection": {
oldObj: slice,
newObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesPerDeviceNodeSelection.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
partitionableDevices: true,
expectValidationError: true, // Spec.NodeName is immutable.
},
"keep-fields-partitionable-devices-with-shared-counters": {
oldObj: slice,
newObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesSharedCounters.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
partitionableDevices: true,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesSharedCounters.DeepCopy()
obj.ResourceVersion = "4"
obj.Generation = 1
return obj
}(),
},
"keep-existing-fields-partitionable-devices-with-consumes-counters": {
oldObj: sliceWithPartitionableDevicesConsumesCounters,
newObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesConsumesCounters.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
partitionableDevices: true,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesConsumesCounters.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
},
"keep-existing-fields-partitionable-devices-with-per-device-node-selection": {
oldObj: sliceWithPartitionableDevicesPerDeviceNodeSelection,
newObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesPerDeviceNodeSelection.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
partitionableDevices: true,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesPerDeviceNodeSelection.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
},
"keep-existing-fields-partitionable-devices-with-shared-counters": {
oldObj: sliceWithPartitionableDevicesSharedCounters,
newObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesSharedCounters.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
partitionableDevices: true,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesSharedCounters.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
},
"keep-existing-fields-partitionable-devices-consumes-counters-disabled-feature": {
oldObj: sliceWithPartitionableDevicesConsumesCounters,
newObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesConsumesCounters.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
partitionableDevices: false,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesConsumesCounters.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
},
"keep-existing-fields-partitionable-devices-per-device-node-selection-disabled-feature": {
oldObj: sliceWithPartitionableDevicesPerDeviceNodeSelection,
newObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesPerDeviceNodeSelection.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
partitionableDevices: false,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesPerDeviceNodeSelection.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
},
"keep-existing-fields-partitionable-devices-shared-counters-disabled-feature": {
oldObj: sliceWithPartitionableDevicesSharedCounters,
newObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesSharedCounters.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),
partitionableDevices: false,
expectObj: func() *resource.ResourceSlice {
obj := sliceWithPartitionableDevicesSharedCounters.DeepCopy()
obj.ResourceVersion = "4"
return obj
}(),

View File

@@ -261,15 +261,11 @@ type ResourcePool struct {
const ResourceSliceMaxSharedCapacity = 128
const ResourceSliceMaxDevices = 128
const ResourceSliceMaxDevicesWithTaints = 64
const ResourceSliceMaxDevicesWithTaintsOrConsumesCounters = 64
const PoolNameMaxLength = validation.DNS1123SubdomainMaxLength // Same as for a single node name.
const BindingConditionsMaxSize = 4
const BindingFailureConditionsMaxSize = 4
// Defines the max number of shared counters that can be specified
// in a ResourceSlice. The number is summed up across all sets.
const ResourceSliceMaxSharedCounters = 32
// Defines the maximum number of counter sets (through the
// SharedCounters field) that can be defined in a ResourceSlice.
const ResourceSliceMaxCounterSets = 8
@@ -287,11 +283,6 @@ const ResourceSliceMaxDeviceCounterConsumptionsPerDevice = 2
// per device counter consumption.
const ResourceSliceMaxCountersPerDeviceCounterConsumption = 32
// Defines the maximum number of counters that can be defined
// in device counter consumptions across all devices in a
// ResourceSlice.
const ResourceSliceMaxConsumedCountersPerResourceSlice = 2048
// Device represents one individual hardware instance that can be selected based
// on its attributes. Besides the name, exactly one field must be set.
type Device struct {
@@ -577,14 +568,6 @@ type CapacityRequestPolicyRange struct {
// Limit for the sum of the number of entries in both attributes and capacity.
const ResourceSliceMaxAttributesAndCapacitiesPerDevice = 32
// Limit for the total number of counters in each device.
const ResourceSliceMaxCountersPerDevice = 32
// Limit for the total number of counters defined in devices in
// a ResourceSlice. We want to allow up to 64 devices to specify
// up to 16 counters, so the limit for the ResourceSlice will be 1024.
const ResourceSliceMaxDeviceCountersPerSlice = 1024 // 64 * 16
// QualifiedName is the name of a device attribute or capacity.
//
// Attributes and capacities are defined either by the owner of the specific

View File

@@ -269,15 +269,11 @@ type ResourcePool struct {
const ResourceSliceMaxSharedCapacity = 128
const ResourceSliceMaxDevices = 128
const ResourceSliceMaxDevicesWithTaints = 64
const ResourceSliceMaxDevicesWithTaintsOrConsumesCounters = 64
const PoolNameMaxLength = validation.DNS1123SubdomainMaxLength // Same as for a single node name.
const BindingConditionsMaxSize = 4
const BindingFailureConditionsMaxSize = 4
// Defines the max number of shared counters that can be specified
// in a ResourceSlice. The number is summed up across all sets.
const ResourceSliceMaxSharedCounters = 32
// Defines the maximum number of counter sets (through the
// SharedCounters field) that can be defined in a ResourceSlice.
const ResourceSliceMaxCounterSets = 8
@@ -295,11 +291,6 @@ const ResourceSliceMaxDeviceCounterConsumptionsPerDevice = 2
// per device counter consumption.
const ResourceSliceMaxCountersPerDeviceCounterConsumption = 32
// Defines the maximum number of counters that can be defined
// in device counter consumptions across all devices in a
// ResourceSlice.
const ResourceSliceMaxConsumedCountersPerResourceSlice = 2048
// Device represents one individual hardware instance that can be selected based
// on its attributes. Besides the name, exactly one field must be set.
type Device struct {
@@ -585,14 +576,6 @@ type CapacityRequestPolicyRange struct {
// Limit for the sum of the number of entries in both attributes and capacity.
const ResourceSliceMaxAttributesAndCapacitiesPerDevice = 32
// Limit for the total number of counters in each device.
const ResourceSliceMaxCountersPerDevice = 32
// Limit for the total number of counters defined in devices in
// a ResourceSlice. We want to allow up to 64 devices to specify
// up to 16 counters, so the limit for the ResourceSlice will be 1024.
const ResourceSliceMaxDeviceCountersPerSlice = 1024 // 64 * 16
// QualifiedName is the name of a device attribute or capacity.
//
// Attributes and capacities are defined either by the owner of the specific

View File

@@ -261,15 +261,11 @@ type ResourcePool struct {
const ResourceSliceMaxSharedCapacity = 128
const ResourceSliceMaxDevices = 128
const ResourceSliceMaxDevicesWithTaints = 64
const ResourceSliceMaxDevicesWithTaintsOrConsumesCounters = 64
const PoolNameMaxLength = validation.DNS1123SubdomainMaxLength // Same as for a single node name.
const BindingConditionsMaxSize = 4
const BindingFailureConditionsMaxSize = 4
// Defines the max number of shared counters that can be specified
// in a ResourceSlice. The number is summed up across all sets.
const ResourceSliceMaxSharedCounters = 32
// Defines the maximum number of counter sets (through the
// SharedCounters field) that can be defined in a ResourceSlice.
const ResourceSliceMaxCounterSets = 8
@@ -287,11 +283,6 @@ const ResourceSliceMaxDeviceCounterConsumptionsPerDevice = 2
// per device counter consumption.
const ResourceSliceMaxCountersPerDeviceCounterConsumption = 32
// Defines the maximum number of counters that can be defined
// in device counter consumptions across all devices in a
// ResourceSlice.
const ResourceSliceMaxConsumedCountersPerResourceSlice = 2048
// Device represents one individual hardware instance that can be selected based
// on its attributes. Besides the name, exactly one field must be set.
type Device struct {
@@ -577,14 +568,6 @@ type CapacityRequestPolicyRange struct {
// Limit for the sum of the number of entries in both attributes and capacity.
const ResourceSliceMaxAttributesAndCapacitiesPerDevice = 32
// Limit for the total number of counters in each device.
const ResourceSliceMaxCountersPerDevice = 32
// Limit for the total number of counters defined in devices in
// a ResourceSlice. We want to allow up to 64 devices to specify
// up to 16 counters, so the limit for the ResourceSlice will be 1024.
const ResourceSliceMaxDeviceCountersPerSlice = 1024 // 64 * 16
// QualifiedName is the name of a device attribute or capacity.
//
// Attributes and capacities are defined either by the owner of the specific