mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-09-14 13:45:06 +00:00
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:
@@ -62,6 +62,7 @@ const (
|
|||||||
slice2 = "slice-2"
|
slice2 = "slice-2"
|
||||||
device1 = "device-1"
|
device1 = "device-1"
|
||||||
device2 = "device-2"
|
device2 = "device-2"
|
||||||
|
device3 = "device-3"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -978,7 +979,7 @@ func TestAllocator(t *testing.T) {
|
|||||||
claimsToAllocate: objects(claimWithRequests(
|
claimsToAllocate: objects(claimWithRequests(
|
||||||
claim0,
|
claim0,
|
||||||
[]resourceapi.DeviceConstraint{{MatchAttribute: &intAttribute}},
|
[]resourceapi.DeviceConstraint{{MatchAttribute: &intAttribute}},
|
||||||
request(req0, classA, 3)),
|
request(req0, classA, 2)),
|
||||||
),
|
),
|
||||||
classes: objects(class(classA, driverA), class(classB, driverB)),
|
classes: objects(class(classA, driverA), class(classB, driverB)),
|
||||||
slices: objects(slice(slice1, node1, pool1, driverA,
|
slices: objects(slice(slice1, node1, pool1, driverA,
|
||||||
@@ -993,30 +994,36 @@ func TestAllocator(t *testing.T) {
|
|||||||
|
|
||||||
expectResults: nil,
|
expectResults: nil,
|
||||||
},
|
},
|
||||||
"with-constraint-not-matching-version-attribute": {
|
"with-constraint-not-matching-int-attribute-all-devices": {
|
||||||
claimsToAllocate: objects(claimWithRequests(
|
claimsToAllocate: objects(
|
||||||
claim0,
|
func() *resourceapi.ResourceClaim {
|
||||||
[]resourceapi.DeviceConstraint{{MatchAttribute: &versionAttribute}},
|
claim := claimWithRequests(
|
||||||
request(req0, classA, 3)),
|
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)),
|
classes: objects(class(classA, driverA), class(classB, driverB)),
|
||||||
slices: objects(slice(slice1, node1, pool1, driverA,
|
slices: objects(slice(slice1, node1, pool1, driverA,
|
||||||
device(device1, nil, map[resourceapi.QualifiedName]resourceapi.DeviceAttribute{
|
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{
|
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),
|
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": {
|
"with-constraint-not-matching-string-attribute": {
|
||||||
claimsToAllocate: objects(claimWithRequests(
|
claimsToAllocate: objects(claimWithRequests(
|
||||||
claim0,
|
claim0,
|
||||||
[]resourceapi.DeviceConstraint{{MatchAttribute: &stringAttribute}},
|
[]resourceapi.DeviceConstraint{{MatchAttribute: &stringAttribute}},
|
||||||
request(req0, classA, 3)),
|
request(req0, classA, 2)),
|
||||||
),
|
),
|
||||||
classes: objects(class(classA, driverA), class(classB, driverB)),
|
classes: objects(class(classA, driverA), class(classB, driverB)),
|
||||||
slices: objects(slice(slice1, node1, pool1, driverA,
|
slices: objects(slice(slice1, node1, pool1, driverA,
|
||||||
@@ -1035,7 +1042,7 @@ func TestAllocator(t *testing.T) {
|
|||||||
claimsToAllocate: objects(claimWithRequests(
|
claimsToAllocate: objects(claimWithRequests(
|
||||||
claim0,
|
claim0,
|
||||||
[]resourceapi.DeviceConstraint{{MatchAttribute: &boolAttribute}},
|
[]resourceapi.DeviceConstraint{{MatchAttribute: &boolAttribute}},
|
||||||
request(req0, classA, 3)),
|
request(req0, classA, 2)),
|
||||||
),
|
),
|
||||||
classes: objects(class(classA, driverA), class(classB, driverB)),
|
classes: objects(class(classA, driverA), class(classB, driverB)),
|
||||||
slices: objects(slice(slice1, node1, pool1, driverA,
|
slices: objects(slice(slice1, node1, pool1, driverA,
|
||||||
@@ -1050,6 +1057,95 @@ func TestAllocator(t *testing.T) {
|
|||||||
|
|
||||||
expectResults: nil,
|
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": {
|
"with-class-device-config": {
|
||||||
claimsToAllocate: objects(claim(claim0, req0, classA)),
|
claimsToAllocate: objects(claim(claim0, req0, classA)),
|
||||||
classes: objects(classWithConfig(classA, driverA, "classAttribute")),
|
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 {
|
for name, tc := range testcases {
|
||||||
|
Reference in New Issue
Block a user