Skip to content

Commit 8f7b43b

Browse files
committed
DRA: Update types and defaults for Prioritized Alternatives in Device Requests
1 parent 0faebe3 commit 8f7b43b

File tree

7 files changed

+502
-31
lines changed

7 files changed

+502
-31
lines changed

pkg/apis/resource/types.go

Lines changed: 108 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -388,16 +388,12 @@ const (
388388
// DeviceRequest is a request for devices required for a claim.
389389
// This is typically a request for a single resource like a device, but can
390390
// also ask for several identical devices.
391-
//
392-
// A DeviceClassName is currently required. Clients must check that it is
393-
// indeed set. It's absence indicates that something changed in a way that
394-
// is not supported by the client yet, in which case it must refuse to
395-
// handle the request.
396391
type DeviceRequest struct {
397392
// Name can be used to reference this request in a pod.spec.containers[].resources.claims
398393
// entry and in a constraint of the claim.
399394
//
400-
// Must be a DNS label.
395+
// Must be a DNS label and unique among all DeviceRequests in a
396+
// ResourceClaim.
401397
//
402398
// +required
403399
Name string
@@ -406,15 +402,17 @@ type DeviceRequest struct {
406402
// additional configuration and selectors to be inherited by this
407403
// request.
408404
//
409-
// A class is required. Which classes are available depends on the cluster.
405+
// A class is required if no subrequests are specified in the
406+
// firstAvailable list. Which classes are available depends on the cluster.
410407
//
411408
// Administrators may use this to restrict which devices may get
412409
// requested by only installing classes with selectors for permitted
413410
// devices. If users are free to request anything without restrictions,
414411
// then administrators can create an empty DeviceClass for users
415412
// to reference.
416413
//
417-
// +required
414+
// +optional
415+
// +oneOf=deviceRequestType
418416
DeviceClassName string
419417

420418
// Selectors define criteria which must be satisfied by a specific
@@ -468,10 +466,93 @@ type DeviceRequest struct {
468466
// +optional
469467
// +featureGate=DRAAdminAccess
470468
AdminAccess *bool
469+
470+
// FirstAvailable contains subrequests, exactly one of which must be satisfied
471+
// in order to satisfy this request. This field may only be set in the
472+
// entries of DeviceClaim.Requests.
473+
//
474+
// +optional
475+
// +oneOf=deviceRequestType
476+
// +listType=atomic
477+
// +featureGate=DRAPrioritizedList
478+
FirstAvailable []DeviceSubRequest
479+
}
480+
481+
// DeviceSubRequest describes a request for device provided in the
482+
// claim.spec.devices.requests[].firstAvailable array. Each
483+
// is typically a request for a single resource like a device, but can
484+
// also ask for several identical devices.
485+
//
486+
// DeviceSubRequest is similar to Request, but doesn't expose the AdminAccess
487+
// or FirstAvailable fields, as those can only be set on the top-level request.
488+
// AdminAccess is not supported for requests with a prioritized list, and
489+
// recursive FirstAvailable fields are not supported.
490+
type DeviceSubRequest struct {
491+
// Name can be used to reference this subrequest in the list of constraints
492+
// or the list of configurations for the claim. References must use the
493+
// format <main request>/<subrequest>.
494+
//
495+
// Must be a DNS label.
496+
//
497+
// +required
498+
Name string
499+
500+
// DeviceClassName references a specific DeviceClass, which can define
501+
// additional configuration and selectors to be inherited by this
502+
// subrequest.
503+
//
504+
// A class is required. Which classes are available depends on the cluster.
505+
//
506+
// Administrators may use this to restrict which devices may get
507+
// requested by only installing classes with selectors for permitted
508+
// devices. If users are free to request anything without restrictions,
509+
// then administrators can create an empty DeviceClass for users
510+
// to reference.
511+
//
512+
// +required
513+
DeviceClassName string
514+
515+
// Selectors define criteria which must be satisfied by a specific
516+
// device in order for that device to be considered for this
517+
// subrequest. All selectors must be satisfied for a device to be
518+
// considered.
519+
//
520+
// +optional
521+
// +listType=atomic
522+
Selectors []DeviceSelector
523+
524+
// AllocationMode and its related fields define how devices are allocated
525+
// to satisfy this subrequest. Supported values are:
526+
//
527+
// - ExactCount: This request is for a specific number of devices.
528+
// This is the default. The exact number is provided in the
529+
// count field.
530+
//
531+
// - All: This subrequest is for all of the matching devices in a pool.
532+
// Allocation will fail if some devices are already allocated,
533+
// unless adminAccess is requested.
534+
//
535+
// If AlloctionMode is not specified, the default mode is ExactCount. If
536+
// the mode is ExactCount and count is not specified, the default count is
537+
// one. Any other subrequests must specify this field.
538+
//
539+
// More modes may get added in the future. Clients must refuse to handle
540+
// requests with unknown modes.
541+
//
542+
// +optional
543+
AllocationMode DeviceAllocationMode
544+
545+
// Count is used only when the count mode is "ExactCount". Must be greater than zero.
546+
// If AllocationMode is ExactCount and this field is not specified, the default is one.
547+
//
548+
// +optional
549+
// +oneOf=AllocationMode
550+
Count int64
471551
}
472552

473553
const (
474-
DeviceSelectorsMaxSize = 32
554+
DeviceSelectorsMaxSize = 32
555+
FirstAvailableDeviceRequestMaxSize = 8
475556
)
476557

477558
type DeviceAllocationMode string
@@ -584,6 +665,10 @@ type DeviceConstraint struct {
584665
// constraint. If this is not specified, this constraint applies to all
585666
// requests in this claim.
586667
//
668+
// References to subrequests must include the name of the main request
669+
// and may include the subrequest using the format <main request>[/<subrequest>]. If just
670+
// the main request is given, the constraint applies to all subrequests.
671+
//
587672
// +optional
588673
// +listType=atomic
589674
Requests []string
@@ -621,6 +706,10 @@ type DeviceClaimConfiguration struct {
621706
// Requests lists the names of requests where the configuration applies.
622707
// If empty, it applies to all requests.
623708
//
709+
// References to subrequests must include the name of the main request
710+
// and may include the subrequest using the format <main request>[/<subrequest>]. If just
711+
// the main request is given, the configuration applies to all subrequests.
712+
//
624713
// +optional
625714
// +listType=atomic
626715
Requests []string
@@ -793,8 +882,12 @@ const AllocationResultsMaxSize = 32
793882
// DeviceRequestAllocationResult contains the allocation result for one request.
794883
type DeviceRequestAllocationResult struct {
795884
// Request is the name of the request in the claim which caused this
796-
// device to be allocated. Multiple devices may have been allocated
797-
// per request.
885+
// device to be allocated. If it references a subrequest in the
886+
// firstAvailable list on a DeviceRequest, this field must
887+
// include both the name of the main request and the subrequest
888+
// using the format <main request>/<subrequest>.
889+
//
890+
// Multiple devices may have been allocated per request.
798891
//
799892
// +required
800893
Request string
@@ -849,6 +942,10 @@ type DeviceAllocationConfiguration struct {
849942
// Requests lists the names of requests where the configuration applies.
850943
// If empty, its applies to all requests.
851944
//
945+
// References to subrequests must include the name of the main request
946+
// and may include the subrequest using the format <main request>[/<subrequest>]. If just
947+
// the main request is given, the configuration applies to all subrequests.
948+
//
852949
// +optional
853950
// +listType=atomic
854951
Requests []string

pkg/apis/resource/v1alpha3/defaults.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@ func addDefaultingFuncs(scheme *runtime.Scheme) error {
2626
}
2727

2828
func SetDefaults_DeviceRequest(obj *resourceapi.DeviceRequest) {
29+
if len(obj.FirstAvailable) > 0 {
30+
return
31+
}
32+
if obj.AllocationMode == "" {
33+
obj.AllocationMode = resourceapi.DeviceAllocationModeExactCount
34+
}
35+
36+
if obj.AllocationMode == resourceapi.DeviceAllocationModeExactCount && obj.Count == 0 {
37+
obj.Count = 1
38+
}
39+
}
40+
41+
func SetDefaults_DeviceSubRequest(obj *resourceapi.DeviceSubRequest) {
2942
if obj.AllocationMode == "" {
3043
obj.AllocationMode = resourceapi.DeviceAllocationModeExactCount
3144
}

pkg/apis/resource/v1alpha3/defaults_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,72 @@ func TestSetDefaultAllocationMode(t *testing.T) {
6464
assert.Equal(t, nonDefaultCount, output.Spec.Devices.Requests[0].Count)
6565
}
6666

67+
func TestSetDefaultAllocationModeWithSubRequests(t *testing.T) {
68+
claim := &v1alpha3.ResourceClaim{
69+
Spec: v1alpha3.ResourceClaimSpec{
70+
Devices: v1alpha3.DeviceClaim{
71+
Requests: []v1alpha3.DeviceRequest{
72+
{
73+
Name: "req-1",
74+
FirstAvailable: []v1alpha3.DeviceSubRequest{
75+
{
76+
Name: "subReq-1",
77+
},
78+
{
79+
Name: "subReq-2",
80+
},
81+
},
82+
},
83+
},
84+
},
85+
},
86+
}
87+
88+
nilValueMode := v1alpha3.DeviceAllocationMode("")
89+
nilValueCount := int64(0)
90+
defaultMode := v1alpha3.DeviceAllocationModeExactCount
91+
defaultCount := int64(1)
92+
output := roundTrip(t, runtime.Object(claim)).(*v1alpha3.ResourceClaim)
93+
// fields on the top-level DeviceRequest should not change
94+
assert.Equal(t, nilValueMode, output.Spec.Devices.Requests[0].AllocationMode)
95+
assert.Equal(t, nilValueCount, output.Spec.Devices.Requests[0].Count)
96+
// fields on the subRequests should be defaulted.
97+
assert.Equal(t, defaultMode, output.Spec.Devices.Requests[0].FirstAvailable[0].AllocationMode)
98+
assert.Equal(t, defaultCount, output.Spec.Devices.Requests[0].FirstAvailable[0].Count)
99+
assert.Equal(t, defaultMode, output.Spec.Devices.Requests[0].FirstAvailable[1].AllocationMode)
100+
assert.Equal(t, defaultCount, output.Spec.Devices.Requests[0].FirstAvailable[1].Count)
101+
102+
// field should not change
103+
nonDefaultMode := v1alpha3.DeviceAllocationModeExactCount
104+
nonDefaultCount := int64(10)
105+
claim = &v1alpha3.ResourceClaim{
106+
Spec: v1alpha3.ResourceClaimSpec{
107+
Devices: v1alpha3.DeviceClaim{
108+
Requests: []v1alpha3.DeviceRequest{{
109+
Name: "req-1",
110+
FirstAvailable: []v1alpha3.DeviceSubRequest{
111+
{
112+
Name: "subReq-1",
113+
AllocationMode: nonDefaultMode,
114+
Count: nonDefaultCount,
115+
},
116+
{
117+
Name: "subReq-2",
118+
AllocationMode: nonDefaultMode,
119+
Count: nonDefaultCount,
120+
},
121+
},
122+
}},
123+
},
124+
},
125+
}
126+
output = roundTrip(t, runtime.Object(claim)).(*v1alpha3.ResourceClaim)
127+
assert.Equal(t, nonDefaultMode, output.Spec.Devices.Requests[0].FirstAvailable[0].AllocationMode)
128+
assert.Equal(t, nonDefaultCount, output.Spec.Devices.Requests[0].FirstAvailable[0].Count)
129+
assert.Equal(t, nonDefaultMode, output.Spec.Devices.Requests[0].FirstAvailable[1].AllocationMode)
130+
assert.Equal(t, nonDefaultCount, output.Spec.Devices.Requests[0].FirstAvailable[1].Count)
131+
}
132+
67133
func roundTrip(t *testing.T, obj runtime.Object) runtime.Object {
68134
codec := legacyscheme.Codecs.LegacyCodec(v1alpha3.SchemeGroupVersion)
69135
data, err := runtime.Encode(codec, obj)

pkg/apis/resource/v1beta1/defaults.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,19 @@ func addDefaultingFuncs(scheme *runtime.Scheme) error {
2626
}
2727

2828
func SetDefaults_DeviceRequest(obj *resourceapi.DeviceRequest) {
29+
if len(obj.FirstAvailable) > 0 {
30+
return
31+
}
32+
if obj.AllocationMode == "" {
33+
obj.AllocationMode = resourceapi.DeviceAllocationModeExactCount
34+
}
35+
36+
if obj.AllocationMode == resourceapi.DeviceAllocationModeExactCount && obj.Count == 0 {
37+
obj.Count = 1
38+
}
39+
}
40+
41+
func SetDefaults_DeviceSubRequest(obj *resourceapi.DeviceSubRequest) {
2942
if obj.AllocationMode == "" {
3043
obj.AllocationMode = resourceapi.DeviceAllocationModeExactCount
3144
}

pkg/apis/resource/v1beta1/defaults_test.go

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,72 @@ func TestSetDefaultAllocationMode(t *testing.T) {
6464
assert.Equal(t, nonDefaultCount, output.Spec.Devices.Requests[0].Count)
6565
}
6666

67+
func TestSetDefaultAllocationModeWithSubRequests(t *testing.T) {
68+
claim := &v1beta1.ResourceClaim{
69+
Spec: v1beta1.ResourceClaimSpec{
70+
Devices: v1beta1.DeviceClaim{
71+
Requests: []v1beta1.DeviceRequest{
72+
{
73+
Name: "req-1",
74+
FirstAvailable: []v1beta1.DeviceSubRequest{
75+
{
76+
Name: "subReq-1",
77+
},
78+
{
79+
Name: "subReq-2",
80+
},
81+
},
82+
},
83+
},
84+
},
85+
},
86+
}
87+
88+
nilValueMode := v1beta1.DeviceAllocationMode("")
89+
nilValueCount := int64(0)
90+
defaultMode := v1beta1.DeviceAllocationModeExactCount
91+
defaultCount := int64(1)
92+
output := roundTrip(t, runtime.Object(claim)).(*v1beta1.ResourceClaim)
93+
// fields on the top-level DeviceRequest should not change
94+
assert.Equal(t, nilValueMode, output.Spec.Devices.Requests[0].AllocationMode)
95+
assert.Equal(t, nilValueCount, output.Spec.Devices.Requests[0].Count)
96+
// fields on the subRequests should be defaulted.
97+
assert.Equal(t, defaultMode, output.Spec.Devices.Requests[0].FirstAvailable[0].AllocationMode)
98+
assert.Equal(t, defaultCount, output.Spec.Devices.Requests[0].FirstAvailable[0].Count)
99+
assert.Equal(t, defaultMode, output.Spec.Devices.Requests[0].FirstAvailable[1].AllocationMode)
100+
assert.Equal(t, defaultCount, output.Spec.Devices.Requests[0].FirstAvailable[1].Count)
101+
102+
// field should not change
103+
nonDefaultMode := v1beta1.DeviceAllocationModeExactCount
104+
nonDefaultCount := int64(10)
105+
claim = &v1beta1.ResourceClaim{
106+
Spec: v1beta1.ResourceClaimSpec{
107+
Devices: v1beta1.DeviceClaim{
108+
Requests: []v1beta1.DeviceRequest{{
109+
Name: "req-1",
110+
FirstAvailable: []v1beta1.DeviceSubRequest{
111+
{
112+
Name: "subReq-1",
113+
AllocationMode: nonDefaultMode,
114+
Count: nonDefaultCount,
115+
},
116+
{
117+
Name: "subReq-2",
118+
AllocationMode: nonDefaultMode,
119+
Count: nonDefaultCount,
120+
},
121+
},
122+
}},
123+
},
124+
},
125+
}
126+
output = roundTrip(t, runtime.Object(claim)).(*v1beta1.ResourceClaim)
127+
assert.Equal(t, nonDefaultMode, output.Spec.Devices.Requests[0].FirstAvailable[0].AllocationMode)
128+
assert.Equal(t, nonDefaultCount, output.Spec.Devices.Requests[0].FirstAvailable[0].Count)
129+
assert.Equal(t, nonDefaultMode, output.Spec.Devices.Requests[0].FirstAvailable[1].AllocationMode)
130+
assert.Equal(t, nonDefaultCount, output.Spec.Devices.Requests[0].FirstAvailable[1].Count)
131+
}
132+
67133
func roundTrip(t *testing.T, obj runtime.Object) runtime.Object {
68134
codec := legacyscheme.Codecs.LegacyCodec(v1beta1.SchemeGroupVersion)
69135
data, err := runtime.Encode(codec, obj)

0 commit comments

Comments
 (0)