From 8f7b43b6fdcb825736268967c3262cee2065a409 Mon Sep 17 00:00:00 2001 From: Morten Torkildsen Date: Fri, 28 Feb 2025 19:13:48 +0000 Subject: [PATCH] DRA: Update types and defaults for Prioritized Alternatives in Device Requests --- pkg/apis/resource/types.go | 119 ++++++++++++++-- pkg/apis/resource/v1alpha3/defaults.go | 13 ++ pkg/apis/resource/v1alpha3/defaults_test.go | 66 +++++++++ pkg/apis/resource/v1beta1/defaults.go | 13 ++ pkg/apis/resource/v1beta1/defaults_test.go | 66 +++++++++ .../src/k8s.io/api/resource/v1alpha3/types.go | 128 ++++++++++++++++-- .../src/k8s.io/api/resource/v1beta1/types.go | 128 ++++++++++++++++-- 7 files changed, 502 insertions(+), 31 deletions(-) diff --git a/pkg/apis/resource/types.go b/pkg/apis/resource/types.go index d802944f3d2..7de57ef6b83 100644 --- a/pkg/apis/resource/types.go +++ b/pkg/apis/resource/types.go @@ -388,16 +388,12 @@ const ( // DeviceRequest is a request for devices required for a claim. // This is typically a request for a single resource like a device, but can // also ask for several identical devices. -// -// A DeviceClassName is currently required. Clients must check that it is -// indeed set. It's absence indicates that something changed in a way that -// is not supported by the client yet, in which case it must refuse to -// handle the request. type DeviceRequest struct { // Name can be used to reference this request in a pod.spec.containers[].resources.claims // entry and in a constraint of the claim. // - // Must be a DNS label. + // Must be a DNS label and unique among all DeviceRequests in a + // ResourceClaim. // // +required Name string @@ -406,7 +402,8 @@ type DeviceRequest struct { // additional configuration and selectors to be inherited by this // request. // - // A class is required. Which classes are available depends on the cluster. + // A class is required if no subrequests are specified in the + // firstAvailable list. Which classes are available depends on the cluster. // // Administrators may use this to restrict which devices may get // requested by only installing classes with selectors for permitted @@ -414,7 +411,8 @@ type DeviceRequest struct { // then administrators can create an empty DeviceClass for users // to reference. // - // +required + // +optional + // +oneOf=deviceRequestType DeviceClassName string // Selectors define criteria which must be satisfied by a specific @@ -468,10 +466,93 @@ type DeviceRequest struct { // +optional // +featureGate=DRAAdminAccess AdminAccess *bool + + // FirstAvailable contains subrequests, exactly one of which must be satisfied + // in order to satisfy this request. This field may only be set in the + // entries of DeviceClaim.Requests. + // + // +optional + // +oneOf=deviceRequestType + // +listType=atomic + // +featureGate=DRAPrioritizedList + FirstAvailable []DeviceSubRequest +} + +// DeviceSubRequest describes a request for device provided in the +// claim.spec.devices.requests[].firstAvailable array. Each +// is typically a request for a single resource like a device, but can +// also ask for several identical devices. +// +// DeviceSubRequest is similar to Request, but doesn't expose the AdminAccess +// or FirstAvailable fields, as those can only be set on the top-level request. +// AdminAccess is not supported for requests with a prioritized list, and +// recursive FirstAvailable fields are not supported. +type DeviceSubRequest struct { + // Name can be used to reference this subrequest in the list of constraints + // or the list of configurations for the claim. References must use the + // format
/. + // + // Must be a DNS label. + // + // +required + Name string + + // DeviceClassName references a specific DeviceClass, which can define + // additional configuration and selectors to be inherited by this + // subrequest. + // + // A class is required. Which classes are available depends on the cluster. + // + // Administrators may use this to restrict which devices may get + // requested by only installing classes with selectors for permitted + // devices. If users are free to request anything without restrictions, + // then administrators can create an empty DeviceClass for users + // to reference. + // + // +required + DeviceClassName string + + // Selectors define criteria which must be satisfied by a specific + // device in order for that device to be considered for this + // subrequest. All selectors must be satisfied for a device to be + // considered. + // + // +optional + // +listType=atomic + Selectors []DeviceSelector + + // AllocationMode and its related fields define how devices are allocated + // to satisfy this subrequest. Supported values are: + // + // - ExactCount: This request is for a specific number of devices. + // This is the default. The exact number is provided in the + // count field. + // + // - All: This subrequest is for all of the matching devices in a pool. + // Allocation will fail if some devices are already allocated, + // unless adminAccess is requested. + // + // If AlloctionMode is not specified, the default mode is ExactCount. If + // the mode is ExactCount and count is not specified, the default count is + // one. Any other subrequests must specify this field. + // + // More modes may get added in the future. Clients must refuse to handle + // requests with unknown modes. + // + // +optional + AllocationMode DeviceAllocationMode + + // Count is used only when the count mode is "ExactCount". Must be greater than zero. + // If AllocationMode is ExactCount and this field is not specified, the default is one. + // + // +optional + // +oneOf=AllocationMode + Count int64 } const ( - DeviceSelectorsMaxSize = 32 + DeviceSelectorsMaxSize = 32 + FirstAvailableDeviceRequestMaxSize = 8 ) type DeviceAllocationMode string @@ -584,6 +665,10 @@ type DeviceConstraint struct { // constraint. If this is not specified, this constraint applies to all // requests in this claim. // + // References to subrequests must include the name of the main request + // and may include the subrequest using the format
[/]. If just + // the main request is given, the constraint applies to all subrequests. + // // +optional // +listType=atomic Requests []string @@ -621,6 +706,10 @@ type DeviceClaimConfiguration struct { // Requests lists the names of requests where the configuration applies. // If empty, it applies to all requests. // + // References to subrequests must include the name of the main request + // and may include the subrequest using the format
[/]. If just + // the main request is given, the configuration applies to all subrequests. + // // +optional // +listType=atomic Requests []string @@ -793,8 +882,12 @@ const AllocationResultsMaxSize = 32 // DeviceRequestAllocationResult contains the allocation result for one request. type DeviceRequestAllocationResult struct { // Request is the name of the request in the claim which caused this - // device to be allocated. Multiple devices may have been allocated - // per request. + // device to be allocated. If it references a subrequest in the + // firstAvailable list on a DeviceRequest, this field must + // include both the name of the main request and the subrequest + // using the format
/. + // + // Multiple devices may have been allocated per request. // // +required Request string @@ -849,6 +942,10 @@ type DeviceAllocationConfiguration struct { // Requests lists the names of requests where the configuration applies. // If empty, its applies to all requests. // + // References to subrequests must include the name of the main request + // and may include the subrequest using the format
[/]. If just + // the main request is given, the configuration applies to all subrequests. + // // +optional // +listType=atomic Requests []string diff --git a/pkg/apis/resource/v1alpha3/defaults.go b/pkg/apis/resource/v1alpha3/defaults.go index 848cc065ba6..80dfa4920ca 100644 --- a/pkg/apis/resource/v1alpha3/defaults.go +++ b/pkg/apis/resource/v1alpha3/defaults.go @@ -26,6 +26,19 @@ func addDefaultingFuncs(scheme *runtime.Scheme) error { } func SetDefaults_DeviceRequest(obj *resourceapi.DeviceRequest) { + if len(obj.FirstAvailable) > 0 { + return + } + if obj.AllocationMode == "" { + obj.AllocationMode = resourceapi.DeviceAllocationModeExactCount + } + + if obj.AllocationMode == resourceapi.DeviceAllocationModeExactCount && obj.Count == 0 { + obj.Count = 1 + } +} + +func SetDefaults_DeviceSubRequest(obj *resourceapi.DeviceSubRequest) { if obj.AllocationMode == "" { obj.AllocationMode = resourceapi.DeviceAllocationModeExactCount } diff --git a/pkg/apis/resource/v1alpha3/defaults_test.go b/pkg/apis/resource/v1alpha3/defaults_test.go index 5af5760e147..10f5a385b90 100644 --- a/pkg/apis/resource/v1alpha3/defaults_test.go +++ b/pkg/apis/resource/v1alpha3/defaults_test.go @@ -64,6 +64,72 @@ func TestSetDefaultAllocationMode(t *testing.T) { assert.Equal(t, nonDefaultCount, output.Spec.Devices.Requests[0].Count) } +func TestSetDefaultAllocationModeWithSubRequests(t *testing.T) { + claim := &v1alpha3.ResourceClaim{ + Spec: v1alpha3.ResourceClaimSpec{ + Devices: v1alpha3.DeviceClaim{ + Requests: []v1alpha3.DeviceRequest{ + { + Name: "req-1", + FirstAvailable: []v1alpha3.DeviceSubRequest{ + { + Name: "subReq-1", + }, + { + Name: "subReq-2", + }, + }, + }, + }, + }, + }, + } + + nilValueMode := v1alpha3.DeviceAllocationMode("") + nilValueCount := int64(0) + defaultMode := v1alpha3.DeviceAllocationModeExactCount + defaultCount := int64(1) + output := roundTrip(t, runtime.Object(claim)).(*v1alpha3.ResourceClaim) + // fields on the top-level DeviceRequest should not change + assert.Equal(t, nilValueMode, output.Spec.Devices.Requests[0].AllocationMode) + assert.Equal(t, nilValueCount, output.Spec.Devices.Requests[0].Count) + // fields on the subRequests should be defaulted. + assert.Equal(t, defaultMode, output.Spec.Devices.Requests[0].FirstAvailable[0].AllocationMode) + assert.Equal(t, defaultCount, output.Spec.Devices.Requests[0].FirstAvailable[0].Count) + assert.Equal(t, defaultMode, output.Spec.Devices.Requests[0].FirstAvailable[1].AllocationMode) + assert.Equal(t, defaultCount, output.Spec.Devices.Requests[0].FirstAvailable[1].Count) + + // field should not change + nonDefaultMode := v1alpha3.DeviceAllocationModeExactCount + nonDefaultCount := int64(10) + claim = &v1alpha3.ResourceClaim{ + Spec: v1alpha3.ResourceClaimSpec{ + Devices: v1alpha3.DeviceClaim{ + Requests: []v1alpha3.DeviceRequest{{ + Name: "req-1", + FirstAvailable: []v1alpha3.DeviceSubRequest{ + { + Name: "subReq-1", + AllocationMode: nonDefaultMode, + Count: nonDefaultCount, + }, + { + Name: "subReq-2", + AllocationMode: nonDefaultMode, + Count: nonDefaultCount, + }, + }, + }}, + }, + }, + } + output = roundTrip(t, runtime.Object(claim)).(*v1alpha3.ResourceClaim) + assert.Equal(t, nonDefaultMode, output.Spec.Devices.Requests[0].FirstAvailable[0].AllocationMode) + assert.Equal(t, nonDefaultCount, output.Spec.Devices.Requests[0].FirstAvailable[0].Count) + assert.Equal(t, nonDefaultMode, output.Spec.Devices.Requests[0].FirstAvailable[1].AllocationMode) + assert.Equal(t, nonDefaultCount, output.Spec.Devices.Requests[0].FirstAvailable[1].Count) +} + func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { codec := legacyscheme.Codecs.LegacyCodec(v1alpha3.SchemeGroupVersion) data, err := runtime.Encode(codec, obj) diff --git a/pkg/apis/resource/v1beta1/defaults.go b/pkg/apis/resource/v1beta1/defaults.go index 59f402867cb..c7d7b2c24a5 100644 --- a/pkg/apis/resource/v1beta1/defaults.go +++ b/pkg/apis/resource/v1beta1/defaults.go @@ -26,6 +26,19 @@ func addDefaultingFuncs(scheme *runtime.Scheme) error { } func SetDefaults_DeviceRequest(obj *resourceapi.DeviceRequest) { + if len(obj.FirstAvailable) > 0 { + return + } + if obj.AllocationMode == "" { + obj.AllocationMode = resourceapi.DeviceAllocationModeExactCount + } + + if obj.AllocationMode == resourceapi.DeviceAllocationModeExactCount && obj.Count == 0 { + obj.Count = 1 + } +} + +func SetDefaults_DeviceSubRequest(obj *resourceapi.DeviceSubRequest) { if obj.AllocationMode == "" { obj.AllocationMode = resourceapi.DeviceAllocationModeExactCount } diff --git a/pkg/apis/resource/v1beta1/defaults_test.go b/pkg/apis/resource/v1beta1/defaults_test.go index 3c7d59e422f..e9e5913e72f 100644 --- a/pkg/apis/resource/v1beta1/defaults_test.go +++ b/pkg/apis/resource/v1beta1/defaults_test.go @@ -64,6 +64,72 @@ func TestSetDefaultAllocationMode(t *testing.T) { assert.Equal(t, nonDefaultCount, output.Spec.Devices.Requests[0].Count) } +func TestSetDefaultAllocationModeWithSubRequests(t *testing.T) { + claim := &v1beta1.ResourceClaim{ + Spec: v1beta1.ResourceClaimSpec{ + Devices: v1beta1.DeviceClaim{ + Requests: []v1beta1.DeviceRequest{ + { + Name: "req-1", + FirstAvailable: []v1beta1.DeviceSubRequest{ + { + Name: "subReq-1", + }, + { + Name: "subReq-2", + }, + }, + }, + }, + }, + }, + } + + nilValueMode := v1beta1.DeviceAllocationMode("") + nilValueCount := int64(0) + defaultMode := v1beta1.DeviceAllocationModeExactCount + defaultCount := int64(1) + output := roundTrip(t, runtime.Object(claim)).(*v1beta1.ResourceClaim) + // fields on the top-level DeviceRequest should not change + assert.Equal(t, nilValueMode, output.Spec.Devices.Requests[0].AllocationMode) + assert.Equal(t, nilValueCount, output.Spec.Devices.Requests[0].Count) + // fields on the subRequests should be defaulted. + assert.Equal(t, defaultMode, output.Spec.Devices.Requests[0].FirstAvailable[0].AllocationMode) + assert.Equal(t, defaultCount, output.Spec.Devices.Requests[0].FirstAvailable[0].Count) + assert.Equal(t, defaultMode, output.Spec.Devices.Requests[0].FirstAvailable[1].AllocationMode) + assert.Equal(t, defaultCount, output.Spec.Devices.Requests[0].FirstAvailable[1].Count) + + // field should not change + nonDefaultMode := v1beta1.DeviceAllocationModeExactCount + nonDefaultCount := int64(10) + claim = &v1beta1.ResourceClaim{ + Spec: v1beta1.ResourceClaimSpec{ + Devices: v1beta1.DeviceClaim{ + Requests: []v1beta1.DeviceRequest{{ + Name: "req-1", + FirstAvailable: []v1beta1.DeviceSubRequest{ + { + Name: "subReq-1", + AllocationMode: nonDefaultMode, + Count: nonDefaultCount, + }, + { + Name: "subReq-2", + AllocationMode: nonDefaultMode, + Count: nonDefaultCount, + }, + }, + }}, + }, + }, + } + output = roundTrip(t, runtime.Object(claim)).(*v1beta1.ResourceClaim) + assert.Equal(t, nonDefaultMode, output.Spec.Devices.Requests[0].FirstAvailable[0].AllocationMode) + assert.Equal(t, nonDefaultCount, output.Spec.Devices.Requests[0].FirstAvailable[0].Count) + assert.Equal(t, nonDefaultMode, output.Spec.Devices.Requests[0].FirstAvailable[1].AllocationMode) + assert.Equal(t, nonDefaultCount, output.Spec.Devices.Requests[0].FirstAvailable[1].Count) +} + func roundTrip(t *testing.T, obj runtime.Object) runtime.Object { codec := legacyscheme.Codecs.LegacyCodec(v1beta1.SchemeGroupVersion) data, err := runtime.Encode(codec, obj) diff --git a/staging/src/k8s.io/api/resource/v1alpha3/types.go b/staging/src/k8s.io/api/resource/v1alpha3/types.go index e6471d05851..354b3658545 100644 --- a/staging/src/k8s.io/api/resource/v1alpha3/types.go +++ b/staging/src/k8s.io/api/resource/v1alpha3/types.go @@ -386,11 +386,6 @@ const ( // DeviceRequest is a request for devices required for a claim. // This is typically a request for a single resource like a device, but can // also ask for several identical devices. -// -// A DeviceClassName is currently required. Clients must check that it is -// indeed set. It's absence indicates that something changed in a way that -// is not supported by the client yet, in which case it must refuse to -// handle the request. type DeviceRequest struct { // Name can be used to reference this request in a pod.spec.containers[].resources.claims // entry and in a constraint of the claim. @@ -404,7 +399,8 @@ type DeviceRequest struct { // additional configuration and selectors to be inherited by this // request. // - // A class is required. Which classes are available depends on the cluster. + // A class is required if no subrequests are specified in the + // firstAvailable list. Which classes are available depends on the cluster. // // Administrators may use this to restrict which devices may get // requested by only installing classes with selectors for permitted @@ -412,7 +408,8 @@ type DeviceRequest struct { // then administrators can create an empty DeviceClass for users // to reference. // - // +required + // +optional + // +oneOf=deviceRequestType DeviceClassName string `json:"deviceClassName" protobuf:"bytes,2,name=deviceClassName"` // Selectors define criteria which must be satisfied by a specific @@ -466,10 +463,105 @@ type DeviceRequest struct { // +optional // +featureGate=DRAAdminAccess AdminAccess *bool `json:"adminAccess,omitempty" protobuf:"bytes,6,opt,name=adminAccess"` + + // FirstAvailable contains subrequests, of which exactly one will be + // satisfied by the scheduler to satisfy this request. It tries to + // satisfy them in the order in which they are listed here. So if + // there are two entries in the list, the schduler will only check + // the second one if it determines that the first one can not be used. + // + // This field may only be set in the entries of DeviceClaim.Requests. + // + // DRA does not yet implement scoring, so the scheduler will + // select the first set of devices that satisfies all the + // requests in the claim. And if the requirements can + // be satisfied on more than one node, other scheduling features + // will determine which node is chosen. This means that the set of + // devices allocated to a claim might not be the optimal set + // available to the cluster. Scoring will be implemented later. + // + // +optional + // +oneOf=deviceRequestType + // +listType=atomic + // +featureGate=DRAPrioritizedList + FirstAvailable []DeviceSubRequest `json:"firstAvailable,omitempty" protobuf:"bytes,7,name=firstAvailable"` +} + +// DeviceSubRequest describes a request for device provided in the +// claim.spec.devices.requests[].firstAvailable array. Each +// is typically a request for a single resource like a device, but can +// also ask for several identical devices. +// +// DeviceSubRequest is similar to Request, but doesn't expose the AdminAccess +// or FirstAvailable fields, as those can only be set on the top-level request. +// AdminAccess is not supported for requests with a prioritized list, and +// recursive FirstAvailable fields are not supported. +type DeviceSubRequest struct { + // Name can be used to reference this subrequest in the list of constraints + // or the list of configurations for the claim. References must use the + // format
/. + // + // Must be a DNS label. + // + // +required + Name string `json:"name" protobuf:"bytes,1,name=name"` + + // DeviceClassName references a specific DeviceClass, which can define + // additional configuration and selectors to be inherited by this + // subrequest. + // + // A class is required. Which classes are available depends on the cluster. + // + // Administrators may use this to restrict which devices may get + // requested by only installing classes with selectors for permitted + // devices. If users are free to request anything without restrictions, + // then administrators can create an empty DeviceClass for users + // to reference. + // + // +required + DeviceClassName string `json:"deviceClassName" protobuf:"bytes,2,name=deviceClassName"` + + // Selectors define criteria which must be satisfied by a specific + // device in order for that device to be considered for this + // request. All selectors must be satisfied for a device to be + // considered. + // + // +optional + // +listType=atomic + Selectors []DeviceSelector `json:"selectors,omitempty" protobuf:"bytes,3,name=selectors"` + + // AllocationMode and its related fields define how devices are allocated + // to satisfy this request. Supported values are: + // + // - ExactCount: This request is for a specific number of devices. + // This is the default. The exact number is provided in the + // count field. + // + // - All: This request is for all of the matching devices in a pool. + // Allocation will fail if some devices are already allocated, + // unless adminAccess is requested. + // + // If AlloctionMode is not specified, the default mode is ExactCount. If + // the mode is ExactCount and count is not specified, the default count is + // one. Any other requests must specify this field. + // + // More modes may get added in the future. Clients must refuse to handle + // requests with unknown modes. + // + // +optional + AllocationMode DeviceAllocationMode `json:"allocationMode,omitempty" protobuf:"bytes,4,opt,name=allocationMode"` + + // Count is used only when the count mode is "ExactCount". Must be greater than zero. + // If AllocationMode is ExactCount and this field is not specified, the default is one. + // + // +optional + // +oneOf=AllocationMode + Count int64 `json:"count,omitempty" protobuf:"bytes,5,opt,name=count"` } const ( - DeviceSelectorsMaxSize = 32 + DeviceSelectorsMaxSize = 32 + FirstAvailableDeviceRequestMaxSize = 8 ) type DeviceAllocationMode string @@ -582,6 +674,10 @@ type DeviceConstraint struct { // constraint. If this is not specified, this constraint applies to all // requests in this claim. // + // References to subrequests must include the name of the main request + // and may include the subrequest using the format
[/]. If just + // the main request is given, the constraint applies to all subrequests. + // // +optional // +listType=atomic Requests []string `json:"requests,omitempty" protobuf:"bytes,1,opt,name=requests"` @@ -619,6 +715,10 @@ type DeviceClaimConfiguration struct { // Requests lists the names of requests where the configuration applies. // If empty, it applies to all requests. // + // References to subrequests must include the name of the main request + // and may include the subrequest using the format
[/]. If just + // the main request is given, the configuration applies to all subrequests. + // // +optional // +listType=atomic Requests []string `json:"requests,omitempty" protobuf:"bytes,1,opt,name=requests"` @@ -791,8 +891,12 @@ const AllocationResultsMaxSize = 32 // DeviceRequestAllocationResult contains the allocation result for one request. type DeviceRequestAllocationResult struct { // Request is the name of the request in the claim which caused this - // device to be allocated. Multiple devices may have been allocated - // per request. + // device to be allocated. If it references a subrequest in the + // firstAvailable list on a DeviceRequest, this field must + // include both the name of the main request and the subrequest + // using the format
/. + // + // Multiple devices may have been allocated per request. // // +required Request string `json:"request" protobuf:"bytes,1,name=request"` @@ -847,6 +951,10 @@ type DeviceAllocationConfiguration struct { // Requests lists the names of requests where the configuration applies. // If empty, its applies to all requests. // + // References to subrequests must include the name of the main request + // and may include the subrequest using the format
[/]. If just + // the main request is given, the configuration applies to all subrequests. + // // +optional // +listType=atomic Requests []string `json:"requests,omitempty" protobuf:"bytes,2,opt,name=requests"` diff --git a/staging/src/k8s.io/api/resource/v1beta1/types.go b/staging/src/k8s.io/api/resource/v1beta1/types.go index 558665386e7..2f2b97521b6 100644 --- a/staging/src/k8s.io/api/resource/v1beta1/types.go +++ b/staging/src/k8s.io/api/resource/v1beta1/types.go @@ -394,16 +394,12 @@ const ( // DeviceRequest is a request for devices required for a claim. // This is typically a request for a single resource like a device, but can // also ask for several identical devices. -// -// A DeviceClassName is currently required. Clients must check that it is -// indeed set. It's absence indicates that something changed in a way that -// is not supported by the client yet, in which case it must refuse to -// handle the request. type DeviceRequest struct { // Name can be used to reference this request in a pod.spec.containers[].resources.claims // entry and in a constraint of the claim. // - // Must be a DNS label. + // Must be a DNS label and unique among all DeviceRequests in a + // ResourceClaim. // // +required Name string `json:"name" protobuf:"bytes,1,name=name"` @@ -412,7 +408,8 @@ type DeviceRequest struct { // additional configuration and selectors to be inherited by this // request. // - // A class is required. Which classes are available depends on the cluster. + // A class is required if no subrequests are specified in the + // firstAvailable list. Which classes are available depends on the cluster. // // Administrators may use this to restrict which devices may get // requested by only installing classes with selectors for permitted @@ -420,7 +417,8 @@ type DeviceRequest struct { // then administrators can create an empty DeviceClass for users // to reference. // - // +required + // +optional + // +oneOf=deviceRequestType DeviceClassName string `json:"deviceClassName" protobuf:"bytes,2,name=deviceClassName"` // Selectors define criteria which must be satisfied by a specific @@ -474,6 +472,100 @@ type DeviceRequest struct { // +optional // +featureGate=DRAAdminAccess AdminAccess *bool `json:"adminAccess,omitempty" protobuf:"bytes,6,opt,name=adminAccess"` + + // FirstAvailable contains subrequests, of which exactly one will be + // satisfied by the scheduler to satisfy this request. It tries to + // satisfy them in the order in which they are listed here. So if + // there are two entries in the list, the schduler will only check + // the second one if it determines that the first one can not be used. + // + // This field may only be set in the entries of DeviceClaim.Requests. + // + // DRA does not yet implement scoring, so the scheduler will + // select the first set of devices that satisfies all the + // requests in the claim. And if the requirements can + // be satisfied on more than one node, other scheduling features + // will determine which node is chosen. This means that the set of + // devices allocated to a claim might not be the optimal set + // available to the cluster. Scoring will be implemented later. + // + // +optional + // +oneOf=deviceRequestType + // +listType=atomic + // +featureGate=DRAPrioritizedList + FirstAvailable []DeviceSubRequest `json:"firstAvailable,omitempty" protobuf:"bytes,7,name=firstAvailable"` +} + +// DeviceSubRequest describes a request for device provided in the +// claim.spec.devices.requests[].firstAvailable array. Each +// is typically a request for a single resource like a device, but can +// also ask for several identical devices. +// +// DeviceSubRequest is similar to Request, but doesn't expose the AdminAccess +// or FirstAvailable fields, as those can only be set on the top-level request. +// AdminAccess is not supported for requests with a prioritized list, and +// recursive FirstAvailable fields are not supported. +type DeviceSubRequest struct { + // Name can be used to reference this subrequest in the list of constraints + // or the list of configurations for the claim. References must use the + // format
/. + // + // Must be a DNS label. + // + // +required + Name string `json:"name" protobuf:"bytes,1,name=name"` + + // DeviceClassName references a specific DeviceClass, which can define + // additional configuration and selectors to be inherited by this + // subrequest. + // + // A class is required. Which classes are available depends on the cluster. + // + // Administrators may use this to restrict which devices may get + // requested by only installing classes with selectors for permitted + // devices. If users are free to request anything without restrictions, + // then administrators can create an empty DeviceClass for users + // to reference. + // + // +required + DeviceClassName string `json:"deviceClassName" protobuf:"bytes,2,name=deviceClassName"` + + // Selectors define criteria which must be satisfied by a specific + // device in order for that device to be considered for this + // subrequest. All selectors must be satisfied for a device to be + // considered. + // + // +optional + // +listType=atomic + Selectors []DeviceSelector `json:"selectors,omitempty" protobuf:"bytes,3,name=selectors"` + + // AllocationMode and its related fields define how devices are allocated + // to satisfy this subrequest. Supported values are: + // + // - ExactCount: This request is for a specific number of devices. + // This is the default. The exact number is provided in the + // count field. + // + // - All: This subrequest is for all of the matching devices in a pool. + // Allocation will fail if some devices are already allocated, + // unless adminAccess is requested. + // + // If AlloctionMode is not specified, the default mode is ExactCount. If + // the mode is ExactCount and count is not specified, the default count is + // one. Any other subrequests must specify this field. + // + // More modes may get added in the future. Clients must refuse to handle + // requests with unknown modes. + // + // +optional + AllocationMode DeviceAllocationMode `json:"allocationMode,omitempty" protobuf:"bytes,4,opt,name=allocationMode"` + + // Count is used only when the count mode is "ExactCount". Must be greater than zero. + // If AllocationMode is ExactCount and this field is not specified, the default is one. + // + // +optional + // +oneOf=AllocationMode + Count int64 `json:"count,omitempty" protobuf:"bytes,5,opt,name=count"` } const ( @@ -590,6 +682,10 @@ type DeviceConstraint struct { // constraint. If this is not specified, this constraint applies to all // requests in this claim. // + // References to subrequests must include the name of the main request + // and may include the subrequest using the format
[/]. If just + // the main request is given, the constraint applies to all subrequests. + // // +optional // +listType=atomic Requests []string `json:"requests,omitempty" protobuf:"bytes,1,opt,name=requests"` @@ -627,6 +723,10 @@ type DeviceClaimConfiguration struct { // Requests lists the names of requests where the configuration applies. // If empty, it applies to all requests. // + // References to subrequests must include the name of the main request + // and may include the subrequest using the format
[/]. If just + // the main request is given, the configuration applies to all subrequests. + // // +optional // +listType=atomic Requests []string `json:"requests,omitempty" protobuf:"bytes,1,opt,name=requests"` @@ -799,8 +899,12 @@ const AllocationResultsMaxSize = 32 // DeviceRequestAllocationResult contains the allocation result for one request. type DeviceRequestAllocationResult struct { // Request is the name of the request in the claim which caused this - // device to be allocated. Multiple devices may have been allocated - // per request. + // device to be allocated. If it references a subrequest in the + // firstAvailable list on a DeviceRequest, this field must + // include both the name of the main request and the subrequest + // using the format
/. + // + // Multiple devices may have been allocated per request. // // +required Request string `json:"request" protobuf:"bytes,1,name=request"` @@ -855,6 +959,10 @@ type DeviceAllocationConfiguration struct { // Requests lists the names of requests where the configuration applies. // If empty, its applies to all requests. // + // References to subrequests must include the name of the main request + // and may include the subrequest using the format
[/]. If just + // the main request is given, the configuration applies to all subrequests. + // // +optional // +listType=atomic Requests []string `json:"requests,omitempty" protobuf:"bytes,2,opt,name=requests"`