From 93fc7422ee80f6a93225391e81058395c7cd151d Mon Sep 17 00:00:00 2001 From: Morten Torkildsen Date: Thu, 6 Nov 2025 16:05:57 +0000 Subject: [PATCH] DRA: Update validation for Partitionable Devices --- pkg/apis/resource/types.go | 19 +- pkg/apis/resource/validation/validation.go | 102 ++---- .../validation_resourceslice_test.go | 285 ++++++++++----- .../declarative_validation_test.go | 326 +++++++++++++++--- .../resource/resourceslice/strategy_test.go | 299 +++++++++++----- staging/src/k8s.io/api/resource/v1/types.go | 19 +- .../src/k8s.io/api/resource/v1beta1/types.go | 19 +- .../src/k8s.io/api/resource/v1beta2/types.go | 19 +- 8 files changed, 744 insertions(+), 344 deletions(-) diff --git a/pkg/apis/resource/types.go b/pkg/apis/resource/types.go index b1d1576bb42..35933110d67 100644 --- a/pkg/apis/resource/types.go +++ b/pkg/apis/resource/types.go @@ -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 diff --git a/pkg/apis/resource/validation/validation.go b/pkg/apis/resource/validation/validation.go index d27e07c11c0..ef2b4cfe826 100644 --- a/pkg/apis/resource/validation/validation.go +++ b/pkg/apis/resource/validation/validation.go @@ -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 } diff --git a/pkg/apis/resource/validation/validation_resourceslice_test.go b/pkg/apis/resource/validation/validation_resourceslice_test.go index 4b594452275..7a867b6bdd5 100644 --- a/pkg/apis/resource/validation/validation_resourceslice_test.go +++ b/pkg/apis/resource/validation/validation_resourceslice_test.go @@ -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 }(), }, diff --git a/pkg/registry/resource/resourceslice/declarative_validation_test.go b/pkg/registry/resource/resourceslice/declarative_validation_test.go index 44aeba1803f..307efe7f448 100644 --- a/pkg/registry/resource/resourceslice/declarative_validation_test.go +++ b/pkg/registry/resource/resourceslice/declarative_validation_test.go @@ -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 + } +} diff --git a/pkg/registry/resource/resourceslice/strategy_test.go b/pkg/registry/resource/resourceslice/strategy_test.go index 747c62ba33e..9ae889d5f1a 100644 --- a/pkg/registry/resource/resourceslice/strategy_test.go +++ b/pkg/registry/resource/resourceslice/strategy_test.go @@ -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 }(), diff --git a/staging/src/k8s.io/api/resource/v1/types.go b/staging/src/k8s.io/api/resource/v1/types.go index e76b1bf26f6..29b4a5fbacc 100644 --- a/staging/src/k8s.io/api/resource/v1/types.go +++ b/staging/src/k8s.io/api/resource/v1/types.go @@ -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 diff --git a/staging/src/k8s.io/api/resource/v1beta1/types.go b/staging/src/k8s.io/api/resource/v1beta1/types.go index f961706a58c..c55e2e92c93 100644 --- a/staging/src/k8s.io/api/resource/v1beta1/types.go +++ b/staging/src/k8s.io/api/resource/v1beta1/types.go @@ -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 diff --git a/staging/src/k8s.io/api/resource/v1beta2/types.go b/staging/src/k8s.io/api/resource/v1beta2/types.go index a9a71414704..49534348843 100644 --- a/staging/src/k8s.io/api/resource/v1beta2/types.go +++ b/staging/src/k8s.io/api/resource/v1beta2/types.go @@ -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