DRA scheduler: increase unit test code coverage

Driven by gaps highlighted by `go test -coverprofile` and also adding some more
complex scenarios.
This commit is contained in:
Patrick Ohly
2024-09-23 17:46:40 +02:00
parent d2ab00cf55
commit 6dffee6534

View File

@@ -62,6 +62,7 @@ const (
slice2 = "slice-2"
device1 = "device-1"
device2 = "device-2"
device3 = "device-3"
)
func init() {
@@ -978,7 +979,7 @@ func TestAllocator(t *testing.T) {
claimsToAllocate: objects(claimWithRequests(
claim0,
[]resourceapi.DeviceConstraint{{MatchAttribute: &intAttribute}},
request(req0, classA, 3)),
request(req0, classA, 2)),
),
classes: objects(class(classA, driverA), class(classB, driverB)),
slices: objects(slice(slice1, node1, pool1, driverA,
@@ -993,30 +994,36 @@ func TestAllocator(t *testing.T) {
expectResults: nil,
},
"with-constraint-not-matching-version-attribute": {
claimsToAllocate: objects(claimWithRequests(
claim0,
[]resourceapi.DeviceConstraint{{MatchAttribute: &versionAttribute}},
request(req0, classA, 3)),
"with-constraint-not-matching-int-attribute-all-devices": {
claimsToAllocate: objects(
func() *resourceapi.ResourceClaim {
claim := claimWithRequests(
claim0,
[]resourceapi.DeviceConstraint{{MatchAttribute: &intAttribute}},
request(req0, classA, 0),
)
claim.Spec.Devices.Requests[0].AllocationMode = resourceapi.DeviceAllocationModeAll
return claim
}(),
),
classes: objects(class(classA, driverA), class(classB, driverB)),
slices: objects(slice(slice1, node1, pool1, driverA,
device(device1, nil, map[resourceapi.QualifiedName]resourceapi.DeviceAttribute{
"driverVersion": {VersionValue: ptr.To("1.0.0")},
"numa": {IntValue: ptr.To(int64(1))},
}),
device(device2, nil, map[resourceapi.QualifiedName]resourceapi.DeviceAttribute{
"driverVersion": {VersionValue: ptr.To("2.0.0")},
"numa": {IntValue: ptr.To(int64(2))},
}),
)),
node: node(node1, region1),
expectResults: nil,
expectError: gomega.MatchError(gomega.ContainSubstring("claim claim-0, request req-0: cannot add device driver-a/pool-1/device-2 because a claim constraint would not be satisfied")),
},
"with-constraint-not-matching-string-attribute": {
claimsToAllocate: objects(claimWithRequests(
claim0,
[]resourceapi.DeviceConstraint{{MatchAttribute: &stringAttribute}},
request(req0, classA, 3)),
request(req0, classA, 2)),
),
classes: objects(class(classA, driverA), class(classB, driverB)),
slices: objects(slice(slice1, node1, pool1, driverA,
@@ -1035,7 +1042,7 @@ func TestAllocator(t *testing.T) {
claimsToAllocate: objects(claimWithRequests(
claim0,
[]resourceapi.DeviceConstraint{{MatchAttribute: &boolAttribute}},
request(req0, classA, 3)),
request(req0, classA, 2)),
),
classes: objects(class(classA, driverA), class(classB, driverB)),
slices: objects(slice(slice1, node1, pool1, driverA,
@@ -1050,6 +1057,95 @@ func TestAllocator(t *testing.T) {
expectResults: nil,
},
"with-constraint-not-matching-version-attribute": {
claimsToAllocate: objects(claimWithRequests(
claim0,
[]resourceapi.DeviceConstraint{{MatchAttribute: &versionAttribute}},
request(req0, classA, 2)),
),
classes: objects(class(classA, driverA), class(classB, driverB)),
slices: objects(slice(slice1, node1, pool1, driverA,
device(device1, nil, map[resourceapi.QualifiedName]resourceapi.DeviceAttribute{
"driverVersion": {VersionValue: ptr.To("1.0.0")},
}),
device(device2, nil, map[resourceapi.QualifiedName]resourceapi.DeviceAttribute{
"driverVersion": {VersionValue: ptr.To("2.0.0")},
}),
)),
node: node(node1, region1),
expectResults: nil,
},
"with-constraint-for-request": {
claimsToAllocate: objects(claimWithRequests(
claim0,
[]resourceapi.DeviceConstraint{
{
Requests: []string{req0},
MatchAttribute: &versionAttribute,
},
},
request(req0, classA, 1),
request(req1, classA, 1),
)),
classes: objects(class(classA, driverA), class(classB, driverB)),
slices: objects(slice(slice1, node1, pool1, driverA,
device(device1, nil, map[resourceapi.QualifiedName]resourceapi.DeviceAttribute{
"driverVersion": {VersionValue: ptr.To("1.0.0")},
}),
device(device2, nil, map[resourceapi.QualifiedName]resourceapi.DeviceAttribute{
"driverVersion": {VersionValue: ptr.To("2.0.0")},
}),
)),
node: node(node1, region1),
expectResults: []any{allocationResult(
localNodeSelector(node1),
deviceAllocationResult(req0, driverA, pool1, device1),
deviceAllocationResult(req1, driverA, pool1, device2),
)},
},
"with-constraint-for-request-retry": {
claimsToAllocate: objects(claimWithRequests(
claim0,
[]resourceapi.DeviceConstraint{
{
Requests: []string{req0},
MatchAttribute: &versionAttribute,
},
{
MatchAttribute: &stringAttribute,
},
},
request(req0, classA, 1),
request(req1, classA, 1),
)),
classes: objects(class(classA, driverA), class(classB, driverB)),
slices: objects(slice(slice1, node1, pool1, driverA,
// This device does not satisfy the second
// match attribute, so the allocator must
// backtrack.
device(device1, nil, map[resourceapi.QualifiedName]resourceapi.DeviceAttribute{
"driverVersion": {VersionValue: ptr.To("1.0.0")},
"stringAttribute": {StringValue: ptr.To("a")},
}),
device(device2, nil, map[resourceapi.QualifiedName]resourceapi.DeviceAttribute{
"driverVersion": {VersionValue: ptr.To("2.0.0")},
"stringAttribute": {StringValue: ptr.To("b")},
}),
device(device3, nil, map[resourceapi.QualifiedName]resourceapi.DeviceAttribute{
"driverVersion": {VersionValue: ptr.To("3.0.0")},
"stringAttribute": {StringValue: ptr.To("b")},
}),
)),
node: node(node1, region1),
expectResults: []any{allocationResult(
localNodeSelector(node1),
deviceAllocationResult(req0, driverA, pool1, device2),
deviceAllocationResult(req1, driverA, pool1, device3),
)},
},
"with-class-device-config": {
claimsToAllocate: objects(claim(claim0, req0, classA)),
classes: objects(classWithConfig(classA, driverA, "classAttribute")),
@@ -1082,6 +1178,138 @@ func TestAllocator(t *testing.T) {
),
},
},
"unknown-selector": {
claimsToAllocate: objects(
func() *resourceapi.ResourceClaim {
claim := claim(claim0, req0, classA)
claim.Spec.Devices.Requests[0].Selectors = []resourceapi.DeviceSelector{
{ /* empty = unknown future selector */ },
}
return claim
}(),
),
classes: objects(class(classA, driverA)),
slices: objects(sliceWithOneDevice(slice1, node1, pool1, driverA)),
node: node(node1, region1),
expectError: gomega.MatchError(gomega.ContainSubstring("CEL expression empty (unsupported selector type?)")),
},
"unknown-allocation-mode": {
claimsToAllocate: objects(
func() *resourceapi.ResourceClaim {
claim := claim(claim0, req0, classA)
claim.Spec.Devices.Requests[0].AllocationMode = resourceapi.DeviceAllocationMode("future-mode")
return claim
}(),
),
classes: objects(class(classA, driverA)),
slices: objects(sliceWithOneDevice(slice1, node1, pool1, driverA)),
node: node(node1, region1),
expectError: gomega.MatchError(gomega.ContainSubstring("unsupported count mode future-mode")),
},
"unknown-constraint": {
claimsToAllocate: objects(
func() *resourceapi.ResourceClaim {
claim := claim(claim0, req0, classA)
claim.Spec.Devices.Constraints = []resourceapi.DeviceConstraint{
{ /* empty = unknown */ },
}
return claim
}(),
),
classes: objects(class(classA, driverA)),
slices: objects(sliceWithOneDevice(slice1, node1, pool1, driverA)),
node: node(node1, region1),
expectError: gomega.MatchError(gomega.ContainSubstring("empty constraint (unsupported constraint type?)")),
},
"unknown-device": {
claimsToAllocate: objects(claim(claim0, req0, classA)),
classes: objects(class(classA, driverA)),
slices: objects(
func() *resourceapi.ResourceSlice {
slice := sliceWithOneDevice(slice1, node1, pool1, driverA)
slice.Spec.Devices[0].Basic = nil /* empty = unknown future extension */
return slice
}(),
),
node: node(node1, region1),
},
"invalid-CEL-one-device": {
claimsToAllocate: objects(
func() *resourceapi.ResourceClaim {
claim := claim(claim0, req0, classA)
claim.Spec.Devices.Requests[0].Selectors = []resourceapi.DeviceSelector{
{CEL: &resourceapi.CELDeviceSelector{Expression: "noSuchVar"}},
}
return claim
}(),
),
classes: objects(class(classA, driverA)),
slices: objects(sliceWithOneDevice(slice1, node1, pool1, driverA)),
node: node(node1, region1),
expectError: gomega.MatchError(gomega.ContainSubstring("undeclared reference")),
},
"invalid-CEL-one-device-class": {
claimsToAllocate: objects(claim(claim0, req0, classA)),
classes: objects(
func() *resourceapi.DeviceClass {
c := class(classA, driverA)
c.Spec.Selectors[0].CEL.Expression = "noSuchVar"
return c
}(),
),
slices: objects(sliceWithOneDevice(slice1, node1, pool1, driverA)),
node: node(node1, region1),
expectError: gomega.MatchError(gomega.ContainSubstring("undeclared reference")),
},
"invalid-CEL-all-devices": {
claimsToAllocate: objects(
func() *resourceapi.ResourceClaim {
claim := claim(claim0, req0, classA)
claim.Spec.Devices.Requests[0].Selectors = []resourceapi.DeviceSelector{
{CEL: &resourceapi.CELDeviceSelector{Expression: "noSuchVar"}},
}
claim.Spec.Devices.Requests[0].AllocationMode = resourceapi.DeviceAllocationModeAll
return claim
}(),
),
classes: objects(class(classA, driverA)),
slices: objects(sliceWithOneDevice(slice1, node1, pool1, driverA)),
node: node(node1, region1),
expectError: gomega.MatchError(gomega.ContainSubstring("undeclared reference")),
},
"too-many-devices-single-request": {
claimsToAllocate: objects(claimWithRequests(claim0, nil, request(req0, classA, 500))),
classes: objects(class(classA, driverA)),
expectError: gomega.MatchError(gomega.ContainSubstring("exceeds the claim limit")),
},
"many-devices-okay": {
claimsToAllocate: objects(claimWithRequests(claim0, nil, request(req0, classA, resourceapi.AllocationResultsMaxSize))),
classes: objects(class(classA, driverA)),
},
"too-many-devices-total": {
claimsToAllocate: objects(
claimWithRequests(claim0, nil,
request(req0, classA, resourceapi.AllocationResultsMaxSize),
request(req1, classA, 1),
),
),
classes: objects(class(classA, driverA)),
expectError: gomega.MatchError(gomega.ContainSubstring("exceeds the claim limit")),
},
"all-devices-invalid-CEL": {
claimsToAllocate: objects(claimWithRequests(claim0, nil, request(req0, classA, 500))),
classes: objects(class(classA, driverA)),
expectError: gomega.MatchError(gomega.ContainSubstring("exceeds the claim limit")),
},
}
for name, tc := range testcases {