diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json index af14dfac5ed..5f47fe88d65 100644 --- a/api/openapi-spec/swagger.json +++ b/api/openapi-spec/swagger.json @@ -15524,7 +15524,7 @@ "description": "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.\n\nA 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.", "properties": { "adminAccess": { - "description": "AdminAccess indicates that this is a claim for administrative access to the device(s). Claims with AdminAccess are expected to be used for monitoring or other management services for a device. They ignore all ordinary claims to the device with respect to access modes and any resource allocations.", + "description": "AdminAccess indicates that this is a claim for administrative access to the device(s). Claims with AdminAccess are expected to be used for monitoring or other management services for a device. They ignore all ordinary claims to the device with respect to access modes and any resource allocations.\n\nThis is an alpha field and requires enabling the DRAAdminAccess feature gate. Admin access is disabled if this field is unset or set to false, otherwise it is enabled.", "type": "boolean" }, "allocationMode": { @@ -15562,6 +15562,10 @@ "io.k8s.api.resource.v1alpha3.DeviceRequestAllocationResult": { "description": "DeviceRequestAllocationResult contains the allocation result for one request.", "properties": { + "adminAccess": { + "description": "AdminAccess indicates that this device was allocated for administrative access. See the corresponding request field for a definition of mode.\n\nThis is an alpha field and requires enabling the DRAAdminAccess feature gate. Admin access is disabled if this field is unset or set to false, otherwise it is enabled.", + "type": "boolean" + }, "device": { "description": "Device references one device instance via its name in the driver's resource pool. It must be a DNS label.", "type": "string" diff --git a/api/openapi-spec/v3/apis__resource.k8s.io__v1alpha3_openapi.json b/api/openapi-spec/v3/apis__resource.k8s.io__v1alpha3_openapi.json index a65fc3728cd..fec9b5a7e80 100644 --- a/api/openapi-spec/v3/apis__resource.k8s.io__v1alpha3_openapi.json +++ b/api/openapi-spec/v3/apis__resource.k8s.io__v1alpha3_openapi.json @@ -480,8 +480,7 @@ "description": "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.\n\nA 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.", "properties": { "adminAccess": { - "default": false, - "description": "AdminAccess indicates that this is a claim for administrative access to the device(s). Claims with AdminAccess are expected to be used for monitoring or other management services for a device. They ignore all ordinary claims to the device with respect to access modes and any resource allocations.", + "description": "AdminAccess indicates that this is a claim for administrative access to the device(s). Claims with AdminAccess are expected to be used for monitoring or other management services for a device. They ignore all ordinary claims to the device with respect to access modes and any resource allocations.\n\nThis is an alpha field and requires enabling the DRAAdminAccess feature gate. Admin access is disabled if this field is unset or set to false, otherwise it is enabled.", "type": "boolean" }, "allocationMode": { @@ -526,6 +525,10 @@ "io.k8s.api.resource.v1alpha3.DeviceRequestAllocationResult": { "description": "DeviceRequestAllocationResult contains the allocation result for one request.", "properties": { + "adminAccess": { + "description": "AdminAccess indicates that this device was allocated for administrative access. See the corresponding request field for a definition of mode.\n\nThis is an alpha field and requires enabling the DRAAdminAccess feature gate. Admin access is disabled if this field is unset or set to false, otherwise it is enabled.", + "type": "boolean" + }, "device": { "default": "", "description": "Device references one device instance via its name in the driver's resource pool. It must be a DNS label.", diff --git a/cmd/kube-controller-manager/app/core.go b/cmd/kube-controller-manager/app/core.go index 4be3947cd13..fa8367f58f8 100644 --- a/cmd/kube-controller-manager/app/core.go +++ b/cmd/kube-controller-manager/app/core.go @@ -407,6 +407,7 @@ func newResourceClaimControllerDescriptor() *ControllerDescriptor { func startResourceClaimController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) { ephemeralController, err := resourceclaim.NewController( klog.FromContext(ctx), + utilfeature.DefaultFeatureGate.Enabled(features.DRAAdminAccess), controllerContext.ClientBuilder.ClientOrDie("resource-claim-controller"), controllerContext.InformerFactory.Core().V1().Pods(), controllerContext.InformerFactory.Resource().V1alpha3().ResourceClaims(), diff --git a/pkg/apis/resource/types.go b/pkg/apis/resource/types.go index 5246bc52528..279aebac721 100644 --- a/pkg/apis/resource/types.go +++ b/pkg/apis/resource/types.go @@ -442,9 +442,13 @@ type DeviceRequest struct { // all ordinary claims to the device with respect to access modes and // any resource allocations. // + // This is an alpha field and requires enabling the DRAAdminAccess + // feature gate. Admin access is disabled if this field is unset or + // set to false, otherwise it is enabled. + // // +optional - // +default=false - AdminAccess bool + // +featureGate=DRAAdminAccess + AdminAccess *bool } const ( @@ -782,6 +786,18 @@ type DeviceRequestAllocationResult struct { // // +required Device string + + // AdminAccess indicates that this device was allocated for + // administrative access. See the corresponding request field + // for a definition of mode. + // + // This is an alpha field and requires enabling the DRAAdminAccess + // feature gate. Admin access is disabled if this field is unset or + // set to false, otherwise it is enabled. + // + // +optional + // +featureGate=DRAAdminAccess + AdminAccess *bool } // DeviceAllocationConfiguration gets embedded in an AllocationResult. diff --git a/pkg/apis/resource/v1alpha3/zz_generated.conversion.go b/pkg/apis/resource/v1alpha3/zz_generated.conversion.go index a998aac2ac5..0c54c8e00bb 100644 --- a/pkg/apis/resource/v1alpha3/zz_generated.conversion.go +++ b/pkg/apis/resource/v1alpha3/zz_generated.conversion.go @@ -712,7 +712,7 @@ func autoConvert_v1alpha3_DeviceRequest_To_resource_DeviceRequest(in *resourcev1 out.Selectors = *(*[]resource.DeviceSelector)(unsafe.Pointer(&in.Selectors)) out.AllocationMode = resource.DeviceAllocationMode(in.AllocationMode) out.Count = in.Count - out.AdminAccess = in.AdminAccess + out.AdminAccess = (*bool)(unsafe.Pointer(in.AdminAccess)) return nil } @@ -727,7 +727,7 @@ func autoConvert_resource_DeviceRequest_To_v1alpha3_DeviceRequest(in *resource.D out.Selectors = *(*[]resourcev1alpha3.DeviceSelector)(unsafe.Pointer(&in.Selectors)) out.AllocationMode = resourcev1alpha3.DeviceAllocationMode(in.AllocationMode) out.Count = in.Count - out.AdminAccess = in.AdminAccess + out.AdminAccess = (*bool)(unsafe.Pointer(in.AdminAccess)) return nil } @@ -741,6 +741,7 @@ func autoConvert_v1alpha3_DeviceRequestAllocationResult_To_resource_DeviceReques out.Driver = in.Driver out.Pool = in.Pool out.Device = in.Device + out.AdminAccess = (*bool)(unsafe.Pointer(in.AdminAccess)) return nil } @@ -754,6 +755,7 @@ func autoConvert_resource_DeviceRequestAllocationResult_To_v1alpha3_DeviceReques out.Driver = in.Driver out.Pool = in.Pool out.Device = in.Device + out.AdminAccess = (*bool)(unsafe.Pointer(in.AdminAccess)) return nil } diff --git a/pkg/apis/resource/validation/validation.go b/pkg/apis/resource/validation/validation.go index 48f3bb84809..d451f3912c8 100644 --- a/pkg/apis/resource/validation/validation.go +++ b/pkg/apis/resource/validation/validation.go @@ -261,7 +261,6 @@ func validateOpaqueConfiguration(config resource.OpaqueDeviceConfiguration, fldP func validateResourceClaimStatusUpdate(status, oldStatus *resource.ResourceClaimStatus, claimDeleted bool, requestNames sets.Set[string], fldPath *field.Path) field.ErrorList { var allErrs field.ErrorList - allErrs = append(allErrs, validateAllocationResult(status.Allocation, fldPath.Child("allocation"), requestNames)...) allErrs = append(allErrs, validateSet(status.ReservedFor, resource.ResourceClaimReservedForMaxSize, validateResourceClaimUserReference, func(consumer resource.ResourceClaimConsumerReference) (types.UID, string) { return consumer.UID, "uid" }, @@ -285,9 +284,14 @@ func validateResourceClaimStatusUpdate(status, oldStatus *resource.ResourceClaim } } - // Updates to a populated status.Allocation are not allowed + // Updates to a populated status.Allocation are not allowed. + // Unmodified fields don't need to be validated again and, + // in this particular case, must not be validated again because + // validation for new results is tighter than it was before. if oldStatus.Allocation != nil && status.Allocation != nil { allErrs = append(allErrs, apimachineryvalidation.ValidateImmutableField(status.Allocation, oldStatus.Allocation, fldPath.Child("allocation"))...) + } else if status.Allocation != nil { + allErrs = append(allErrs, validateAllocationResult(status.Allocation, fldPath.Child("allocation"), requestNames)...) } return allErrs @@ -307,11 +311,10 @@ func validateResourceClaimUserReference(ref resource.ResourceClaimConsumerRefere return allErrs } +// validateAllocationResult enforces constraints for *new* results, which in at +// least one case (admin access) are more strict than before. Therefore it +// may not be called to re-validate results which were stored earlier. func validateAllocationResult(allocation *resource.AllocationResult, fldPath *field.Path, requestNames sets.Set[string]) field.ErrorList { - if allocation == nil { - return nil - } - var allErrs field.ErrorList allErrs = append(allErrs, validateDeviceAllocationResult(allocation.Devices, fldPath.Child("devices"), requestNames)...) if allocation.NodeSelector != nil { diff --git a/pkg/apis/resource/validation/validation_resourceclaim_test.go b/pkg/apis/resource/validation/validation_resourceclaim_test.go index b24ff849a89..516fa6f9dfc 100644 --- a/pkg/apis/resource/validation/validation_resourceclaim_test.go +++ b/pkg/apis/resource/validation/validation_resourceclaim_test.go @@ -27,8 +27,11 @@ import ( "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/util/validation/field" + utilfeature "k8s.io/apiserver/pkg/util/feature" + featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/kubernetes/pkg/apis/core" "k8s.io/kubernetes/pkg/apis/resource" + "k8s.io/kubernetes/pkg/features" "k8s.io/utils/pointer" "k8s.io/utils/ptr" ) @@ -408,16 +411,20 @@ func TestValidateClaimStatusUpdate(t *testing.T) { Allocation: &resource.AllocationResult{ Devices: resource.DeviceAllocationResult{ Results: []resource.DeviceRequestAllocationResult{{ - Request: goodName, - Driver: goodName, - Pool: goodName, - Device: goodName, + Request: goodName, + Driver: goodName, + Pool: goodName, + Device: goodName, + AdminAccess: ptr.To(false), // Required for new allocations. }}, }, }, } + validAllocatedClaimOld := validAllocatedClaim.DeepCopy() + validAllocatedClaimOld.Status.Allocation.Devices.Results[0].AdminAccess = nil // Not required in 1.31. scenarios := map[string]struct { + adminAccess bool oldClaim *resource.ResourceClaim update func(claim *resource.ResourceClaim) *resource.ResourceClaim wantFailures field.ErrorList @@ -439,10 +446,11 @@ func TestValidateClaimStatusUpdate(t *testing.T) { claim.Status.Allocation = &resource.AllocationResult{ Devices: resource.DeviceAllocationResult{ Results: []resource.DeviceRequestAllocationResult{{ - Request: goodName, - Driver: goodName, - Pool: goodName, - Device: goodName, + Request: goodName, + Driver: goodName, + Pool: goodName, + Device: goodName, + AdminAccess: ptr.To(false), }}, }, } @@ -459,10 +467,29 @@ func TestValidateClaimStatusUpdate(t *testing.T) { claim.Status.Allocation = &resource.AllocationResult{ Devices: resource.DeviceAllocationResult{ Results: []resource.DeviceRequestAllocationResult{{ - Request: badName, - Driver: goodName, - Pool: goodName, - Device: goodName, + Request: badName, + Driver: goodName, + Pool: goodName, + Device: goodName, + AdminAccess: ptr.To(false), + }}, + }, + } + return claim + }, + }, + "okay-add-allocation-missing-admin-access": { + adminAccess: false, + oldClaim: validClaim, + update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { + claim.Status.Allocation = &resource.AllocationResult{ + Devices: resource.DeviceAllocationResult{ + Results: []resource.DeviceRequestAllocationResult{{ + Request: goodName, + Driver: goodName, + Pool: goodName, + Device: goodName, + AdminAccess: nil, // Intentionally not set. }}, }, } @@ -495,6 +522,20 @@ func TestValidateClaimStatusUpdate(t *testing.T) { return claim }, }, + "add-reservation-old-claim": { + oldClaim: validAllocatedClaimOld, + update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { + for i := 0; i < resource.ResourceClaimReservedForMaxSize; i++ { + claim.Status.ReservedFor = append(claim.Status.ReservedFor, + resource.ResourceClaimConsumerReference{ + Resource: "pods", + Name: fmt.Sprintf("foo-%d", i), + UID: types.UID(fmt.Sprintf("%d", i)), + }) + } + return claim + }, + }, "add-reservation-and-allocation": { oldClaim: validClaim, update: func(claim *resource.ResourceClaim) *resource.ResourceClaim { @@ -652,6 +693,7 @@ func TestValidateClaimStatusUpdate(t *testing.T) { for name, scenario := range scenarios { t.Run(name, func(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAAdminAccess, scenario.adminAccess) scenario.oldClaim.ResourceVersion = "1" errs := ValidateResourceClaimStatusUpdate(scenario.update(scenario.oldClaim.DeepCopy()), scenario.oldClaim) assert.Equal(t, scenario.wantFailures, errs) diff --git a/pkg/apis/resource/zz_generated.deepcopy.go b/pkg/apis/resource/zz_generated.deepcopy.go index 7243a553af6..5eb864e4227 100644 --- a/pkg/apis/resource/zz_generated.deepcopy.go +++ b/pkg/apis/resource/zz_generated.deepcopy.go @@ -144,7 +144,9 @@ func (in *DeviceAllocationResult) DeepCopyInto(out *DeviceAllocationResult) { if in.Results != nil { in, out := &in.Results, &out.Results *out = make([]DeviceRequestAllocationResult, len(*in)) - copy(*out, *in) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } if in.Config != nil { in, out := &in.Config, &out.Config @@ -425,6 +427,11 @@ func (in *DeviceRequest) DeepCopyInto(out *DeviceRequest) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.AdminAccess != nil { + in, out := &in.AdminAccess, &out.AdminAccess + *out = new(bool) + **out = **in + } return } @@ -441,6 +448,11 @@ func (in *DeviceRequest) DeepCopy() *DeviceRequest { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DeviceRequestAllocationResult) DeepCopyInto(out *DeviceRequestAllocationResult) { *out = *in + if in.AdminAccess != nil { + in, out := &in.AdminAccess, &out.AdminAccess + *out = new(bool) + **out = **in + } return } diff --git a/pkg/controller/resourceclaim/controller.go b/pkg/controller/resourceclaim/controller.go index e2f3aa3dbbc..acfdbc679b0 100644 --- a/pkg/controller/resourceclaim/controller.go +++ b/pkg/controller/resourceclaim/controller.go @@ -71,6 +71,9 @@ const ( // Controller creates ResourceClaims for ResourceClaimTemplates in a pod spec. type Controller struct { + // adminAccessEnabled matches the DRAAdminAccess feature gate state. + adminAccessEnabled bool + // kubeClient is the kube API client used to communicate with the API // server. kubeClient clientset.Interface @@ -118,20 +121,22 @@ const ( // NewController creates a ResourceClaim controller. func NewController( logger klog.Logger, + adminAccessEnabled bool, kubeClient clientset.Interface, podInformer v1informers.PodInformer, claimInformer resourceinformers.ResourceClaimInformer, templateInformer resourceinformers.ResourceClaimTemplateInformer) (*Controller, error) { ec := &Controller{ - kubeClient: kubeClient, - podLister: podInformer.Lister(), - podIndexer: podInformer.Informer().GetIndexer(), - podSynced: podInformer.Informer().HasSynced, - claimLister: claimInformer.Lister(), - claimsSynced: claimInformer.Informer().HasSynced, - templateLister: templateInformer.Lister(), - templatesSynced: templateInformer.Informer().HasSynced, + adminAccessEnabled: adminAccessEnabled, + kubeClient: kubeClient, + podLister: podInformer.Lister(), + podIndexer: podInformer.Informer().GetIndexer(), + podSynced: podInformer.Informer().HasSynced, + claimLister: claimInformer.Lister(), + claimsSynced: claimInformer.Informer().HasSynced, + templateLister: templateInformer.Lister(), + templatesSynced: templateInformer.Informer().HasSynced, queue: workqueue.NewTypedRateLimitingQueueWithConfig( workqueue.DefaultTypedControllerRateLimiter[string](), workqueue.TypedRateLimitingQueueConfig[string]{Name: "resource_claim"}, @@ -612,6 +617,10 @@ func (ec *Controller) handleClaim(ctx context.Context, pod *v1.Pod, podClaim v1. return fmt.Errorf("resource claim template %q: %v", *templateName, err) } + if !ec.adminAccessEnabled && needsAdminAccess(template) { + return errors.New("admin access is requested, but the feature is disabled") + } + // Create the ResourceClaim with pod as owner, with a generated name that uses // - as base. isTrue := true @@ -670,6 +679,15 @@ func (ec *Controller) handleClaim(ctx context.Context, pod *v1.Pod, podClaim v1. return nil } +func needsAdminAccess(claimTemplate *resourceapi.ResourceClaimTemplate) bool { + for _, request := range claimTemplate.Spec.Spec.Devices.Requests { + if ptr.Deref(request.AdminAccess, false) { + return true + } + } + return false +} + // findPodResourceClaim looks for an existing ResourceClaim with the right // annotation (ties it to the pod claim) and the right ownership (ties it to // the pod). diff --git a/pkg/controller/resourceclaim/controller_test.go b/pkg/controller/resourceclaim/controller_test.go index e76497dcfee..54d9b8753ef 100644 --- a/pkg/controller/resourceclaim/controller_test.go +++ b/pkg/controller/resourceclaim/controller_test.go @@ -37,7 +37,6 @@ import ( "k8s.io/client-go/kubernetes/fake" k8stesting "k8s.io/client-go/testing" "k8s.io/component-base/metrics/testutil" - "k8s.io/klog/v2" "k8s.io/kubernetes/pkg/controller" "k8s.io/kubernetes/pkg/controller/resourceclaim/metrics" "k8s.io/kubernetes/test/utils/ktesting" @@ -83,17 +82,18 @@ var ( func TestSyncHandler(t *testing.T) { tests := []struct { - name string - key string - claims []*resourceapi.ResourceClaim - claimsInCache []*resourceapi.ResourceClaim - pods []*v1.Pod - podsLater []*v1.Pod - templates []*resourceapi.ResourceClaimTemplate - expectedClaims []resourceapi.ResourceClaim - expectedStatuses map[string][]v1.PodResourceClaimStatus - expectedError bool - expectedMetrics expectedMetrics + name string + key string + adminAccessEnabled bool + claims []*resourceapi.ResourceClaim + claimsInCache []*resourceapi.ResourceClaim + pods []*v1.Pod + podsLater []*v1.Pod + templates []*resourceapi.ResourceClaimTemplate + expectedClaims []resourceapi.ResourceClaim + expectedStatuses map[string][]v1.PodResourceClaimStatus + expectedError bool + expectedMetrics expectedMetrics }{ { name: "create", @@ -390,7 +390,7 @@ func TestSyncHandler(t *testing.T) { claimInformer := informerFactory.Resource().V1alpha3().ResourceClaims() templateInformer := informerFactory.Resource().V1alpha3().ResourceClaimTemplates() - ec, err := NewController(klog.FromContext(tCtx), fakeKubeClient, podInformer, claimInformer, templateInformer) + ec, err := NewController(tCtx.Logger(), tc.adminAccessEnabled, fakeKubeClient, podInformer, claimInformer, templateInformer) if err != nil { t.Fatalf("error creating ephemeral controller : %v", err) } @@ -465,7 +465,7 @@ func TestResourceClaimEventHandler(t *testing.T) { templateInformer := informerFactory.Resource().V1alpha3().ResourceClaimTemplates() claimClient := fakeKubeClient.ResourceV1alpha3().ResourceClaims(testNamespace) - _, err := NewController(tCtx.Logger(), fakeKubeClient, podInformer, claimInformer, templateInformer) + _, err := NewController(tCtx.Logger(), false /* admin access */, fakeKubeClient, podInformer, claimInformer, templateInformer) tCtx.ExpectNoError(err, "creating ephemeral controller") informerFactory.Start(tCtx.Done()) diff --git a/pkg/controlplane/apiserver/options/validation.go b/pkg/controlplane/apiserver/options/validation.go index 4838d1f29ef..2f001025d4e 100644 --- a/pkg/controlplane/apiserver/options/validation.go +++ b/pkg/controlplane/apiserver/options/validation.go @@ -77,6 +77,13 @@ func validateNodeSelectorAuthorizationFeature() []error { return nil } +func validateDRAAdminAccessFeature() []error { + if utilfeature.DefaultFeatureGate.Enabled(features.DRAAdminAccess) && !utilfeature.DefaultFeatureGate.Enabled(features.DynamicResourceAllocation) { + return []error{fmt.Errorf("DRAAdminAccess feature requires DynamicResourceAllocation feature to be enabled")} + } + return nil +} + func validateUnknownVersionInteroperabilityProxyFeature() []error { if utilfeature.DefaultFeatureGate.Enabled(features.UnknownVersionInteroperabilityProxy) { if utilfeature.DefaultFeatureGate.Enabled(genericfeatures.StorageVersionAPI) { @@ -121,6 +128,7 @@ func (s *Options) Validate() []error { errs = append(errs, validateUnknownVersionInteroperabilityProxyFeature()...) errs = append(errs, validateUnknownVersionInteroperabilityProxyFlags(s)...) errs = append(errs, validateNodeSelectorAuthorizationFeature()...) + errs = append(errs, validateDRAAdminAccessFeature()...) return errs } diff --git a/pkg/features/kube_features.go b/pkg/features/kube_features.go index af2a4ec9a4a..e7296c575bd 100644 --- a/pkg/features/kube_features.go +++ b/pkg/features/kube_features.go @@ -203,6 +203,18 @@ const ( // DisableNodeKubeProxyVersion disable the status.nodeInfo.kubeProxyVersion field of v1.Node DisableNodeKubeProxyVersion featuregate.Feature = "DisableNodeKubeProxyVersion" + // owner: @pohly + // kep: http://kep.k8s.io/4381 + // + // Enables support for requesting admin access in a ResourceClaim. + // Admin access is granted even if a device is already in use and, + // depending on the DRA driver, may enable additional permissions + // when a container uses the allocated device. + // + // This feature gate is currently defined in KEP #4381. The intent + // is to move it into a separate KEP. + DRAAdminAccess featuregate.Feature = "DRAAdminAccess" + // owner: @pohly // kep: http://kep.k8s.io/4381 // diff --git a/pkg/features/versioned_kube_features.go b/pkg/features/versioned_kube_features.go index 5d1572c902f..96e1a426417 100644 --- a/pkg/features/versioned_kube_features.go +++ b/pkg/features/versioned_kube_features.go @@ -173,6 +173,10 @@ var defaultVersionedKubernetesFeatureGates = map[featuregate.Feature]featuregate {Version: version.MustParse("1.31"), Default: false, PreRelease: featuregate.Deprecated}, }, + DRAAdminAccess: { + {Version: version.MustParse("1.32"), Default: false, PreRelease: featuregate.Alpha}, + }, + DynamicResourceAllocation: { {Version: version.MustParse("1.26"), Default: false, PreRelease: featuregate.Alpha}, }, diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go index 19733195c6e..41e0b0fa592 100644 --- a/pkg/generated/openapi/zz_generated.openapi.go +++ b/pkg/generated/openapi/zz_generated.openapi.go @@ -46504,8 +46504,7 @@ func schema_k8sio_api_resource_v1alpha3_DeviceRequest(ref common.ReferenceCallba }, "adminAccess": { SchemaProps: spec.SchemaProps{ - Description: "AdminAccess indicates that this is a claim for administrative access to the device(s). Claims with AdminAccess are expected to be used for monitoring or other management services for a device. They ignore all ordinary claims to the device with respect to access modes and any resource allocations.", - Default: false, + Description: "AdminAccess indicates that this is a claim for administrative access to the device(s). Claims with AdminAccess are expected to be used for monitoring or other management services for a device. They ignore all ordinary claims to the device with respect to access modes and any resource allocations.\n\nThis is an alpha field and requires enabling the DRAAdminAccess feature gate. Admin access is disabled if this field is unset or set to false, otherwise it is enabled.", Type: []string{"boolean"}, Format: "", }, @@ -46558,6 +46557,13 @@ func schema_k8sio_api_resource_v1alpha3_DeviceRequestAllocationResult(ref common Format: "", }, }, + "adminAccess": { + SchemaProps: spec.SchemaProps{ + Description: "AdminAccess indicates that this device was allocated for administrative access. See the corresponding request field for a definition of mode.\n\nThis is an alpha field and requires enabling the DRAAdminAccess feature gate. Admin access is disabled if this field is unset or set to false, otherwise it is enabled.", + Type: []string{"boolean"}, + Format: "", + }, + }, }, Required: []string{"request", "driver", "pool", "device"}, }, diff --git a/pkg/quota/v1/evaluator/core/resource_claims_test.go b/pkg/quota/v1/evaluator/core/resource_claims_test.go index 2f1b59c913e..244dbb2c28e 100644 --- a/pkg/quota/v1/evaluator/core/resource_claims_test.go +++ b/pkg/quota/v1/evaluator/core/resource_claims_test.go @@ -25,6 +25,7 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" api "k8s.io/kubernetes/pkg/apis/resource" + "k8s.io/utils/ptr" ) func testResourceClaim(name string, namespace string, spec api.ResourceClaimSpec) *api.ResourceClaim { @@ -101,7 +102,7 @@ func TestResourceClaimEvaluatorUsage(t *testing.T) { claim: func() *api.ResourceClaim { claim := validClaim.DeepCopy() // Admins are *not* exempt from quota. - claim.Spec.Devices.Requests[0].AdminAccess = true + claim.Spec.Devices.Requests[0].AdminAccess = ptr.To(true) return claim }(), usage: corev1.ResourceList{ diff --git a/pkg/registry/resource/resourceclaim/strategy.go b/pkg/registry/resource/resourceclaim/strategy.go index 507446f9a07..1aa2f47e918 100644 --- a/pkg/registry/resource/resourceclaim/strategy.go +++ b/pkg/registry/resource/resourceclaim/strategy.go @@ -28,9 +28,11 @@ import ( "k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/storage" "k8s.io/apiserver/pkg/storage/names" + utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/apis/resource" "k8s.io/kubernetes/pkg/apis/resource/validation" + "k8s.io/kubernetes/pkg/features" "sigs.k8s.io/structured-merge-diff/v4/fieldpath" ) @@ -172,4 +174,49 @@ func toSelectableFields(claim *resource.ResourceClaim) fields.Set { // dropDisabledFields removes fields which are covered by a feature gate. func dropDisabledFields(newClaim, oldClaim *resource.ResourceClaim) { + dropDisabledDRAAdminAccessFields(newClaim, oldClaim) +} + +func dropDisabledDRAAdminAccessFields(newClaim, oldClaim *resource.ResourceClaim) { + if utilfeature.DefaultFeatureGate.Enabled(features.DRAAdminAccess) { + // No need to drop anything. + return + } + + for i := range newClaim.Spec.Devices.Requests { + if newClaim.Spec.Devices.Requests[i].AdminAccess != nil && !draAdminAccessFeatureInUse(oldClaim) { + newClaim.Spec.Devices.Requests[i].AdminAccess = nil + } + } + + if newClaim.Status.Allocation == nil { + return + } + for i := range newClaim.Status.Allocation.Devices.Results { + if newClaim.Status.Allocation.Devices.Results[i].AdminAccess != nil && !draAdminAccessFeatureInUse(oldClaim) { + newClaim.Status.Allocation.Devices.Results[i].AdminAccess = nil + } + } +} + +func draAdminAccessFeatureInUse(claim *resource.ResourceClaim) bool { + if claim == nil { + return false + } + + for _, request := range claim.Spec.Devices.Requests { + if request.AdminAccess != nil { + return true + } + } + + if allocation := claim.Status.Allocation; allocation != nil { + for _, result := range allocation.Devices.Results { + if result.AdminAccess != nil { + return true + } + } + } + + return false } diff --git a/pkg/registry/resource/resourceclaim/strategy_test.go b/pkg/registry/resource/resourceclaim/strategy_test.go index fb7b3721368..3c43558d18f 100644 --- a/pkg/registry/resource/resourceclaim/strategy_test.go +++ b/pkg/registry/resource/resourceclaim/strategy_test.go @@ -23,7 +23,11 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + utilfeature "k8s.io/apiserver/pkg/util/feature" + featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/kubernetes/pkg/apis/resource" + "k8s.io/kubernetes/pkg/features" + "k8s.io/utils/ptr" ) var obj = &resource.ResourceClaim{ @@ -31,6 +35,102 @@ var obj = &resource.ResourceClaim{ Name: "valid-claim", Namespace: "default", }, + Spec: resource.ResourceClaimSpec{ + Devices: resource.DeviceClaim{ + Requests: []resource.DeviceRequest{ + { + Name: "req-0", + DeviceClassName: "class", + AllocationMode: resource.DeviceAllocationModeAll, + }, + }, + }, + }, +} + +var objWithStatus = &resource.ResourceClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "valid-claim", + Namespace: "default", + }, + Spec: resource.ResourceClaimSpec{ + Devices: resource.DeviceClaim{ + Requests: []resource.DeviceRequest{ + { + Name: "req-0", + DeviceClassName: "class", + AllocationMode: resource.DeviceAllocationModeAll, + }, + }, + }, + }, + Status: resource.ResourceClaimStatus{ + Allocation: &resource.AllocationResult{ + Devices: resource.DeviceAllocationResult{ + Results: []resource.DeviceRequestAllocationResult{ + { + Request: "req-0", + Driver: "dra.example.com", + Pool: "pool-0", + Device: "device-0", + }, + }, + }, + }, + }, +} + +var objWithAdminAccess = &resource.ResourceClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "valid-claim", + Namespace: "default", + }, + Spec: resource.ResourceClaimSpec{ + Devices: resource.DeviceClaim{ + Requests: []resource.DeviceRequest{ + { + Name: "req-0", + DeviceClassName: "class", + AllocationMode: resource.DeviceAllocationModeAll, + AdminAccess: ptr.To(true), + }, + }, + }, + }, +} + +var objWithAdminAccessStatus = &resource.ResourceClaim{ + ObjectMeta: metav1.ObjectMeta{ + Name: "valid-claim", + Namespace: "default", + }, + Spec: resource.ResourceClaimSpec{ + Devices: resource.DeviceClaim{ + Requests: []resource.DeviceRequest{ + { + Name: "req-0", + DeviceClassName: "class", + AllocationMode: resource.DeviceAllocationModeAll, + AdminAccess: ptr.To(true), + }, + }, + }, + }, + Status: resource.ResourceClaimStatus{ + Allocation: &resource.AllocationResult{ + Devices: resource.DeviceAllocationResult{ + Results: []resource.DeviceRequestAllocationResult{ + { + Request: "req-0", + Driver: "dra.example.com", + Pool: "pool-0", + Device: "device-0", + AdminAccess: ptr.To(true), + }, + }, + }, + }, + }, } func TestStrategy(t *testing.T) { @@ -47,6 +147,7 @@ func TestStrategyCreate(t *testing.T) { testcases := map[string]struct { obj *resource.ResourceClaim + adminAccess bool expectValidationError bool expectObj *resource.ResourceClaim }{ @@ -62,10 +163,22 @@ func TestStrategyCreate(t *testing.T) { }(), expectValidationError: true, }, + "drop-fields-admin-access": { + obj: objWithAdminAccess, + adminAccess: false, + expectObj: obj, + }, + "keep-fields-admin-access": { + obj: objWithAdminAccess, + adminAccess: true, + expectObj: objWithAdminAccess, + }, } for name, tc := range testcases { t.Run(name, func(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAAdminAccess, tc.adminAccess) + obj := tc.obj.DeepCopy() Strategy.PrepareForCreate(ctx, obj) if errs := Strategy.Validate(ctx, obj); len(errs) != 0 { @@ -91,6 +204,7 @@ func TestStrategyUpdate(t *testing.T) { testcases := map[string]struct { oldObj *resource.ResourceClaim newObj *resource.ResourceClaim + adminAccess bool expectValidationError bool expectObj *resource.ResourceClaim }{ @@ -108,10 +222,30 @@ func TestStrategyUpdate(t *testing.T) { }(), expectValidationError: true, }, + "drop-fields-admin-access": { + oldObj: obj, + newObj: objWithAdminAccess, + adminAccess: false, + expectObj: obj, + }, + "keep-fields-admin-access": { + oldObj: obj, + newObj: objWithAdminAccess, + adminAccess: true, + expectValidationError: true, // Spec is immutable. + }, + "keep-existing-fields-admin-access": { + oldObj: objWithAdminAccess, + newObj: objWithAdminAccess, + adminAccess: true, + expectObj: objWithAdminAccess, + }, } for name, tc := range testcases { t.Run(name, func(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAAdminAccess, tc.adminAccess) + oldObj := tc.oldObj.DeepCopy() newObj := tc.newObj.DeepCopy() newObj.ResourceVersion = "4" @@ -143,6 +277,7 @@ func TestStatusStrategyUpdate(t *testing.T) { testcases := map[string]struct { oldObj *resource.ResourceClaim newObj *resource.ResourceClaim + adminAccess bool expectValidationError bool expectObj *resource.ResourceClaim }{ @@ -172,10 +307,52 @@ func TestStatusStrategyUpdate(t *testing.T) { }(), expectObj: obj, }, + "drop-fields-admin-access": { + oldObj: obj, + newObj: objWithAdminAccessStatus, + adminAccess: false, + expectObj: objWithStatus, + }, + "keep-fields-admin-access": { + oldObj: obj, + newObj: objWithAdminAccessStatus, + adminAccess: true, + expectObj: func() *resource.ResourceClaim { + expectObj := objWithAdminAccessStatus.DeepCopy() + // Spec remains unchanged. + expectObj.Spec = obj.Spec + return expectObj + }(), + }, + "keep-fields-admin-access-because-of-spec": { + oldObj: objWithAdminAccess, + newObj: objWithAdminAccessStatus, + adminAccess: false, + expectObj: objWithAdminAccessStatus, + }, + // Normally a claim without admin access in the spec shouldn't + // have one in the status either, but it's not invalid and thus + // let's test this. + "keep-fields-admin-access-because-of-status": { + oldObj: func() *resource.ResourceClaim { + oldObj := objWithAdminAccessStatus.DeepCopy() + oldObj.Spec.Devices.Requests[0].AdminAccess = ptr.To(false) + return oldObj + }(), + newObj: objWithAdminAccessStatus, + adminAccess: false, + expectObj: func() *resource.ResourceClaim { + oldObj := objWithAdminAccessStatus.DeepCopy() + oldObj.Spec.Devices.Requests[0].AdminAccess = ptr.To(false) + return oldObj + }(), + }, } for name, tc := range testcases { t.Run(name, func(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAAdminAccess, tc.adminAccess) + oldObj := tc.oldObj.DeepCopy() newObj := tc.newObj.DeepCopy() newObj.ResourceVersion = "4" diff --git a/pkg/registry/resource/resourceclaimtemplate/strategy.go b/pkg/registry/resource/resourceclaimtemplate/strategy.go index 02cda68d89d..7f43c79919e 100644 --- a/pkg/registry/resource/resourceclaimtemplate/strategy.go +++ b/pkg/registry/resource/resourceclaimtemplate/strategy.go @@ -26,9 +26,11 @@ import ( "k8s.io/apimachinery/pkg/util/validation/field" "k8s.io/apiserver/pkg/registry/generic" "k8s.io/apiserver/pkg/storage/names" + utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/kubernetes/pkg/api/legacyscheme" "k8s.io/kubernetes/pkg/apis/resource" "k8s.io/kubernetes/pkg/apis/resource/validation" + "k8s.io/kubernetes/pkg/features" ) // resourceClaimTemplateStrategy implements behavior for ResourceClaimTemplate objects @@ -44,6 +46,8 @@ func (resourceClaimTemplateStrategy) NamespaceScoped() bool { } func (resourceClaimTemplateStrategy) PrepareForCreate(ctx context.Context, obj runtime.Object) { + claimTemplate := obj.(*resource.ResourceClaimTemplate) + dropDisabledFields(claimTemplate, nil) } func (resourceClaimTemplateStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList { @@ -63,6 +67,8 @@ func (resourceClaimTemplateStrategy) AllowCreateOnUpdate() bool { } func (resourceClaimTemplateStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object) { + claimTemplate, oldClaimTemplate := obj.(*resource.ResourceClaimTemplate), old.(*resource.ResourceClaimTemplate) + dropDisabledFields(claimTemplate, oldClaimTemplate) } func (resourceClaimTemplateStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object) field.ErrorList { @@ -92,3 +98,34 @@ func toSelectableFields(template *resource.ResourceClaimTemplate) fields.Set { fields := generic.ObjectMetaFieldsSet(&template.ObjectMeta, true) return fields } + +func dropDisabledFields(newClaimTemplate, oldClaimTemplate *resource.ResourceClaimTemplate) { + dropDisabledDRAAdminAccessFields(newClaimTemplate, oldClaimTemplate) +} + +func dropDisabledDRAAdminAccessFields(newClaimTemplate, oldClaimTemplate *resource.ResourceClaimTemplate) { + if utilfeature.DefaultFeatureGate.Enabled(features.DRAAdminAccess) { + // No need to drop anything. + return + } + + for i := range newClaimTemplate.Spec.Spec.Devices.Requests { + if newClaimTemplate.Spec.Spec.Devices.Requests[i].AdminAccess != nil && !draAdminAccessFeatureInUse(oldClaimTemplate) { + newClaimTemplate.Spec.Spec.Devices.Requests[i].AdminAccess = nil + } + } +} + +func draAdminAccessFeatureInUse(claimTemplate *resource.ResourceClaimTemplate) bool { + if claimTemplate == nil { + return false + } + + for _, request := range claimTemplate.Spec.Spec.Devices.Requests { + if request.AdminAccess != nil { + return true + } + } + + return false +} diff --git a/pkg/registry/resource/resourceclaimtemplate/strategy_test.go b/pkg/registry/resource/resourceclaimtemplate/strategy_test.go index 14c9eddaeba..b48f78d7bee 100644 --- a/pkg/registry/resource/resourceclaimtemplate/strategy_test.go +++ b/pkg/registry/resource/resourceclaimtemplate/strategy_test.go @@ -19,16 +19,56 @@ package resourceclaimtemplate import ( "testing" + "github.com/stretchr/testify/assert" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" genericapirequest "k8s.io/apiserver/pkg/endpoints/request" + utilfeature "k8s.io/apiserver/pkg/util/feature" + featuregatetesting "k8s.io/component-base/featuregate/testing" "k8s.io/kubernetes/pkg/apis/resource" + "k8s.io/kubernetes/pkg/features" + "k8s.io/utils/ptr" ) -var resourceClaimTemplate = &resource.ResourceClaimTemplate{ +var obj = &resource.ResourceClaimTemplate{ ObjectMeta: metav1.ObjectMeta{ Name: "valid-claim-template", Namespace: "default", }, + Spec: resource.ResourceClaimTemplateSpec{ + Spec: resource.ResourceClaimSpec{ + Devices: resource.DeviceClaim{ + Requests: []resource.DeviceRequest{ + { + Name: "req-0", + DeviceClassName: "class", + AllocationMode: resource.DeviceAllocationModeAll, + }, + }, + }, + }, + }, +} + +var objWithAdminAccess = &resource.ResourceClaimTemplate{ + ObjectMeta: metav1.ObjectMeta{ + Name: "valid-claim-template", + Namespace: "default", + }, + Spec: resource.ResourceClaimTemplateSpec{ + Spec: resource.ResourceClaimSpec{ + Devices: resource.DeviceClaim{ + Requests: []resource.DeviceRequest{ + { + Name: "req-0", + DeviceClassName: "class", + AllocationMode: resource.DeviceAllocationModeAll, + AdminAccess: ptr.To(true), + }, + }, + }, + }, + }, } func TestClaimTemplateStrategy(t *testing.T) { @@ -42,19 +82,64 @@ func TestClaimTemplateStrategy(t *testing.T) { func TestClaimTemplateStrategyCreate(t *testing.T) { ctx := genericapirequest.NewDefaultContext() - resourceClaimTemplate := resourceClaimTemplate.DeepCopy() - Strategy.PrepareForCreate(ctx, resourceClaimTemplate) - errs := Strategy.Validate(ctx, resourceClaimTemplate) - if len(errs) != 0 { - t.Errorf("unexpected error validating for create %v", errs) + testcases := map[string]struct { + obj *resource.ResourceClaimTemplate + adminAccess bool + expectValidationError bool + expectObj *resource.ResourceClaimTemplate + }{ + "simple": { + obj: obj, + expectObj: obj, + }, + "validation-error": { + obj: func() *resource.ResourceClaimTemplate { + obj := obj.DeepCopy() + obj.Name = "%#@$%$" + return obj + }(), + expectValidationError: true, + }, + "drop-fields-admin-access": { + obj: objWithAdminAccess, + adminAccess: false, + expectObj: obj, + }, + "keep-fields-admin-access": { + obj: objWithAdminAccess, + adminAccess: true, + expectObj: objWithAdminAccess, + }, + } + + for name, tc := range testcases { + t.Run(name, func(t *testing.T) { + featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAAdminAccess, tc.adminAccess) + + obj := tc.obj.DeepCopy() + Strategy.PrepareForCreate(ctx, obj) + if errs := Strategy.Validate(ctx, obj); len(errs) != 0 { + if !tc.expectValidationError { + t.Fatalf("unexpected validation errors: %q", errs) + } + return + } else if tc.expectValidationError { + t.Fatal("expected validation error(s), got none") + } + if warnings := Strategy.WarningsOnCreate(ctx, obj); len(warnings) != 0 { + t.Fatalf("unexpected warnings: %q", warnings) + } + Strategy.Canonicalize(obj) + assert.Equal(t, tc.expectObj, obj) + }) } } func TestClaimTemplateStrategyUpdate(t *testing.T) { t.Run("no-changes-okay", func(t *testing.T) { ctx := genericapirequest.NewDefaultContext() - resourceClaimTemplate := resourceClaimTemplate.DeepCopy() + resourceClaimTemplate := obj.DeepCopy() newClaimTemplate := resourceClaimTemplate.DeepCopy() newClaimTemplate.ResourceVersion = "4" @@ -67,7 +152,7 @@ func TestClaimTemplateStrategyUpdate(t *testing.T) { t.Run("name-change-not-allowed", func(t *testing.T) { ctx := genericapirequest.NewDefaultContext() - resourceClaimTemplate := resourceClaimTemplate.DeepCopy() + resourceClaimTemplate := obj.DeepCopy() newClaimTemplate := resourceClaimTemplate.DeepCopy() newClaimTemplate.Name = "valid-class-2" newClaimTemplate.ResourceVersion = "4" diff --git a/pkg/scheduler/framework/plugins/dynamicresources/dynamicresources.go b/pkg/scheduler/framework/plugins/dynamicresources/dynamicresources.go index 27ea690296e..58505bb3880 100644 --- a/pkg/scheduler/framework/plugins/dynamicresources/dynamicresources.go +++ b/pkg/scheduler/framework/plugins/dynamicresources/dynamicresources.go @@ -103,6 +103,7 @@ type informationForClaim struct { // dynamicResources is a plugin that ensures that ResourceClaims are allocated. type dynamicResources struct { enabled bool + enableAdminAccess bool enableSchedulingQueueHint bool fh framework.Handle @@ -175,6 +176,7 @@ func New(ctx context.Context, plArgs runtime.Object, fh framework.Handle, fts fe pl := &dynamicResources{ enabled: true, + enableAdminAccess: fts.EnableDRAAdminAccess, enableSchedulingQueueHint: fts.EnableSchedulingQueueHint, fh: fh, @@ -527,7 +529,7 @@ func (pl *dynamicResources) PreFilter(ctx context.Context, state *framework.Cycl // // Claims are treated as "allocated" if they are in the assume cache // or currently their allocation is in-flight. - allocator, err := structured.NewAllocator(ctx, allocateClaims, &claimListerForAssumeCache{assumeCache: pl.claimAssumeCache, inFlightAllocations: &pl.inFlightAllocations}, pl.classLister, pl.sliceLister) + allocator, err := structured.NewAllocator(ctx, pl.enableAdminAccess, allocateClaims, &claimListerForAssumeCache{assumeCache: pl.claimAssumeCache, inFlightAllocations: &pl.inFlightAllocations}, pl.classLister, pl.sliceLister) if err != nil { return nil, statusError(logger, err) } diff --git a/pkg/scheduler/framework/plugins/dynamicresources/dynamicresources_test.go b/pkg/scheduler/framework/plugins/dynamicresources/dynamicresources_test.go index 1292f1ac680..2b5e73a5fc8 100644 --- a/pkg/scheduler/framework/plugins/dynamicresources/dynamicresources_test.go +++ b/pkg/scheduler/framework/plugins/dynamicresources/dynamicresources_test.go @@ -178,6 +178,19 @@ func reserve(claim *resourceapi.ResourceClaim, pod *v1.Pod) *resourceapi.Resourc Obj() } +func adminAccess(claim *resourceapi.ResourceClaim) *resourceapi.ResourceClaim { + claim = claim.DeepCopy() + for i := range claim.Spec.Devices.Requests { + claim.Spec.Devices.Requests[i].AdminAccess = ptr.To(true) + } + if claim.Status.Allocation != nil { + for i := range claim.Status.Allocation.Devices.Results { + claim.Status.Allocation.Devices.Results[i].AdminAccess = ptr.To(true) + } + } + return claim +} + func breakCELInClaim(claim *resourceapi.ResourceClaim) *resourceapi.ResourceClaim { claim = claim.DeepCopy() for i := range claim.Spec.Devices.Requests { @@ -556,6 +569,66 @@ func TestPlugin(t *testing.T) { }, }, + "request-admin-access": { + // Because the pending claim asks for admin access, allocation succeeds despite resources + // being exhausted. + pod: podWithClaimName, + claims: []*resourceapi.ResourceClaim{adminAccess(pendingClaim), otherAllocatedClaim}, + classes: []*resourceapi.DeviceClass{deviceClass}, + objs: []apiruntime.Object{workerNodeSlice}, + want: want{ + reserve: result{ + inFlightClaim: adminAccess(allocatedClaim), + }, + prebind: result{ + assumedClaim: reserve(adminAccess(allocatedClaim), podWithClaimName), + changes: change{ + claim: func(claim *resourceapi.ResourceClaim) *resourceapi.ResourceClaim { + if claim.Name == claimName { + claim = claim.DeepCopy() + claim.Finalizers = allocatedClaim.Finalizers + claim.Status = adminAccess(inUseClaim).Status + } + return claim + }, + }, + }, + postbind: result{ + assumedClaim: reserve(adminAccess(allocatedClaim), podWithClaimName), + }, + }, + }, + + "structured-ignore-allocated-admin-access": { + // The allocated claim uses admin access, so a second claim may use + // the same device. + pod: podWithClaimName, + claims: []*resourceapi.ResourceClaim{pendingClaim, adminAccess(otherAllocatedClaim)}, + classes: []*resourceapi.DeviceClass{deviceClass}, + objs: []apiruntime.Object{workerNodeSlice}, + want: want{ + reserve: result{ + inFlightClaim: allocatedClaim, + }, + prebind: result{ + assumedClaim: reserve(allocatedClaim, podWithClaimName), + changes: change{ + claim: func(claim *resourceapi.ResourceClaim) *resourceapi.ResourceClaim { + if claim.Name == claimName { + claim = claim.DeepCopy() + claim.Finalizers = allocatedClaim.Finalizers + claim.Status = inUseClaim.Status + } + return claim + }, + }, + }, + postbind: result{ + assumedClaim: reserve(allocatedClaim, podWithClaimName), + }, + }, + }, + "claim-parameters-CEL-runtime-error": { pod: podWithClaimName, claims: []*resourceapi.ResourceClaim{breakCELInClaim(pendingClaim)}, diff --git a/pkg/scheduler/framework/plugins/feature/feature.go b/pkg/scheduler/framework/plugins/feature/feature.go index af28abfc901..219e9c3e746 100644 --- a/pkg/scheduler/framework/plugins/feature/feature.go +++ b/pkg/scheduler/framework/plugins/feature/feature.go @@ -20,6 +20,7 @@ package feature // This struct allows us to break the dependency of the plugins on // the internal k8s features pkg. type Features struct { + EnableDRAAdminAccess bool EnableDynamicResourceAllocation bool EnableVolumeCapacityPriority bool EnableNodeInclusionPolicyInPodTopologySpread bool diff --git a/pkg/scheduler/framework/plugins/registry.go b/pkg/scheduler/framework/plugins/registry.go index f63ef0bbd4a..6a2e2edbe66 100644 --- a/pkg/scheduler/framework/plugins/registry.go +++ b/pkg/scheduler/framework/plugins/registry.go @@ -46,6 +46,7 @@ import ( // through the WithFrameworkOutOfTreeRegistry option. func NewInTreeRegistry() runtime.Registry { fts := plfeature.Features{ + EnableDRAAdminAccess: feature.DefaultFeatureGate.Enabled(features.DRAAdminAccess), EnableDynamicResourceAllocation: feature.DefaultFeatureGate.Enabled(features.DynamicResourceAllocation), EnableVolumeCapacityPriority: feature.DefaultFeatureGate.Enabled(features.VolumeCapacityPriority), EnableNodeInclusionPolicyInPodTopologySpread: feature.DefaultFeatureGate.Enabled(features.NodeInclusionPolicyInPodTopologySpread), diff --git a/staging/src/k8s.io/api/resource/v1alpha3/generated.pb.go b/staging/src/k8s.io/api/resource/v1alpha3/generated.pb.go index d60033a89c0..c9d4bae7976 100644 --- a/staging/src/k8s.io/api/resource/v1alpha3/generated.pb.go +++ b/staging/src/k8s.io/api/resource/v1alpha3/generated.pb.go @@ -957,122 +957,122 @@ func init() { } var fileDescriptor_66649ee9bbcd89d2 = []byte{ - // 1832 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x19, 0xcd, 0x6f, 0xe4, 0x56, - 0x3d, 0x1e, 0x27, 0x93, 0xc9, 0x6f, 0xf2, 0xb5, 0x6f, 0xa1, 0x64, 0x43, 0x99, 0xd9, 0x75, 0x11, - 0x64, 0xdb, 0xad, 0xa7, 0xbb, 0x2d, 0x6d, 0xa1, 0x1c, 0x88, 0x93, 0x74, 0x95, 0xd5, 0x7e, 0x64, - 0x5f, 0xda, 0x15, 0x0b, 0xa5, 0xf0, 0xe2, 0x79, 0x99, 0x98, 0x78, 0x6c, 0xd7, 0xef, 0x79, 0x68, - 0x2e, 0xa8, 0xe2, 0xc2, 0x6d, 0xc5, 0x3f, 0x80, 0xb8, 0x21, 0x71, 0x82, 0x3f, 0x00, 0x09, 0x24, - 0x90, 0x58, 0x89, 0xcb, 0x4a, 0x70, 0xe8, 0x69, 0xe8, 0x0e, 0xe2, 0xc2, 0x9f, 0x90, 0x13, 0xf2, - 0xf3, 0xf3, 0xe7, 0x8c, 0xb3, 0x9e, 0xaa, 0x44, 0xed, 0x6d, 0xfc, 0xfb, 0xfe, 0xfe, 0xfd, 0xec, - 0x81, 0x6b, 0xc7, 0x6f, 0x32, 0xdd, 0x72, 0x3b, 0xc4, 0xb3, 0x3a, 0x3e, 0x65, 0x6e, 0xe0, 0x9b, - 0xb4, 0x33, 0xb8, 0x4e, 0x6c, 0xef, 0x88, 0xbc, 0xda, 0xe9, 0x51, 0x87, 0xfa, 0x84, 0xd3, 0xae, - 0xee, 0xf9, 0x2e, 0x77, 0xd1, 0xf3, 0x11, 0xb5, 0x4e, 0x3c, 0x4b, 0x8f, 0xa9, 0xf5, 0x98, 0x7a, - 0xfd, 0xe5, 0x9e, 0xc5, 0x8f, 0x82, 0x03, 0xdd, 0x74, 0xfb, 0x9d, 0x9e, 0xdb, 0x73, 0x3b, 0x82, - 0xe9, 0x20, 0x38, 0x14, 0x4f, 0xe2, 0x41, 0xfc, 0x8a, 0x84, 0xad, 0x6b, 0x19, 0xd5, 0xa6, 0xeb, - 0x87, 0x6a, 0x8b, 0x0a, 0xd7, 0x5f, 0x4b, 0x69, 0xfa, 0xc4, 0x3c, 0xb2, 0x1c, 0xea, 0x9f, 0x74, - 0xbc, 0xe3, 0x5e, 0xde, 0xde, 0x69, 0xb8, 0x58, 0xa7, 0x4f, 0x39, 0x99, 0xa4, 0xab, 0x53, 0xc6, - 0xe5, 0x07, 0x0e, 0xb7, 0xfa, 0xe3, 0x6a, 0x5e, 0x7f, 0x16, 0x03, 0x33, 0x8f, 0x68, 0x9f, 0x14, - 0xf9, 0xb4, 0xbf, 0x2b, 0xb0, 0xba, 0x69, 0xdb, 0xae, 0x49, 0xb8, 0xe5, 0x3a, 0x98, 0xb2, 0xc0, - 0xe6, 0xe8, 0xc7, 0x30, 0xdf, 0xa5, 0x03, 0xcb, 0xa4, 0x6c, 0x4d, 0xb9, 0xac, 0x6c, 0x34, 0x6f, - 0xbc, 0xa6, 0x9f, 0x15, 0x6c, 0x7d, 0x5b, 0x10, 0x17, 0xc5, 0x18, 0x2b, 0x8f, 0x87, 0xed, 0x99, - 0xd1, 0xb0, 0x3d, 0x1f, 0xe1, 0x19, 0x8e, 0xa5, 0xa2, 0x07, 0xb0, 0xe8, 0xb8, 0x5d, 0xba, 0x4f, - 0x6d, 0x6a, 0x72, 0xd7, 0x5f, 0x53, 0x85, 0x96, 0xcb, 0x59, 0x2d, 0x61, 0x16, 0xf4, 0xc1, 0x75, - 0xfd, 0x6e, 0x86, 0xce, 0x58, 0x1d, 0x0d, 0xdb, 0x8b, 0x59, 0x08, 0xce, 0xc9, 0xd1, 0x3e, 0x51, - 0xa1, 0x69, 0x10, 0x66, 0x99, 0x91, 0x46, 0xf4, 0x73, 0x00, 0xc2, 0xb9, 0x6f, 0x1d, 0x04, 0x5c, - 0xf8, 0xa2, 0x6e, 0x34, 0x6f, 0x7c, 0xfb, 0x6c, 0x5f, 0x32, 0xec, 0xfa, 0x66, 0xc2, 0xbb, 0xe3, - 0x70, 0xff, 0xc4, 0x78, 0x41, 0x3a, 0x04, 0x29, 0xe2, 0x17, 0xff, 0x6a, 0x2f, 0xdd, 0x0f, 0x88, - 0x6d, 0x1d, 0x5a, 0xb4, 0x7b, 0x97, 0xf4, 0x29, 0xce, 0x68, 0x44, 0x03, 0x68, 0x98, 0xc4, 0x23, - 0xa6, 0xc5, 0x4f, 0xd6, 0x6a, 0x42, 0xfb, 0x1b, 0xd5, 0xb5, 0x6f, 0x49, 0xce, 0x48, 0xf7, 0x15, - 0xa9, 0xbb, 0x11, 0x83, 0xc7, 0x35, 0x27, 0xba, 0xd6, 0x6d, 0x58, 0x29, 0xd8, 0x8e, 0x56, 0x41, - 0x3d, 0xa6, 0x27, 0x22, 0x9f, 0x0b, 0x38, 0xfc, 0x89, 0xb6, 0x60, 0x6e, 0x40, 0xec, 0x80, 0xae, - 0xd5, 0x44, 0xf4, 0x5f, 0xae, 0x94, 0xe3, 0x58, 0x2a, 0x8e, 0x78, 0xbf, 0x53, 0x7b, 0x53, 0x59, - 0x3f, 0x86, 0xa5, 0x9c, 0xad, 0x13, 0x74, 0x6d, 0xe7, 0x75, 0xe9, 0x19, 0x5d, 0x49, 0xb9, 0xea, - 0xde, 0x71, 0x2f, 0xaf, 0xfc, 0x7e, 0x40, 0x1c, 0x6e, 0xf1, 0x93, 0x8c, 0x32, 0xed, 0x26, 0x5c, - 0xd8, 0xda, 0xb9, 0x1d, 0x59, 0x13, 0xe7, 0x1d, 0xdd, 0x00, 0xa0, 0x1f, 0x7a, 0x3e, 0x65, 0xcc, - 0x72, 0x9d, 0x48, 0xaf, 0x81, 0xe2, 0x64, 0xed, 0x24, 0x18, 0x9c, 0xa1, 0xd2, 0x06, 0x50, 0x97, - 0x55, 0x72, 0x19, 0x66, 0x1d, 0xd2, 0xa7, 0x92, 0x6f, 0x51, 0xf2, 0xcd, 0x8a, 0x98, 0x0a, 0x0c, - 0xba, 0x05, 0x73, 0x07, 0x61, 0x66, 0xa4, 0xf9, 0x57, 0x2b, 0x27, 0xd1, 0x58, 0x18, 0x0d, 0xdb, - 0x73, 0x02, 0x80, 0x23, 0x11, 0xda, 0xa3, 0x1a, 0x7c, 0xad, 0xd8, 0x30, 0x5b, 0xae, 0x73, 0x68, - 0xf5, 0x02, 0x5f, 0x3c, 0xa0, 0xef, 0x41, 0x3d, 0x12, 0x29, 0x2d, 0xda, 0x90, 0x16, 0xd5, 0xf7, - 0x05, 0xf4, 0x74, 0xd8, 0x7e, 0xae, 0xc8, 0x1a, 0x61, 0xb0, 0xe4, 0x43, 0x1b, 0xd0, 0xf0, 0xe9, - 0x07, 0x01, 0x65, 0x9c, 0x89, 0xba, 0x5b, 0x30, 0x16, 0xc3, 0xd2, 0xc1, 0x12, 0x86, 0x13, 0x2c, - 0xfa, 0x48, 0x81, 0x8b, 0x51, 0x57, 0xe6, 0x6c, 0x90, 0x1d, 0x79, 0xbd, 0x4a, 0x4d, 0xe4, 0x18, - 0x8d, 0xaf, 0x4a, 0x63, 0x2f, 0x4e, 0x40, 0xe2, 0x49, 0xaa, 0xb4, 0xff, 0x28, 0xf0, 0xdc, 0xe4, - 0x09, 0x82, 0x0e, 0x61, 0xde, 0x17, 0xbf, 0xe2, 0xe6, 0x7d, 0xab, 0x8a, 0x41, 0xd2, 0xcd, 0xf2, - 0x79, 0x14, 0x3d, 0x33, 0x1c, 0x0b, 0x47, 0x26, 0xd4, 0x4d, 0x61, 0x93, 0xec, 0xd2, 0xb7, 0xa6, - 0x9b, 0x77, 0xf9, 0x08, 0x2c, 0xc7, 0xe9, 0x8a, 0xc0, 0x58, 0x8a, 0xd6, 0x7e, 0xa7, 0xc0, 0x4a, - 0xa1, 0x8b, 0x50, 0x0b, 0x54, 0xcb, 0xe1, 0xa2, 0xac, 0xd4, 0x28, 0x47, 0xbb, 0x0e, 0x7f, 0x10, - 0x16, 0x3b, 0x0e, 0x11, 0xe8, 0x0a, 0xcc, 0x1e, 0xb8, 0xae, 0x2d, 0xd2, 0xd1, 0x30, 0x96, 0x46, - 0xc3, 0xf6, 0x82, 0xe1, 0xba, 0x76, 0x44, 0x21, 0x50, 0xe8, 0x9b, 0x50, 0x67, 0xdc, 0xb7, 0x9c, - 0xde, 0xda, 0xac, 0xa8, 0x96, 0x95, 0xd1, 0xb0, 0xdd, 0xdc, 0x17, 0x90, 0x88, 0x4c, 0xa2, 0xd1, - 0x8b, 0x30, 0x3f, 0xa0, 0xbe, 0xe8, 0x90, 0x39, 0x41, 0x29, 0xa6, 0xe9, 0x83, 0x08, 0x14, 0x91, - 0xc6, 0x04, 0xda, 0xef, 0x6b, 0xd0, 0x94, 0x09, 0xb4, 0x89, 0xd5, 0x47, 0x0f, 0x33, 0x05, 0x15, - 0x65, 0xe2, 0xa5, 0x29, 0x32, 0x61, 0xac, 0xc6, 0xc3, 0x6b, 0x42, 0x05, 0x52, 0x68, 0x9a, 0xae, - 0xc3, 0xb8, 0x4f, 0x2c, 0x47, 0x96, 0x6b, 0x7e, 0x40, 0x9c, 0x55, 0x78, 0x92, 0xcd, 0xb8, 0x28, - 0x15, 0x34, 0x53, 0x18, 0xc3, 0x59, 0xb9, 0xe8, 0xfd, 0x24, 0xc5, 0xaa, 0xd0, 0xf0, 0x7a, 0x25, - 0x0d, 0xa1, 0xf3, 0xd5, 0xb2, 0xfb, 0x37, 0x05, 0xd6, 0xca, 0x98, 0x72, 0xfd, 0xa8, 0x7c, 0xaa, - 0x7e, 0xac, 0x9d, 0x5f, 0x3f, 0xfe, 0x59, 0xc9, 0xe4, 0x9e, 0x31, 0xf4, 0x13, 0x68, 0x84, 0x67, - 0x4a, 0x97, 0x70, 0x22, 0xcf, 0x81, 0x57, 0xce, 0x1a, 0xdf, 0x4c, 0x0f, 0xa9, 0xc3, 0xd5, 0x7d, - 0xef, 0xe0, 0xa7, 0xd4, 0xe4, 0x77, 0x28, 0x27, 0xe9, 0x30, 0x4e, 0x61, 0x38, 0x91, 0x8a, 0xee, - 0xc1, 0x2c, 0xf3, 0xa8, 0x39, 0xcd, 0x22, 0x12, 0xa6, 0xed, 0x7b, 0xd4, 0x4c, 0xe7, 0x75, 0xf8, - 0x84, 0x85, 0x20, 0xed, 0xd7, 0xd9, 0x64, 0x30, 0x96, 0x4f, 0x46, 0x59, 0x88, 0x95, 0xf3, 0x0b, - 0xf1, 0x9f, 0x92, 0x51, 0x20, 0xec, 0xbb, 0x6d, 0x31, 0x8e, 0xde, 0x1b, 0x0b, 0xb3, 0x5e, 0x2d, - 0xcc, 0x21, 0xb7, 0x08, 0x72, 0xd2, 0x65, 0x31, 0x24, 0x13, 0xe2, 0xbb, 0x30, 0x67, 0x71, 0xda, - 0x8f, 0xfb, 0xeb, 0x6a, 0xe5, 0x18, 0x1b, 0x4b, 0x52, 0xea, 0xdc, 0x6e, 0xc8, 0x8f, 0x23, 0x31, - 0xda, 0x93, 0xbc, 0x07, 0x61, 0xec, 0xd1, 0x8f, 0x60, 0x81, 0xc9, 0x8d, 0x1c, 0x4f, 0x89, 0x6b, - 0x55, 0xf4, 0x24, 0xe7, 0xdd, 0x05, 0xa9, 0x6a, 0x21, 0x86, 0x30, 0x9c, 0x4a, 0xcc, 0x74, 0x70, - 0x6d, 0xaa, 0x0e, 0x2e, 0xe4, 0xbf, 0xb4, 0x83, 0x7d, 0x98, 0x94, 0x40, 0xf4, 0x43, 0xa8, 0xbb, - 0x1e, 0xf9, 0x20, 0xa0, 0x32, 0x2b, 0xcf, 0xb8, 0xe0, 0xee, 0x09, 0xda, 0x49, 0x65, 0x02, 0xa1, - 0xce, 0x08, 0x8d, 0xa5, 0x48, 0xed, 0x91, 0x02, 0xab, 0xc5, 0x61, 0x36, 0xc5, 0xb4, 0xd8, 0x83, - 0xe5, 0x3e, 0xe1, 0xe6, 0x51, 0xb2, 0x50, 0x44, 0x0b, 0x2d, 0x18, 0x1b, 0xa3, 0x61, 0x7b, 0xf9, - 0x4e, 0x0e, 0x73, 0x3a, 0x6c, 0xa3, 0xb7, 0x03, 0xdb, 0x3e, 0xc9, 0xdf, 0x8c, 0x05, 0x7e, 0xed, - 0x97, 0x2a, 0x2c, 0xe5, 0x66, 0x77, 0x85, 0xeb, 0x68, 0x13, 0x56, 0xba, 0x69, 0xb0, 0x43, 0x84, - 0x34, 0xe3, 0x2b, 0x92, 0x38, 0x5b, 0x29, 0x82, 0xaf, 0x48, 0x9f, 0x2f, 0x1d, 0xf5, 0x33, 0x2f, - 0x9d, 0x07, 0xb0, 0x4c, 0x92, 0x6d, 0x7d, 0xc7, 0xed, 0x52, 0xb9, 0x2b, 0x75, 0xc9, 0xb5, 0xbc, - 0x99, 0xc3, 0x9e, 0x0e, 0xdb, 0x5f, 0x2a, 0xee, 0xf8, 0x10, 0x8e, 0x0b, 0x52, 0xd0, 0x0b, 0x30, - 0x67, 0xba, 0x81, 0xc3, 0xc5, 0x42, 0x55, 0xd3, 0x56, 0xd9, 0x0a, 0x81, 0x38, 0xc2, 0xa1, 0x6f, - 0x41, 0x93, 0x74, 0xfb, 0x96, 0xb3, 0x69, 0x9a, 0x94, 0xb1, 0xb5, 0xba, 0x58, 0xe5, 0xc9, 0xc2, - 0xda, 0x4c, 0x51, 0x38, 0x4b, 0xa7, 0xfd, 0x51, 0x89, 0xef, 0xc4, 0x92, 0x7b, 0x06, 0x5d, 0x0d, - 0xaf, 0x23, 0x81, 0x92, 0xc9, 0xc9, 0x1c, 0x38, 0x02, 0x8c, 0x63, 0x3c, 0xfa, 0x06, 0xd4, 0xbb, - 0xbe, 0x35, 0xa0, 0xbe, 0xcc, 0x4c, 0xd2, 0x03, 0xdb, 0x02, 0x8a, 0x25, 0x36, 0x4c, 0xb6, 0x17, - 0xdf, 0x1b, 0x99, 0x64, 0xef, 0xb9, 0xae, 0x8d, 0x05, 0x46, 0x48, 0x12, 0x56, 0xc9, 0x10, 0xa6, - 0x92, 0x22, 0x5b, 0x25, 0x56, 0x7b, 0x0f, 0x96, 0x0b, 0x47, 0xfa, 0x2d, 0x50, 0x4d, 0x6a, 0xcb, - 0x2e, 0xea, 0x9c, 0x9d, 0xdd, 0xb1, 0x13, 0xdf, 0x98, 0x1f, 0x0d, 0xdb, 0xea, 0xd6, 0xce, 0x6d, - 0x1c, 0x0a, 0xd1, 0x7e, 0xab, 0xc0, 0xa5, 0xd2, 0x4e, 0xcb, 0x78, 0xab, 0x9c, 0xe9, 0x2d, 0x01, - 0xf0, 0x88, 0x4f, 0xfa, 0x94, 0x53, 0x9f, 0x4d, 0xd8, 0x3e, 0xf9, 0xa1, 0x2b, 0xdf, 0xa4, 0x75, - 0x4c, 0x7e, 0xb6, 0xf3, 0x21, 0xa7, 0x4e, 0x78, 0x28, 0xa5, 0x8b, 0x6d, 0x2f, 0x11, 0x84, 0x33, - 0x42, 0xb5, 0xdf, 0xd4, 0x60, 0x09, 0x4b, 0xf7, 0xa2, 0x53, 0xea, 0xff, 0xbf, 0x4e, 0xef, 0xe7, - 0xd6, 0xe9, 0x33, 0x22, 0x9d, 0x33, 0xae, 0x6c, 0xa1, 0xa2, 0x87, 0xe1, 0x91, 0x49, 0x78, 0xc0, - 0xaa, 0xbd, 0x18, 0xe4, 0x85, 0x0a, 0xc6, 0x34, 0x09, 0xd1, 0x33, 0x96, 0x02, 0xb5, 0x91, 0x02, - 0xad, 0x1c, 0x7d, 0x38, 0x09, 0x83, 0x3e, 0xf5, 0x31, 0x3d, 0xa4, 0x3e, 0x75, 0x4c, 0x8a, 0xae, - 0x41, 0x83, 0x78, 0xd6, 0x4d, 0xdf, 0x0d, 0x3c, 0x99, 0xd1, 0x64, 0xd5, 0x6d, 0xee, 0xed, 0x0a, - 0x38, 0x4e, 0x28, 0x42, 0xea, 0xd8, 0x22, 0x59, 0xc7, 0x99, 0xf3, 0x33, 0x82, 0xe3, 0x84, 0x22, - 0x19, 0x6f, 0xb3, 0xa5, 0xe3, 0xcd, 0x00, 0x35, 0xb0, 0xba, 0xf2, 0x66, 0x7e, 0x45, 0x12, 0xa8, - 0xef, 0xee, 0x6e, 0x9f, 0x0e, 0xdb, 0x57, 0xca, 0x3e, 0xbb, 0xf0, 0x13, 0x8f, 0x32, 0xfd, 0xdd, - 0xdd, 0x6d, 0x1c, 0x32, 0x6b, 0x7f, 0x51, 0xe0, 0x42, 0xce, 0xc9, 0x73, 0x58, 0xf9, 0x7b, 0xf9, - 0x95, 0xff, 0xd2, 0x14, 0x29, 0x2b, 0x59, 0xfa, 0x56, 0xc1, 0x09, 0xb1, 0xf5, 0xdf, 0x29, 0x7e, - 0x2c, 0xba, 0x5a, 0xf9, 0xb2, 0x2e, 0xff, 0x42, 0xa4, 0xfd, 0x57, 0x81, 0x8b, 0x13, 0xaa, 0x08, - 0xbd, 0x0f, 0x90, 0xce, 0xe0, 0x09, 0x41, 0x9b, 0xa0, 0x70, 0xec, 0x3d, 0x70, 0x59, 0x7c, 0xc2, - 0x49, 0xa1, 0x19, 0x89, 0x88, 0x41, 0xd3, 0xa7, 0x8c, 0xfa, 0x03, 0xda, 0x7d, 0xdb, 0xf5, 0x65, - 0xe8, 0xbe, 0x3b, 0x45, 0xe8, 0xc6, 0xaa, 0x37, 0x1d, 0xf5, 0x38, 0x15, 0x8c, 0xb3, 0x5a, 0xb4, - 0x7f, 0x2a, 0xf0, 0xe5, 0x9c, 0x90, 0x77, 0x68, 0xdf, 0xb3, 0x09, 0xa7, 0xe7, 0x30, 0x2c, 0x1e, - 0xe6, 0x86, 0xc5, 0x1b, 0x53, 0x78, 0x1a, 0x1b, 0x59, 0x7a, 0x85, 0xff, 0x43, 0x81, 0x4b, 0x13, - 0x39, 0xce, 0xa1, 0xf8, 0xbf, 0x9f, 0x2f, 0xfe, 0x57, 0x3f, 0x85, 0x5f, 0xe5, 0x97, 0xef, 0xa5, - 0xd2, 0x38, 0x7c, 0x21, 0xa7, 0xbb, 0xf6, 0x07, 0x05, 0x16, 0x63, 0xca, 0x70, 0xd5, 0x57, 0xb8, - 0xf9, 0x6e, 0x00, 0xc8, 0x4f, 0xc9, 0xf1, 0xdb, 0xa9, 0x9a, 0xda, 0x7d, 0x33, 0xc1, 0xe0, 0x0c, - 0x15, 0xba, 0x05, 0x28, 0xb6, 0x70, 0xdf, 0x16, 0x4b, 0x3b, 0x3c, 0x9d, 0x54, 0xc1, 0xbb, 0x2e, - 0x79, 0x11, 0x1e, 0xa3, 0xc0, 0x13, 0xb8, 0xb4, 0xbf, 0x2a, 0xe9, 0x5e, 0x15, 0xe0, 0xcf, 0x6b, - 0xe4, 0x85, 0x71, 0xa5, 0x91, 0xcf, 0xee, 0x05, 0x41, 0xf9, 0xb9, 0xdd, 0x0b, 0xc2, 0xba, 0x92, - 0x96, 0x78, 0xa4, 0x16, 0xbc, 0x10, 0xad, 0x50, 0xf5, 0x0a, 0xbb, 0x2d, 0x6f, 0xce, 0x28, 0xac, - 0x2f, 0x56, 0x33, 0x27, 0x2c, 0xd3, 0x89, 0xf7, 0xe9, 0x35, 0x68, 0x38, 0x6e, 0x97, 0x8a, 0xb7, - 0x90, 0xc2, 0xf6, 0xbf, 0x2b, 0xe1, 0x38, 0xa1, 0x18, 0xfb, 0x23, 0x62, 0xf6, 0xb3, 0xf9, 0x23, - 0x42, 0x5c, 0x2c, 0xb6, 0x1d, 0x12, 0x30, 0x71, 0x38, 0x34, 0x32, 0x17, 0x8b, 0x84, 0xe3, 0x84, - 0x02, 0xdd, 0x4b, 0x57, 0x68, 0x5d, 0xe4, 0xe4, 0xeb, 0x55, 0x56, 0x68, 0xf9, 0xf6, 0x34, 0x8c, - 0xc7, 0x4f, 0x5b, 0x33, 0x4f, 0x9e, 0xb6, 0x66, 0x3e, 0x7e, 0xda, 0x9a, 0xf9, 0x68, 0xd4, 0x52, - 0x1e, 0x8f, 0x5a, 0xca, 0x93, 0x51, 0x4b, 0xf9, 0x78, 0xd4, 0x52, 0x3e, 0x19, 0xb5, 0x94, 0x5f, - 0xfd, 0xbb, 0x35, 0xf3, 0x83, 0xe7, 0xcf, 0xfa, 0xbf, 0xed, 0x7f, 0x01, 0x00, 0x00, 0xff, 0xff, - 0xaa, 0x59, 0xd2, 0xe3, 0x8e, 0x1b, 0x00, 0x00, + // 1835 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xd4, 0x19, 0xcd, 0x6f, 0x1c, 0x57, + 0xdd, 0xb3, 0xe3, 0x5d, 0xdb, 0xbf, 0xf5, 0x57, 0x5e, 0xa0, 0x38, 0xa6, 0xec, 0x26, 0x53, 0x04, + 0x4e, 0x9b, 0xce, 0x36, 0x69, 0xd5, 0x16, 0xca, 0x01, 0x8f, 0xed, 0x46, 0x8e, 0xf2, 0xe1, 0x3c, + 0xb7, 0x11, 0x81, 0x52, 0x78, 0x9e, 0x7d, 0x5e, 0x0f, 0x9e, 0x9d, 0x99, 0xce, 0x7b, 0xb3, 0xd4, + 0x17, 0x54, 0xc1, 0x3d, 0xe2, 0x1f, 0x40, 0xdc, 0x90, 0x38, 0xc1, 0x7f, 0x00, 0x12, 0x48, 0x44, + 0xe2, 0x12, 0x09, 0x0e, 0x3d, 0x2d, 0xcd, 0x22, 0x2e, 0x5c, 0xb8, 0xfb, 0x84, 0xe6, 0xcd, 0x9b, + 0xcf, 0xdd, 0x71, 0x66, 0xab, 0x62, 0x85, 0xdb, 0xce, 0xef, 0xfb, 0xfb, 0xf7, 0x9b, 0x59, 0xb8, + 0x76, 0xfc, 0x36, 0xd3, 0x2d, 0xb7, 0x43, 0x3c, 0xab, 0xe3, 0x53, 0xe6, 0x06, 0xbe, 0x49, 0x3b, + 0x83, 0xeb, 0xc4, 0xf6, 0x8e, 0xc8, 0xeb, 0x9d, 0x1e, 0x75, 0xa8, 0x4f, 0x38, 0xed, 0xea, 0x9e, + 0xef, 0x72, 0x17, 0xbd, 0x18, 0x51, 0xeb, 0xc4, 0xb3, 0xf4, 0x98, 0x5a, 0x8f, 0xa9, 0xd7, 0x5f, + 0xed, 0x59, 0xfc, 0x28, 0x38, 0xd0, 0x4d, 0xb7, 0xdf, 0xe9, 0xb9, 0x3d, 0xb7, 0x23, 0x98, 0x0e, + 0x82, 0x43, 0xf1, 0x24, 0x1e, 0xc4, 0xaf, 0x48, 0xd8, 0xba, 0x96, 0x51, 0x6d, 0xba, 0x7e, 0xa8, + 0xb6, 0xa8, 0x70, 0xfd, 0x8d, 0x94, 0xa6, 0x4f, 0xcc, 0x23, 0xcb, 0xa1, 0xfe, 0x49, 0xc7, 0x3b, + 0xee, 0xe5, 0xed, 0x9d, 0x86, 0x8b, 0x75, 0xfa, 0x94, 0x93, 0x49, 0xba, 0x3a, 0x65, 0x5c, 0x7e, + 0xe0, 0x70, 0xab, 0x3f, 0xae, 0xe6, 0xcd, 0x67, 0x31, 0x30, 0xf3, 0x88, 0xf6, 0x49, 0x91, 0x4f, + 0xfb, 0xab, 0x02, 0xab, 0x9b, 0xb6, 0xed, 0x9a, 0x84, 0x5b, 0xae, 0x83, 0x29, 0x0b, 0x6c, 0x8e, + 0x7e, 0x04, 0x73, 0x5d, 0x3a, 0xb0, 0x4c, 0xca, 0xd6, 0x94, 0xcb, 0xca, 0x46, 0xf3, 0xc6, 0x1b, + 0xfa, 0x59, 0xc1, 0xd6, 0xb7, 0x05, 0x71, 0x51, 0x8c, 0xb1, 0xf2, 0x78, 0xd8, 0x9e, 0x19, 0x0d, + 0xdb, 0x73, 0x11, 0x9e, 0xe1, 0x58, 0x2a, 0x7a, 0x00, 0x8b, 0x8e, 0xdb, 0xa5, 0xfb, 0xd4, 0xa6, + 0x26, 0x77, 0xfd, 0x35, 0x55, 0x68, 0xb9, 0x9c, 0xd5, 0x12, 0x66, 0x41, 0x1f, 0x5c, 0xd7, 0xef, + 0x66, 0xe8, 0x8c, 0xd5, 0xd1, 0xb0, 0xbd, 0x98, 0x85, 0xe0, 0x9c, 0x1c, 0xed, 0x33, 0x15, 0x9a, + 0x06, 0x61, 0x96, 0x19, 0x69, 0x44, 0x3f, 0x03, 0x20, 0x9c, 0xfb, 0xd6, 0x41, 0xc0, 0x85, 0x2f, + 0xea, 0x46, 0xf3, 0xc6, 0xb7, 0xce, 0xf6, 0x25, 0xc3, 0xae, 0x6f, 0x26, 0xbc, 0x3b, 0x0e, 0xf7, + 0x4f, 0x8c, 0x97, 0xa4, 0x43, 0x90, 0x22, 0x7e, 0xfe, 0x8f, 0xf6, 0xd2, 0xfd, 0x80, 0xd8, 0xd6, + 0xa1, 0x45, 0xbb, 0x77, 0x49, 0x9f, 0xe2, 0x8c, 0x46, 0x34, 0x80, 0x79, 0x93, 0x78, 0xc4, 0xb4, + 0xf8, 0xc9, 0x5a, 0x4d, 0x68, 0x7f, 0xab, 0xba, 0xf6, 0x2d, 0xc9, 0x19, 0xe9, 0xbe, 0x22, 0x75, + 0xcf, 0xc7, 0xe0, 0x71, 0xcd, 0x89, 0xae, 0x75, 0x1b, 0x56, 0x0a, 0xb6, 0xa3, 0x55, 0x50, 0x8f, + 0xe9, 0x89, 0xc8, 0xe7, 0x02, 0x0e, 0x7f, 0xa2, 0x2d, 0xa8, 0x0f, 0x88, 0x1d, 0xd0, 0xb5, 0x9a, + 0x88, 0xfe, 0xab, 0x95, 0x72, 0x1c, 0x4b, 0xc5, 0x11, 0xef, 0xb7, 0x6b, 0x6f, 0x2b, 0xeb, 0xc7, + 0xb0, 0x94, 0xb3, 0x75, 0x82, 0xae, 0xed, 0xbc, 0x2e, 0x3d, 0xa3, 0x2b, 0x29, 0x57, 0xdd, 0x3b, + 0xee, 0xe5, 0x95, 0xdf, 0x0f, 0x88, 0xc3, 0x2d, 0x7e, 0x92, 0x51, 0xa6, 0xdd, 0x84, 0x0b, 0x5b, + 0x3b, 0xb7, 0x23, 0x6b, 0xe2, 0xbc, 0xa3, 0x1b, 0x00, 0xf4, 0x63, 0xcf, 0xa7, 0x8c, 0x59, 0xae, + 0x13, 0xe9, 0x35, 0x50, 0x9c, 0xac, 0x9d, 0x04, 0x83, 0x33, 0x54, 0xda, 0x00, 0x1a, 0xb2, 0x4a, + 0x2e, 0xc3, 0xac, 0x43, 0xfa, 0x54, 0xf2, 0x2d, 0x4a, 0xbe, 0x59, 0x11, 0x53, 0x81, 0x41, 0xb7, + 0xa0, 0x7e, 0x10, 0x66, 0x46, 0x9a, 0x7f, 0xb5, 0x72, 0x12, 0x8d, 0x85, 0xd1, 0xb0, 0x5d, 0x17, + 0x00, 0x1c, 0x89, 0xd0, 0x1e, 0xd5, 0xe0, 0x6b, 0xc5, 0x86, 0xd9, 0x72, 0x9d, 0x43, 0xab, 0x17, + 0xf8, 0xe2, 0x01, 0x7d, 0x17, 0x1a, 0x91, 0x48, 0x69, 0xd1, 0x86, 0xb4, 0xa8, 0xb1, 0x2f, 0xa0, + 0xa7, 0xc3, 0xf6, 0x0b, 0x45, 0xd6, 0x08, 0x83, 0x25, 0x1f, 0xda, 0x80, 0x79, 0x9f, 0x7e, 0x14, + 0x50, 0xc6, 0x99, 0xa8, 0xbb, 0x05, 0x63, 0x31, 0x2c, 0x1d, 0x2c, 0x61, 0x38, 0xc1, 0xa2, 0x4f, + 0x14, 0xb8, 0x18, 0x75, 0x65, 0xce, 0x06, 0xd9, 0x91, 0xd7, 0xab, 0xd4, 0x44, 0x8e, 0xd1, 0xf8, + 0xaa, 0x34, 0xf6, 0xe2, 0x04, 0x24, 0x9e, 0xa4, 0x4a, 0xfb, 0x97, 0x02, 0x2f, 0x4c, 0x9e, 0x20, + 0xe8, 0x10, 0xe6, 0x7c, 0xf1, 0x2b, 0x6e, 0xde, 0x77, 0xaa, 0x18, 0x24, 0xdd, 0x2c, 0x9f, 0x47, + 0xd1, 0x33, 0xc3, 0xb1, 0x70, 0x64, 0x42, 0xc3, 0x14, 0x36, 0xc9, 0x2e, 0x7d, 0x67, 0xba, 0x79, + 0x97, 0x8f, 0xc0, 0x72, 0x9c, 0xae, 0x08, 0x8c, 0xa5, 0x68, 0xed, 0xb7, 0x0a, 0xac, 0x14, 0xba, + 0x08, 0xb5, 0x40, 0xb5, 0x1c, 0x2e, 0xca, 0x4a, 0x8d, 0x72, 0xb4, 0xeb, 0xf0, 0x07, 0x61, 0xb1, + 0xe3, 0x10, 0x81, 0xae, 0xc0, 0xec, 0x81, 0xeb, 0xda, 0x22, 0x1d, 0xf3, 0xc6, 0xd2, 0x68, 0xd8, + 0x5e, 0x30, 0x5c, 0xd7, 0x8e, 0x28, 0x04, 0x0a, 0x7d, 0x13, 0x1a, 0x8c, 0xfb, 0x96, 0xd3, 0x5b, + 0x9b, 0x15, 0xd5, 0xb2, 0x32, 0x1a, 0xb6, 0x9b, 0xfb, 0x02, 0x12, 0x91, 0x49, 0x34, 0x7a, 0x19, + 0xe6, 0x06, 0xd4, 0x17, 0x1d, 0x52, 0x17, 0x94, 0x62, 0x9a, 0x3e, 0x88, 0x40, 0x11, 0x69, 0x4c, + 0xa0, 0xfd, 0xae, 0x06, 0x4d, 0x99, 0x40, 0x9b, 0x58, 0x7d, 0xf4, 0x30, 0x53, 0x50, 0x51, 0x26, + 0x5e, 0x99, 0x22, 0x13, 0xc6, 0x6a, 0x3c, 0xbc, 0x26, 0x54, 0x20, 0x85, 0xa6, 0xe9, 0x3a, 0x8c, + 0xfb, 0xc4, 0x72, 0x64, 0xb9, 0xe6, 0x07, 0xc4, 0x59, 0x85, 0x27, 0xd9, 0x8c, 0x8b, 0x52, 0x41, + 0x33, 0x85, 0x31, 0x9c, 0x95, 0x8b, 0x3e, 0x4c, 0x52, 0xac, 0x0a, 0x0d, 0x6f, 0x56, 0xd2, 0x10, + 0x3a, 0x5f, 0x2d, 0xbb, 0x7f, 0x51, 0x60, 0xad, 0x8c, 0x29, 0xd7, 0x8f, 0xca, 0xe7, 0xea, 0xc7, + 0xda, 0xf9, 0xf5, 0xe3, 0x1f, 0x95, 0x4c, 0xee, 0x19, 0x43, 0x3f, 0x86, 0xf9, 0xf0, 0x4c, 0xe9, + 0x12, 0x4e, 0xe4, 0x39, 0xf0, 0xda, 0x59, 0xe3, 0x9b, 0xe9, 0x21, 0x75, 0xb8, 0xba, 0xef, 0x1d, + 0xfc, 0x84, 0x9a, 0xfc, 0x0e, 0xe5, 0x24, 0x1d, 0xc6, 0x29, 0x0c, 0x27, 0x52, 0xd1, 0x3d, 0x98, + 0x65, 0x1e, 0x35, 0xa7, 0x59, 0x44, 0xc2, 0xb4, 0x7d, 0x8f, 0x9a, 0xe9, 0xbc, 0x0e, 0x9f, 0xb0, + 0x10, 0xa4, 0xfd, 0x2a, 0x9b, 0x0c, 0xc6, 0xf2, 0xc9, 0x28, 0x0b, 0xb1, 0x72, 0x7e, 0x21, 0xfe, + 0x43, 0x32, 0x0a, 0x84, 0x7d, 0xb7, 0x2d, 0xc6, 0xd1, 0x07, 0x63, 0x61, 0xd6, 0xab, 0x85, 0x39, + 0xe4, 0x16, 0x41, 0x4e, 0xba, 0x2c, 0x86, 0x64, 0x42, 0x7c, 0x17, 0xea, 0x16, 0xa7, 0xfd, 0xb8, + 0xbf, 0xae, 0x56, 0x8e, 0xb1, 0xb1, 0x24, 0xa5, 0xd6, 0x77, 0x43, 0x7e, 0x1c, 0x89, 0xd1, 0x9e, + 0xe4, 0x3d, 0x08, 0x63, 0x8f, 0x7e, 0x08, 0x0b, 0x4c, 0x6e, 0xe4, 0x78, 0x4a, 0x5c, 0xab, 0xa2, + 0x27, 0x39, 0xef, 0x2e, 0x48, 0x55, 0x0b, 0x31, 0x84, 0xe1, 0x54, 0x62, 0xa6, 0x83, 0x6b, 0x53, + 0x75, 0x70, 0x21, 0xff, 0xa5, 0x1d, 0xec, 0xc3, 0xa4, 0x04, 0xa2, 0x1f, 0x40, 0xc3, 0xf5, 0xc8, + 0x47, 0x01, 0x95, 0x59, 0x79, 0xc6, 0x05, 0x77, 0x4f, 0xd0, 0x4e, 0x2a, 0x13, 0x08, 0x75, 0x46, + 0x68, 0x2c, 0x45, 0x6a, 0x8f, 0x14, 0x58, 0x2d, 0x0e, 0xb3, 0x29, 0xa6, 0xc5, 0x1e, 0x2c, 0xf7, + 0x09, 0x37, 0x8f, 0x92, 0x85, 0x22, 0x5a, 0x68, 0xc1, 0xd8, 0x18, 0x0d, 0xdb, 0xcb, 0x77, 0x72, + 0x98, 0xd3, 0x61, 0x1b, 0xbd, 0x1b, 0xd8, 0xf6, 0x49, 0xfe, 0x66, 0x2c, 0xf0, 0x6b, 0xbf, 0x50, + 0x61, 0x29, 0x37, 0xbb, 0x2b, 0x5c, 0x47, 0x9b, 0xb0, 0xd2, 0x4d, 0x83, 0x1d, 0x22, 0xa4, 0x19, + 0x5f, 0x91, 0xc4, 0xd9, 0x4a, 0x11, 0x7c, 0x45, 0xfa, 0x7c, 0xe9, 0xa8, 0x5f, 0x78, 0xe9, 0x3c, + 0x80, 0x65, 0x92, 0x6c, 0xeb, 0x3b, 0x6e, 0x97, 0xca, 0x5d, 0xa9, 0x4b, 0xae, 0xe5, 0xcd, 0x1c, + 0xf6, 0x74, 0xd8, 0xfe, 0x52, 0x71, 0xc7, 0x87, 0x70, 0x5c, 0x90, 0x82, 0x5e, 0x82, 0xba, 0xe9, + 0x06, 0x0e, 0x17, 0x0b, 0x55, 0x4d, 0x5b, 0x65, 0x2b, 0x04, 0xe2, 0x08, 0x87, 0xae, 0x43, 0x93, + 0x74, 0xfb, 0x96, 0xb3, 0x69, 0x9a, 0x94, 0xb1, 0xb5, 0x86, 0x58, 0xe5, 0x62, 0x4b, 0x6f, 0xa6, + 0x60, 0x9c, 0xa5, 0xd1, 0xfe, 0xa3, 0xc4, 0x37, 0x62, 0xc9, 0x2d, 0x83, 0xae, 0x86, 0x97, 0x91, + 0x40, 0xc9, 0xc4, 0x64, 0x8e, 0x1b, 0x01, 0xc6, 0x31, 0x1e, 0x7d, 0x03, 0x1a, 0x5d, 0xdf, 0x1a, + 0x50, 0x5f, 0x66, 0x25, 0xa9, 0xff, 0x6d, 0x01, 0xc5, 0x12, 0x1b, 0x26, 0xda, 0x8b, 0x6f, 0x8d, + 0x4c, 0xa2, 0xf7, 0x5c, 0xd7, 0xc6, 0x02, 0x23, 0x24, 0x09, 0xab, 0x64, 0xf8, 0x52, 0x49, 0x91, + 0xad, 0x12, 0x5b, 0xf4, 0xb8, 0x5e, 0xc1, 0xe3, 0x0f, 0x60, 0xb9, 0x70, 0xd3, 0xdf, 0x02, 0xd5, + 0xa4, 0xb6, 0x6c, 0xba, 0xce, 0xd9, 0xc5, 0x30, 0xf6, 0x46, 0x60, 0xcc, 0x8d, 0x86, 0x6d, 0x75, + 0x6b, 0xe7, 0x36, 0x0e, 0x85, 0x68, 0xbf, 0x51, 0xe0, 0x52, 0x69, 0x63, 0x66, 0x02, 0xa4, 0x9c, + 0x19, 0x20, 0x02, 0xe0, 0x11, 0x9f, 0xf4, 0x29, 0xa7, 0x3e, 0x9b, 0xb0, 0xac, 0xf2, 0x33, 0x5a, + 0xbe, 0x78, 0xeb, 0x98, 0xfc, 0x74, 0xe7, 0x63, 0x4e, 0x9d, 0xf0, 0xae, 0x4a, 0xf7, 0xe0, 0x5e, + 0x22, 0x08, 0x67, 0x84, 0x6a, 0xbf, 0xae, 0xc1, 0x12, 0x96, 0xee, 0x45, 0x97, 0xd7, 0xff, 0x7e, + 0xfb, 0xde, 0xcf, 0x6d, 0xdf, 0x67, 0x44, 0x3a, 0x67, 0x5c, 0xd9, 0xfe, 0x45, 0x0f, 0xc3, 0x9b, + 0x94, 0xf0, 0x80, 0x55, 0x7b, 0x8f, 0xc8, 0x0b, 0x15, 0x8c, 0x69, 0x12, 0xa2, 0x67, 0x2c, 0x05, + 0x6a, 0x23, 0x05, 0x5a, 0x39, 0xfa, 0x70, 0x70, 0x06, 0x7d, 0xea, 0x63, 0x7a, 0x48, 0x7d, 0xea, + 0x98, 0x14, 0x5d, 0x83, 0x79, 0xe2, 0x59, 0x37, 0x7d, 0x37, 0xf0, 0x64, 0x46, 0x93, 0xcd, 0xb8, + 0xb9, 0xb7, 0x2b, 0xe0, 0x38, 0xa1, 0x08, 0xa9, 0x63, 0x8b, 0x64, 0xe9, 0x67, 0xae, 0xd5, 0x08, + 0x8e, 0x13, 0x8a, 0x64, 0x1a, 0xce, 0x96, 0x4e, 0x43, 0x03, 0xd4, 0xc0, 0xea, 0xca, 0x13, 0xfb, + 0x35, 0x49, 0xa0, 0xbe, 0xbf, 0xbb, 0x7d, 0x3a, 0x6c, 0x5f, 0x29, 0xfb, 0x4a, 0xc3, 0x4f, 0x3c, + 0xca, 0xf4, 0xf7, 0x77, 0xb7, 0x71, 0xc8, 0xac, 0xfd, 0x49, 0x81, 0x0b, 0x39, 0x27, 0xcf, 0xe1, + 0x42, 0xd8, 0xcb, 0x5f, 0x08, 0xaf, 0x4c, 0x91, 0xb2, 0x92, 0x1b, 0xc1, 0x2a, 0x38, 0x21, 0x8e, + 0x84, 0xf7, 0x8a, 0xdf, 0x96, 0xae, 0x56, 0x3e, 0xc4, 0xcb, 0x3f, 0x28, 0x69, 0xff, 0x56, 0xe0, + 0xe2, 0x84, 0x2a, 0x42, 0x1f, 0x02, 0xa4, 0x23, 0x7b, 0x42, 0xd0, 0x26, 0x28, 0x1c, 0x7b, 0x6d, + 0x5c, 0x16, 0x5f, 0x7c, 0x52, 0x68, 0x46, 0x22, 0x62, 0xd0, 0xf4, 0x29, 0xa3, 0xfe, 0x80, 0x76, + 0xdf, 0x75, 0x7d, 0x19, 0xba, 0xef, 0x4c, 0x11, 0xba, 0xb1, 0xea, 0x4d, 0x5f, 0x65, 0x70, 0x2a, + 0x18, 0x67, 0xb5, 0x68, 0x7f, 0x57, 0xe0, 0xcb, 0x39, 0x21, 0xef, 0xd1, 0xbe, 0x67, 0x13, 0x4e, + 0xcf, 0x61, 0x58, 0x3c, 0xcc, 0x0d, 0x8b, 0xb7, 0xa6, 0xf0, 0x34, 0x36, 0xb2, 0xf4, 0x68, 0xff, + 0x9b, 0x02, 0x97, 0x26, 0x72, 0x9c, 0x43, 0xf1, 0x7f, 0x2f, 0x5f, 0xfc, 0xaf, 0x7f, 0x0e, 0xbf, + 0xca, 0x0f, 0xe5, 0x4b, 0xa5, 0x71, 0xf8, 0xbf, 0x9c, 0xee, 0xda, 0xef, 0x15, 0x58, 0x8c, 0x29, + 0xc3, 0xeb, 0xa0, 0xc2, 0x89, 0x78, 0x03, 0x40, 0x7e, 0x79, 0x8e, 0x5f, 0x66, 0xd5, 0xd4, 0xee, + 0x9b, 0x09, 0x06, 0x67, 0xa8, 0xd0, 0x2d, 0x40, 0xb1, 0x85, 0xfb, 0xb6, 0x58, 0xda, 0xe1, 0xa5, + 0xa5, 0x0a, 0xde, 0x75, 0xc9, 0x8b, 0xf0, 0x18, 0x05, 0x9e, 0xc0, 0xa5, 0xfd, 0x59, 0x49, 0xf7, + 0xaa, 0x00, 0x3f, 0xaf, 0x91, 0x17, 0xc6, 0x95, 0x46, 0x3e, 0xbb, 0x17, 0x04, 0xe5, 0x73, 0xbb, + 0x17, 0x84, 0x75, 0x25, 0x2d, 0xf1, 0x48, 0x2d, 0x78, 0x21, 0x5a, 0xa1, 0xea, 0x15, 0x76, 0x5b, + 0x9e, 0xa9, 0x51, 0x58, 0x5f, 0xae, 0x66, 0x4e, 0x58, 0xa6, 0x13, 0x4f, 0xda, 0x6b, 0x30, 0xef, + 0xb8, 0x5d, 0x2a, 0x5e, 0x5a, 0x0a, 0xdb, 0xff, 0xae, 0x84, 0xe3, 0x84, 0x62, 0xec, 0x7f, 0x8b, + 0xd9, 0x2f, 0xe6, 0x7f, 0x0b, 0x71, 0xb1, 0xd8, 0x76, 0x48, 0x10, 0x5f, 0xcb, 0xe9, 0xc5, 0x22, + 0xe1, 0x38, 0xa1, 0x40, 0xf7, 0xd2, 0x15, 0xda, 0x10, 0x39, 0xf9, 0x7a, 0x95, 0x15, 0x5a, 0xbe, + 0x3d, 0x0d, 0xe3, 0xf1, 0xd3, 0xd6, 0xcc, 0x93, 0xa7, 0xad, 0x99, 0x4f, 0x9f, 0xb6, 0x66, 0x3e, + 0x19, 0xb5, 0x94, 0xc7, 0xa3, 0x96, 0xf2, 0x64, 0xd4, 0x52, 0x3e, 0x1d, 0xb5, 0x94, 0xcf, 0x46, + 0x2d, 0xe5, 0x97, 0xff, 0x6c, 0xcd, 0x7c, 0xff, 0xc5, 0xb3, 0xfe, 0x9e, 0xfb, 0x6f, 0x00, 0x00, + 0x00, 0xff, 0xff, 0x3c, 0xd4, 0x2c, 0x15, 0xbd, 0x1b, 0x00, 0x00, } func (m *AllocationResult) Marshal() (dAtA []byte, err error) { @@ -1794,14 +1794,16 @@ func (m *DeviceRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l - i-- - if m.AdminAccess { - dAtA[i] = 1 - } else { - dAtA[i] = 0 + if m.AdminAccess != nil { + i-- + if *m.AdminAccess { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x30 } - i-- - dAtA[i] = 0x30 i = encodeVarintGenerated(dAtA, i, uint64(m.Count)) i-- dAtA[i] = 0x28 @@ -1857,6 +1859,16 @@ func (m *DeviceRequestAllocationResult) MarshalToSizedBuffer(dAtA []byte) (int, _ = i var l int _ = l + if m.AdminAccess != nil { + i-- + if *m.AdminAccess { + dAtA[i] = 1 + } else { + dAtA[i] = 0 + } + i-- + dAtA[i] = 0x28 + } i -= len(m.Device) copy(dAtA[i:], m.Device) i = encodeVarintGenerated(dAtA, i, uint64(len(m.Device))) @@ -2811,7 +2823,9 @@ func (m *DeviceRequest) Size() (n int) { l = len(m.AllocationMode) n += 1 + l + sovGenerated(uint64(l)) n += 1 + sovGenerated(uint64(m.Count)) - n += 2 + if m.AdminAccess != nil { + n += 2 + } return n } @@ -2829,6 +2843,9 @@ func (m *DeviceRequestAllocationResult) Size() (n int) { n += 1 + l + sovGenerated(uint64(l)) l = len(m.Device) n += 1 + l + sovGenerated(uint64(l)) + if m.AdminAccess != nil { + n += 2 + } return n } @@ -3296,7 +3313,7 @@ func (this *DeviceRequest) String() string { `Selectors:` + repeatedStringForSelectors + `,`, `AllocationMode:` + fmt.Sprintf("%v", this.AllocationMode) + `,`, `Count:` + fmt.Sprintf("%v", this.Count) + `,`, - `AdminAccess:` + fmt.Sprintf("%v", this.AdminAccess) + `,`, + `AdminAccess:` + valueToStringGenerated(this.AdminAccess) + `,`, `}`, }, "") return s @@ -3310,6 +3327,7 @@ func (this *DeviceRequestAllocationResult) String() string { `Driver:` + fmt.Sprintf("%v", this.Driver) + `,`, `Pool:` + fmt.Sprintf("%v", this.Pool) + `,`, `Device:` + fmt.Sprintf("%v", this.Device) + `,`, + `AdminAccess:` + valueToStringGenerated(this.AdminAccess) + `,`, `}`, }, "") return s @@ -5655,7 +5673,8 @@ func (m *DeviceRequest) Unmarshal(dAtA []byte) error { break } } - m.AdminAccess = bool(v != 0) + b := bool(v != 0) + m.AdminAccess = &b default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) @@ -5834,6 +5853,27 @@ func (m *DeviceRequestAllocationResult) Unmarshal(dAtA []byte) error { } m.Device = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 5: + if wireType != 0 { + return fmt.Errorf("proto: wrong wireType = %d for field AdminAccess", wireType) + } + var v int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowGenerated + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + v |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + b := bool(v != 0) + m.AdminAccess = &b default: iNdEx = preIndex skippy, err := skipGenerated(dAtA[iNdEx:]) diff --git a/staging/src/k8s.io/api/resource/v1alpha3/generated.proto b/staging/src/k8s.io/api/resource/v1alpha3/generated.proto index b549fe3f8f7..2315bde82c2 100644 --- a/staging/src/k8s.io/api/resource/v1alpha3/generated.proto +++ b/staging/src/k8s.io/api/resource/v1alpha3/generated.proto @@ -415,8 +415,12 @@ message DeviceRequest { // all ordinary claims to the device with respect to access modes and // any resource allocations. // + // This is an alpha field and requires enabling the DRAAdminAccess + // feature gate. Admin access is disabled if this field is unset or + // set to false, otherwise it is enabled. + // // +optional - // +default=false + // +featureGate=DRAAdminAccess optional bool adminAccess = 6; } @@ -453,6 +457,18 @@ message DeviceRequestAllocationResult { // // +required optional string device = 4; + + // AdminAccess indicates that this device was allocated for + // administrative access. See the corresponding request field + // for a definition of mode. + // + // This is an alpha field and requires enabling the DRAAdminAccess + // feature gate. Admin access is disabled if this field is unset or + // set to false, otherwise it is enabled. + // + // +optional + // +featureGate=DRAAdminAccess + optional bool adminAccess = 5; } // DeviceSelector must have exactly one field set. diff --git a/staging/src/k8s.io/api/resource/v1alpha3/types.go b/staging/src/k8s.io/api/resource/v1alpha3/types.go index 5bb9cdbdbd0..67d41749122 100644 --- a/staging/src/k8s.io/api/resource/v1alpha3/types.go +++ b/staging/src/k8s.io/api/resource/v1alpha3/types.go @@ -448,9 +448,13 @@ type DeviceRequest struct { // all ordinary claims to the device with respect to access modes and // any resource allocations. // + // This is an alpha field and requires enabling the DRAAdminAccess + // feature gate. Admin access is disabled if this field is unset or + // set to false, otherwise it is enabled. + // // +optional - // +default=false - AdminAccess bool `json:"adminAccess,omitempty" protobuf:"bytes,6,opt,name=adminAccess"` + // +featureGate=DRAAdminAccess + AdminAccess *bool `json:"adminAccess,omitempty" protobuf:"bytes,6,opt,name=adminAccess"` } const ( @@ -788,6 +792,18 @@ type DeviceRequestAllocationResult struct { // // +required Device string `json:"device" protobuf:"bytes,4,name=device"` + + // AdminAccess indicates that this device was allocated for + // administrative access. See the corresponding request field + // for a definition of mode. + // + // This is an alpha field and requires enabling the DRAAdminAccess + // feature gate. Admin access is disabled if this field is unset or + // set to false, otherwise it is enabled. + // + // +optional + // +featureGate=DRAAdminAccess + AdminAccess *bool `json:"adminAccess" protobuf:"bytes,5,name=adminAccess"` } // DeviceAllocationConfiguration gets embedded in an AllocationResult. diff --git a/staging/src/k8s.io/api/resource/v1alpha3/types_swagger_doc_generated.go b/staging/src/k8s.io/api/resource/v1alpha3/types_swagger_doc_generated.go index a5ff1ad6f97..9e549189c7d 100644 --- a/staging/src/k8s.io/api/resource/v1alpha3/types_swagger_doc_generated.go +++ b/staging/src/k8s.io/api/resource/v1alpha3/types_swagger_doc_generated.go @@ -182,7 +182,7 @@ var map_DeviceRequest = map[string]string{ "selectors": "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.", "allocationMode": "AllocationMode and its related fields define how devices are allocated to satisfy this request. Supported values are:\n\n- ExactCount: This request is for a specific number of devices.\n This is the default. The exact number is provided in the\n count field.\n\n- All: This request is for all of the matching devices in a pool.\n Allocation will fail if some devices are already allocated,\n unless adminAccess is requested.\n\nIf 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.\n\nMore modes may get added in the future. Clients must refuse to handle requests with unknown modes.", "count": "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.", - "adminAccess": "AdminAccess indicates that this is a claim for administrative access to the device(s). Claims with AdminAccess are expected to be used for monitoring or other management services for a device. They ignore all ordinary claims to the device with respect to access modes and any resource allocations.", + "adminAccess": "AdminAccess indicates that this is a claim for administrative access to the device(s). Claims with AdminAccess are expected to be used for monitoring or other management services for a device. They ignore all ordinary claims to the device with respect to access modes and any resource allocations.\n\nThis is an alpha field and requires enabling the DRAAdminAccess feature gate. Admin access is disabled if this field is unset or set to false, otherwise it is enabled.", } func (DeviceRequest) SwaggerDoc() map[string]string { @@ -190,11 +190,12 @@ func (DeviceRequest) SwaggerDoc() map[string]string { } var map_DeviceRequestAllocationResult = map[string]string{ - "": "DeviceRequestAllocationResult contains the allocation result for one request.", - "request": "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.", - "driver": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", - "pool": "This name together with the driver name and the device name field identify which device was allocated (`//`).\n\nMust not be longer than 253 characters and may contain one or more DNS sub-domains separated by slashes.", - "device": "Device references one device instance via its name in the driver's resource pool. It must be a DNS label.", + "": "DeviceRequestAllocationResult contains the allocation result for one request.", + "request": "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.", + "driver": "Driver specifies the name of the DRA driver whose kubelet plugin should be invoked to process the allocation once the claim is needed on a node.\n\nMust be a DNS subdomain and should end with a DNS domain owned by the vendor of the driver.", + "pool": "This name together with the driver name and the device name field identify which device was allocated (`//`).\n\nMust not be longer than 253 characters and may contain one or more DNS sub-domains separated by slashes.", + "device": "Device references one device instance via its name in the driver's resource pool. It must be a DNS label.", + "adminAccess": "AdminAccess indicates that this device was allocated for administrative access. See the corresponding request field for a definition of mode.\n\nThis is an alpha field and requires enabling the DRAAdminAccess feature gate. Admin access is disabled if this field is unset or set to false, otherwise it is enabled.", } func (DeviceRequestAllocationResult) SwaggerDoc() map[string]string { diff --git a/staging/src/k8s.io/api/resource/v1alpha3/zz_generated.deepcopy.go b/staging/src/k8s.io/api/resource/v1alpha3/zz_generated.deepcopy.go index b951d76aa04..68d24f53c3b 100644 --- a/staging/src/k8s.io/api/resource/v1alpha3/zz_generated.deepcopy.go +++ b/staging/src/k8s.io/api/resource/v1alpha3/zz_generated.deepcopy.go @@ -144,7 +144,9 @@ func (in *DeviceAllocationResult) DeepCopyInto(out *DeviceAllocationResult) { if in.Results != nil { in, out := &in.Results, &out.Results *out = make([]DeviceRequestAllocationResult, len(*in)) - copy(*out, *in) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } if in.Config != nil { in, out := &in.Config, &out.Config @@ -425,6 +427,11 @@ func (in *DeviceRequest) DeepCopyInto(out *DeviceRequest) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.AdminAccess != nil { + in, out := &in.AdminAccess, &out.AdminAccess + *out = new(bool) + **out = **in + } return } @@ -441,6 +448,11 @@ func (in *DeviceRequest) DeepCopy() *DeviceRequest { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *DeviceRequestAllocationResult) DeepCopyInto(out *DeviceRequestAllocationResult) { *out = *in + if in.AdminAccess != nil { + in, out := &in.AdminAccess, &out.AdminAccess + *out = new(bool) + **out = **in + } return } diff --git a/staging/src/k8s.io/api/testdata/HEAD/resource.k8s.io.v1alpha3.ResourceClaim.json b/staging/src/k8s.io/api/testdata/HEAD/resource.k8s.io.v1alpha3.ResourceClaim.json index 131328e42d4..d64766cc66c 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/resource.k8s.io.v1alpha3.ResourceClaim.json +++ b/staging/src/k8s.io/api/testdata/HEAD/resource.k8s.io.v1alpha3.ResourceClaim.json @@ -99,7 +99,8 @@ "request": "requestValue", "driver": "driverValue", "pool": "poolValue", - "device": "deviceValue" + "device": "deviceValue", + "adminAccess": true } ], "config": [ diff --git a/staging/src/k8s.io/api/testdata/HEAD/resource.k8s.io.v1alpha3.ResourceClaim.pb b/staging/src/k8s.io/api/testdata/HEAD/resource.k8s.io.v1alpha3.ResourceClaim.pb index 7bac0d15d7a..6518cf12480 100644 Binary files a/staging/src/k8s.io/api/testdata/HEAD/resource.k8s.io.v1alpha3.ResourceClaim.pb and b/staging/src/k8s.io/api/testdata/HEAD/resource.k8s.io.v1alpha3.ResourceClaim.pb differ diff --git a/staging/src/k8s.io/api/testdata/HEAD/resource.k8s.io.v1alpha3.ResourceClaim.yaml b/staging/src/k8s.io/api/testdata/HEAD/resource.k8s.io.v1alpha3.ResourceClaim.yaml index 26f136171bb..640f9427b90 100644 --- a/staging/src/k8s.io/api/testdata/HEAD/resource.k8s.io.v1alpha3.ResourceClaim.yaml +++ b/staging/src/k8s.io/api/testdata/HEAD/resource.k8s.io.v1alpha3.ResourceClaim.yaml @@ -76,7 +76,8 @@ status: - requestsValue source: sourceValue results: - - device: deviceValue + - adminAccess: true + device: deviceValue driver: driverValue pool: poolValue request: requestValue diff --git a/staging/src/k8s.io/api/testdata/v1.31.0/resource.k8s.io.v1alpha3.ResourceClaim.after_roundtrip.json b/staging/src/k8s.io/api/testdata/v1.31.0/resource.k8s.io.v1alpha3.ResourceClaim.after_roundtrip.json index 131328e42d4..d14468fd1ff 100644 --- a/staging/src/k8s.io/api/testdata/v1.31.0/resource.k8s.io.v1alpha3.ResourceClaim.after_roundtrip.json +++ b/staging/src/k8s.io/api/testdata/v1.31.0/resource.k8s.io.v1alpha3.ResourceClaim.after_roundtrip.json @@ -99,7 +99,8 @@ "request": "requestValue", "driver": "driverValue", "pool": "poolValue", - "device": "deviceValue" + "device": "deviceValue", + "adminAccess": null } ], "config": [ diff --git a/staging/src/k8s.io/api/testdata/v1.31.0/resource.k8s.io.v1alpha3.ResourceClaim.after_roundtrip.yaml b/staging/src/k8s.io/api/testdata/v1.31.0/resource.k8s.io.v1alpha3.ResourceClaim.after_roundtrip.yaml index 26f136171bb..7ddd6d9c06c 100644 --- a/staging/src/k8s.io/api/testdata/v1.31.0/resource.k8s.io.v1alpha3.ResourceClaim.after_roundtrip.yaml +++ b/staging/src/k8s.io/api/testdata/v1.31.0/resource.k8s.io.v1alpha3.ResourceClaim.after_roundtrip.yaml @@ -76,7 +76,8 @@ status: - requestsValue source: sourceValue results: - - device: deviceValue + - adminAccess: null + device: deviceValue driver: driverValue pool: poolValue request: requestValue diff --git a/staging/src/k8s.io/client-go/applyconfigurations/internal/internal.go b/staging/src/k8s.io/client-go/applyconfigurations/internal/internal.go index c5c674fcd53..ec581483b35 100644 --- a/staging/src/k8s.io/client-go/applyconfigurations/internal/internal.go +++ b/staging/src/k8s.io/client-go/applyconfigurations/internal/internal.go @@ -12419,7 +12419,6 @@ var schemaYAML = typed.YAMLObject(`types: - name: adminAccess type: scalar: boolean - default: false - name: allocationMode type: scalar: string @@ -12443,6 +12442,9 @@ var schemaYAML = typed.YAMLObject(`types: - name: io.k8s.api.resource.v1alpha3.DeviceRequestAllocationResult map: fields: + - name: adminAccess + type: + scalar: boolean - name: device type: scalar: string diff --git a/staging/src/k8s.io/client-go/applyconfigurations/resource/v1alpha3/devicerequestallocationresult.go b/staging/src/k8s.io/client-go/applyconfigurations/resource/v1alpha3/devicerequestallocationresult.go index 712b9bf9b18..4c3cffcf465 100644 --- a/staging/src/k8s.io/client-go/applyconfigurations/resource/v1alpha3/devicerequestallocationresult.go +++ b/staging/src/k8s.io/client-go/applyconfigurations/resource/v1alpha3/devicerequestallocationresult.go @@ -21,10 +21,11 @@ package v1alpha3 // DeviceRequestAllocationResultApplyConfiguration represents a declarative configuration of the DeviceRequestAllocationResult type for use // with apply. type DeviceRequestAllocationResultApplyConfiguration struct { - Request *string `json:"request,omitempty"` - Driver *string `json:"driver,omitempty"` - Pool *string `json:"pool,omitempty"` - Device *string `json:"device,omitempty"` + Request *string `json:"request,omitempty"` + Driver *string `json:"driver,omitempty"` + Pool *string `json:"pool,omitempty"` + Device *string `json:"device,omitempty"` + AdminAccess *bool `json:"adminAccess,omitempty"` } // DeviceRequestAllocationResultApplyConfiguration constructs a declarative configuration of the DeviceRequestAllocationResult type for use with @@ -64,3 +65,11 @@ func (b *DeviceRequestAllocationResultApplyConfiguration) WithDevice(value strin b.Device = &value return b } + +// WithAdminAccess sets the AdminAccess field in the declarative configuration to the given value +// and returns the receiver, so that objects can be built by chaining "With" function invocations. +// If called multiple times, the AdminAccess field is set to the value of the last call. +func (b *DeviceRequestAllocationResultApplyConfiguration) WithAdminAccess(value bool) *DeviceRequestAllocationResultApplyConfiguration { + b.AdminAccess = &value + return b +} diff --git a/staging/src/k8s.io/dynamic-resource-allocation/structured/allocator.go b/staging/src/k8s.io/dynamic-resource-allocation/structured/allocator.go index 271fe064557..7ab3fece1d6 100644 --- a/staging/src/k8s.io/dynamic-resource-allocation/structured/allocator.go +++ b/staging/src/k8s.io/dynamic-resource-allocation/structured/allocator.go @@ -46,25 +46,28 @@ type ClaimLister interface { // available and the current state of the cluster (claims, classes, resource // slices). type Allocator struct { - claimsToAllocate []*resourceapi.ResourceClaim - claimLister ClaimLister - classLister resourcelisters.DeviceClassLister - sliceLister resourcelisters.ResourceSliceLister + adminAccessEnabled bool + claimsToAllocate []*resourceapi.ResourceClaim + claimLister ClaimLister + classLister resourcelisters.DeviceClassLister + sliceLister resourcelisters.ResourceSliceLister } // NewAllocator returns an allocator for a certain set of claims or an error if // some problem was detected which makes it impossible to allocate claims. func NewAllocator(ctx context.Context, + adminAccessEnabled bool, claimsToAllocate []*resourceapi.ResourceClaim, claimLister ClaimLister, classLister resourcelisters.DeviceClassLister, sliceLister resourcelisters.ResourceSliceLister, ) (*Allocator, error) { return &Allocator{ - claimsToAllocate: claimsToAllocate, - claimLister: claimLister, - classLister: classLister, - sliceLister: sliceLister, + adminAccessEnabled: adminAccessEnabled, + claimsToAllocate: claimsToAllocate, + claimLister: claimLister, + classLister: classLister, + sliceLister: sliceLister, }, nil } @@ -160,6 +163,10 @@ func (a *Allocator) Allocate(ctx context.Context, node *v1.Node) (finalResult [] } } + if !a.adminAccessEnabled && request.AdminAccess != nil { + return nil, fmt.Errorf("claim %s, request %s: admin access is requested, but the feature is disabled", klog.KObj(claim), request.Name) + } + // Should be set. If it isn't, something changed and we should refuse to proceed. if request.DeviceClassName == "" { return nil, fmt.Errorf("claim %s, request %s: missing device class name (unsupported request type?)", klog.KObj(claim), request.Name) @@ -273,6 +280,15 @@ func (a *Allocator) Allocate(ctx context.Context, node *v1.Node) (finalResult [] continue } for _, result := range claim.Status.Allocation.Devices.Results { + // Kubernetes 1.31 did not set this, 1.32 always does. + // Supporting 1.31 is not worth the additional code that + // would have to be written (= looking up in request) because + // it is extremely unlikely that there really is a result + // that still exists in a cluster from 1.31 where this matters. + if ptr.Deref(result.AdminAccess, false) { + // Ignore, it's not considered allocated. + continue + } deviceID := DeviceID{Driver: result.Driver, Pool: result.Pool, Device: result.Device} alloc.allocated[deviceID] = true numAllocated++ @@ -568,7 +584,7 @@ func (alloc *allocator) allocateOne(r deviceIndices) (bool, error) { deviceID := DeviceID{Driver: pool.Driver, Pool: pool.Pool, Device: slice.Spec.Devices[deviceIndex].Name} // Checking for "in use" is cheap and thus gets done first. - if !request.AdminAccess && alloc.allocated[deviceID] { + if !ptr.Deref(request.AdminAccess, false) && alloc.allocated[deviceID] { alloc.logger.V(7).Info("Device in use", "device", deviceID) continue } @@ -704,7 +720,7 @@ func (alloc *allocator) selectorsMatch(r requestIndices, device *resourceapi.Bas func (alloc *allocator) allocateDevice(r deviceIndices, device *resourceapi.BasicDevice, deviceID DeviceID, must bool) (bool, func(), error) { claim := alloc.claimsToAllocate[r.claimIndex] request := &claim.Spec.Devices.Requests[r.requestIndex] - adminAccess := request.AdminAccess + adminAccess := ptr.Deref(request.AdminAccess, false) if !adminAccess && alloc.allocated[deviceID] { alloc.logger.V(7).Info("Device in use", "device", deviceID) return false, nil, nil @@ -740,6 +756,9 @@ func (alloc *allocator) allocateDevice(r deviceIndices, device *resourceapi.Basi Pool: deviceID.Pool, Device: deviceID.Device, } + if adminAccess { + result.AdminAccess = &adminAccess + } previousNumResults := len(alloc.result[r.claimIndex].Devices.Results) alloc.result[r.claimIndex].Devices.Results = append(alloc.result[r.claimIndex].Devices.Results, result) diff --git a/staging/src/k8s.io/dynamic-resource-allocation/structured/allocator_test.go b/staging/src/k8s.io/dynamic-resource-allocation/structured/allocator_test.go index 8626302418f..40a6b7561db 100644 --- a/staging/src/k8s.io/dynamic-resource-allocation/structured/allocator_test.go +++ b/staging/src/k8s.io/dynamic-resource-allocation/structured/allocator_test.go @@ -241,13 +241,17 @@ func slice(name string, nodeSelection any, pool, driver string, devices ...resou return slice } -func deviceAllocationResult(request, driver, pool, device string) resourceapi.DeviceRequestAllocationResult { - return resourceapi.DeviceRequestAllocationResult{ +func deviceAllocationResult(request, driver, pool, device string, adminAccess bool) resourceapi.DeviceRequestAllocationResult { + r := resourceapi.DeviceRequestAllocationResult{ Request: request, Driver: driver, Pool: pool, Device: device, } + if adminAccess { + r.AdminAccess = &adminAccess + } + return r } // nodeLabelSelector creates a node selector with a label match for "key" in "values". @@ -356,6 +360,7 @@ func TestAllocator(t *testing.T) { intAttribute := resourceapi.FullyQualifiedName("numa") testcases := map[string]struct { + adminAccess bool claimsToAllocate []*resourceapi.ResourceClaim allocatedClaims []*resourceapi.ResourceClaim classes []*resourceapi.DeviceClass @@ -375,7 +380,7 @@ func TestAllocator(t *testing.T) { expectResults: []any{allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverA, pool1, device1), + deviceAllocationResult(req0, driverA, pool1, device1, false), )}, }, "other-node": { @@ -389,7 +394,7 @@ func TestAllocator(t *testing.T) { expectResults: []any{allocationResult( localNodeSelector(node2), - deviceAllocationResult(req0, driverA, pool2, device1), + deviceAllocationResult(req0, driverA, pool2, device1, false), )}, }, "small-and-large": { @@ -418,8 +423,8 @@ func TestAllocator(t *testing.T) { expectResults: []any{allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverA, pool1, device1), - deviceAllocationResult(req1, driverA, pool1, device2), + deviceAllocationResult(req0, driverA, pool1, device1, false), + deviceAllocationResult(req1, driverA, pool1, device2, false), )}, }, "small-and-large-backtrack-requests": { @@ -451,8 +456,8 @@ func TestAllocator(t *testing.T) { expectResults: []any{allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverA, pool1, device1), - deviceAllocationResult(req1, driverA, pool1, device2), + deviceAllocationResult(req0, driverA, pool1, device1, false), + deviceAllocationResult(req1, driverA, pool1, device2, false), )}, }, "small-and-large-backtrack-claims": { @@ -487,8 +492,8 @@ func TestAllocator(t *testing.T) { node: node(node1, region1), expectResults: []any{ - allocationResult(localNodeSelector(node1), deviceAllocationResult(req0, driverA, pool1, device1)), - allocationResult(localNodeSelector(node1), deviceAllocationResult(req1, driverA, pool1, device2)), + allocationResult(localNodeSelector(node1), deviceAllocationResult(req0, driverA, pool1, device1, false)), + allocationResult(localNodeSelector(node1), deviceAllocationResult(req1, driverA, pool1, device2, false)), }, }, "devices-split-across-different-slices": { @@ -507,8 +512,8 @@ func TestAllocator(t *testing.T) { expectResults: []any{allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverA, pool1, device1), - deviceAllocationResult(req0, driverA, pool2, device1), + deviceAllocationResult(req0, driverA, pool1, device1, false), + deviceAllocationResult(req0, driverA, pool2, device1, false), )}, }, "obsolete-slice": { @@ -527,7 +532,7 @@ func TestAllocator(t *testing.T) { expectResults: []any{allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverA, pool1, device1), + deviceAllocationResult(req0, driverA, pool1, device1, false), )}, }, "no-slices": { @@ -594,7 +599,7 @@ func TestAllocator(t *testing.T) { expectResults: []any{allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverA, pool1, device1), + deviceAllocationResult(req0, driverA, pool1, device1, false), )}, }, "all-devices-many": { @@ -612,8 +617,8 @@ func TestAllocator(t *testing.T) { expectResults: []any{allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverA, pool1, device1), - deviceAllocationResult(req0, driverA, pool2, device1), + deviceAllocationResult(req0, driverA, pool1, device1, false), + deviceAllocationResult(req0, driverA, pool2, device1, false), )}, }, "all-devices-of-the-incomplete-pool": { @@ -664,11 +669,11 @@ func TestAllocator(t *testing.T) { expectResults: []any{ allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverA, pool1, device1), + deviceAllocationResult(req0, driverA, pool1, device1, false), ), allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverB, pool1, device1), + deviceAllocationResult(req0, driverB, pool1, device1, false), ), }, }, @@ -699,11 +704,11 @@ func TestAllocator(t *testing.T) { expectResults: []any{ allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverB, pool1, device1), + deviceAllocationResult(req0, driverB, pool1, device1, false), ), allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverA, pool1, device1), + deviceAllocationResult(req0, driverA, pool1, device1, false), ), }, }, @@ -737,12 +742,12 @@ func TestAllocator(t *testing.T) { expectResults: []any{ allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverA, pool1, device1), - deviceAllocationResult(req0, driverA, pool1, device2), + deviceAllocationResult(req0, driverA, pool1, device1, false), + deviceAllocationResult(req0, driverA, pool1, device2, false), ), allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverB, pool1, device1), + deviceAllocationResult(req0, driverB, pool1, device1, false), ), }, }, @@ -776,12 +781,12 @@ func TestAllocator(t *testing.T) { expectResults: []any{ allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverB, pool1, device1), + deviceAllocationResult(req0, driverB, pool1, device1, false), ), allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverA, pool1, device1), - deviceAllocationResult(req0, driverA, pool1, device2), + deviceAllocationResult(req0, driverA, pool1, device1, false), + deviceAllocationResult(req0, driverA, pool1, device2, false), ), }, }, @@ -839,7 +844,7 @@ func TestAllocator(t *testing.T) { expectResults: []any{allocationResult( nodeLabelSelector(regionKey, region1), - deviceAllocationResult(req0, driverA, pool1, device1), + deviceAllocationResult(req0, driverA, pool1, device1, false), )}, }, "unsuccessful-allocation-network-attached-device": { @@ -875,10 +880,10 @@ func TestAllocator(t *testing.T) { }, }}, }, - deviceAllocationResult(req0, driverA, pool1, device1), - deviceAllocationResult(req0, driverA, pool2, device1), - deviceAllocationResult(req0, driverA, pool3, device1), - deviceAllocationResult(req0, driverA, pool4, device1), + deviceAllocationResult(req0, driverA, pool1, device1, false), + deviceAllocationResult(req0, driverA, pool2, device1, false), + deviceAllocationResult(req0, driverA, pool3, device1, false), + deviceAllocationResult(req0, driverA, pool4, device1, false), )}, }, "local-and-network-attached-devices": { @@ -893,8 +898,8 @@ func TestAllocator(t *testing.T) { expectResults: []any{allocationResult( // Once there is any node-local device, the selector is for that node. localNodeSelector(node1), - deviceAllocationResult(req0, driverA, pool1, device1), - deviceAllocationResult(req0, driverA, pool2, device1), + deviceAllocationResult(req0, driverA, pool1, device1, false), + deviceAllocationResult(req0, driverA, pool2, device1, false), )}, }, "several-different-drivers": { @@ -910,16 +915,16 @@ func TestAllocator(t *testing.T) { node: node(node1, region1), expectResults: []any{ - allocationResult(localNodeSelector(node1), deviceAllocationResult(req0, driverA, pool1, device1)), - allocationResult(localNodeSelector(node1), deviceAllocationResult(req0, driverB, pool1, device1)), + allocationResult(localNodeSelector(node1), deviceAllocationResult(req0, driverA, pool1, device1, false)), + allocationResult(localNodeSelector(node1), deviceAllocationResult(req0, driverB, pool1, device1, false)), }, }, "already-allocated-devices": { claimsToAllocate: objects(claim(claim0, req0, classA)), allocatedClaims: objects( allocatedClaim(claim0, req0, classA, - deviceAllocationResult(req0, driverA, pool1, device1), - deviceAllocationResult(req1, driverA, pool1, device2), + deviceAllocationResult(req0, driverA, pool1, device1, false), + deviceAllocationResult(req1, driverA, pool1, device2, false), ), ), classes: objects(class(classA, driverA)), @@ -928,6 +933,57 @@ func TestAllocator(t *testing.T) { expectResults: nil, }, + "admin-access-disabled": { + adminAccess: false, + claimsToAllocate: func() []*resourceapi.ResourceClaim { + c := claim(claim0, req0, classA) + c.Spec.Devices.Requests[0].AdminAccess = ptr.To(true) + return []*resourceapi.ResourceClaim{c} + }(), + classes: objects(class(classA, driverA)), + slices: objects(sliceWithOneDevice(slice1, node1, pool1, driverA)), + node: node(node1, region1), + + expectResults: nil, + expectError: gomega.MatchError(gomega.ContainSubstring("claim claim-0, request req-0: admin access is requested, but the feature is disabled")), + }, + "admin-access-enabled": { + adminAccess: true, + claimsToAllocate: func() []*resourceapi.ResourceClaim { + c := claim(claim0, req0, classA) + c.Spec.Devices.Requests[0].AdminAccess = ptr.To(true) + return []*resourceapi.ResourceClaim{c} + }(), + allocatedClaims: objects( + allocatedClaim(claim0, req0, classA, + deviceAllocationResult(req0, driverA, pool1, device1, false), + deviceAllocationResult(req1, driverA, pool1, device2, false), + ), + ), + classes: objects(class(classA, driverA)), + slices: objects(sliceWithOneDevice(slice1, node1, pool1, driverA)), + node: node(node1, region1), + + expectResults: []any{ + allocationResult(localNodeSelector(node1), deviceAllocationResult(req0, driverA, pool1, device1, true)), + }, + }, + "already-allocated-for-admin-access": { + claimsToAllocate: objects(claim(claim0, req0, classA)), + allocatedClaims: objects( + allocatedClaim(claim0, req0, classA, + deviceAllocationResult(req0, driverA, pool1, device1, true), + deviceAllocationResult(req1, driverA, pool1, device2, true), + ), + ), + classes: objects(class(classA, driverA)), + slices: objects(sliceWithOneDevice(slice1, node1, pool1, driverA)), + node: node(node1, region1), + + expectResults: []any{ + allocationResult(localNodeSelector(node1), deviceAllocationResult(req0, driverA, pool1, device1, false)), + }, + }, "with-constraint": { claimsToAllocate: objects(claimWithRequests( claim0, @@ -960,8 +1016,8 @@ func TestAllocator(t *testing.T) { expectResults: []any{allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverA, pool1, device1), - deviceAllocationResult(req1, driverA, pool1, device2), + deviceAllocationResult(req0, driverA, pool1, device1, false), + deviceAllocationResult(req1, driverA, pool1, device2, false), )}, }, "with-constraint-non-existent-attribute": { @@ -1100,8 +1156,8 @@ func TestAllocator(t *testing.T) { expectResults: []any{allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverA, pool1, device1), - deviceAllocationResult(req1, driverA, pool1, device2), + deviceAllocationResult(req0, driverA, pool1, device1, false), + deviceAllocationResult(req1, driverA, pool1, device2, false), )}, }, "with-constraint-for-request-retry": { @@ -1141,8 +1197,8 @@ func TestAllocator(t *testing.T) { expectResults: []any{allocationResult( localNodeSelector(node1), - deviceAllocationResult(req0, driverA, pool1, device2), - deviceAllocationResult(req1, driverA, pool1, device3), + deviceAllocationResult(req0, driverA, pool1, device2, false), + deviceAllocationResult(req1, driverA, pool1, device3, false), )}, }, "with-class-device-config": { @@ -1157,7 +1213,7 @@ func TestAllocator(t *testing.T) { driverA, resourceapi.AllocationConfigSourceClass, "classAttribute", - deviceAllocationResult(req0, driverA, pool1, device1), + deviceAllocationResult(req0, driverA, pool1, device1, false), ), }, }, @@ -1173,7 +1229,7 @@ func TestAllocator(t *testing.T) { driverA, resourceapi.AllocationConfigSourceClaim, "deviceAttribute", - deviceAllocationResult(req0, driverA, pool1, device1), + deviceAllocationResult(req0, driverA, pool1, device1, false), ), }, }, @@ -1335,7 +1391,7 @@ func TestAllocator(t *testing.T) { classLister.objs = append(classLister.objs, class.DeepCopy()) } - allocator, err := NewAllocator(ctx, toAllocate.claims, allocated, classLister, sliceLister) + allocator, err := NewAllocator(ctx, tc.adminAccess, toAllocate.claims, allocated, classLister, sliceLister) g.Expect(err).ToNot(gomega.HaveOccurred()) results, err := allocator.Allocate(ctx, tc.node) diff --git a/test/e2e/dra/dra.go b/test/e2e/dra/dra.go index d03193a3f4c..5b2fa693354 100644 --- a/test/e2e/dra/dra.go +++ b/test/e2e/dra/dra.go @@ -33,6 +33,7 @@ import ( "github.com/onsi/gomega/types" admissionregistrationv1 "k8s.io/api/admissionregistration/v1" + appsv1 "k8s.io/api/apps/v1" v1 "k8s.io/api/core/v1" resourceapi "k8s.io/api/resource/v1alpha3" apierrors "k8s.io/apimachinery/pkg/api/errors" @@ -44,6 +45,7 @@ import ( "k8s.io/klog/v2" "k8s.io/kubernetes/test/e2e/feature" "k8s.io/kubernetes/test/e2e/framework" + e2edaemonset "k8s.io/kubernetes/test/e2e/framework/daemonset" e2epod "k8s.io/kubernetes/test/e2e/framework/pod" admissionapi "k8s.io/pod-security-admission/api" "k8s.io/utils/ptr" @@ -732,7 +734,7 @@ var _ = framework.SIGDescribe("node")("DRA", feature.DynamicResourceAllocation, driver := NewDriver(f, nodes, networkResources) b := newBuilder(f, driver) - ginkgo.It("support validating admission policy for admin access", func(ctx context.Context) { + f.It("support validating admission policy for admin access", feature.DRAAdminAccess, func(ctx context.Context) { // Create VAP, after making it unique to the current test. adminAccessPolicyYAML := strings.ReplaceAll(adminAccessPolicyYAML, "dra.example.com", b.f.UniqueName) driver.createFromYAML(ctx, []byte(adminAccessPolicyYAML), "") @@ -752,9 +754,9 @@ var _ = framework.SIGDescribe("node")("DRA", feature.DynamicResourceAllocation, // Attempt to create claim and claim template with admin access. Must fail eventually. claim := b.externalClaim() - claim.Spec.Devices.Requests[0].AdminAccess = true + claim.Spec.Devices.Requests[0].AdminAccess = ptr.To(true) _, claimTemplate := b.podInline() - claimTemplate.Spec.Spec.Devices.Requests[0].AdminAccess = true + claimTemplate.Spec.Spec.Devices.Requests[0].AdminAccess = ptr.To(true) matchVAPError := gomega.MatchError(gomega.ContainSubstring("admin access to devices not enabled" /* in namespace " + b.f.Namespace.Name */)) gomega.Eventually(ctx, func(ctx context.Context) error { // First delete, in case that it succeeded earlier. @@ -844,6 +846,42 @@ var _ = framework.SIGDescribe("node")("DRA", feature.DynamicResourceAllocation, _, err = f.ClientSet.ResourceV1alpha3().ResourceClaims(f.Namespace.Name).Create(ctx, claim2, metav1.CreateOptions{}) gomega.Expect(err).Should(gomega.MatchError(gomega.ContainSubstring("exceeded quota: object-count, requested: count/resourceclaims.resource.k8s.io=1, used: count/resourceclaims.resource.k8s.io=1, limited: count/resourceclaims.resource.k8s.io=1")), "creating second claim not allowed") }) + + f.It("DaemonSet with admin access", feature.DRAAdminAccess, func(ctx context.Context) { + pod, template := b.podInline() + template.Spec.Spec.Devices.Requests[0].AdminAccess = ptr.To(true) + // Limit the daemon set to the one node where we have the driver. + nodeName := nodes.NodeNames[0] + pod.Spec.NodeSelector = map[string]string{"kubernetes.io/hostname": nodeName} + pod.Spec.RestartPolicy = v1.RestartPolicyAlways + daemonSet := &appsv1.DaemonSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: "monitoring-ds", + }, + Spec: appsv1.DaemonSetSpec{ + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{"app": "monitoring"}, + }, + Template: v1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"app": "monitoring"}, + }, + Spec: pod.Spec, + }, + }, + } + + created := b.create(ctx, template, daemonSet) + if !ptr.Deref(created[0].(*resourceapi.ResourceClaimTemplate).Spec.Spec.Devices.Requests[0].AdminAccess, false) { + framework.Fail("AdminAccess field was cleared. This test depends on the DRAAdminAccess feature.") + } + ds := created[1].(*appsv1.DaemonSet) + + gomega.Eventually(ctx, func(ctx context.Context) (bool, error) { + return e2edaemonset.CheckDaemonPodOnNodes(f, ds, []string{nodeName})(ctx) + }).WithTimeout(f.Timeouts.PodStart).Should(gomega.BeTrueBecause("DaemonSet pod should be running on node %s but isn't", nodeName)) + framework.ExpectNoError(e2edaemonset.CheckDaemonStatus(ctx, f, daemonSet.Name)) + }) }) ginkgo.Context("cluster", func() { @@ -1317,6 +1355,13 @@ func (b *builder) create(ctx context.Context, objs ...klog.KMetadata) []klog.KMe err := b.f.ClientSet.ResourceV1alpha3().ResourceSlices().Delete(ctx, createdObj.GetName(), metav1.DeleteOptions{}) framework.ExpectNoError(err, "delete node resource slice") }) + case *appsv1.DaemonSet: + createdObj, err = b.f.ClientSet.AppsV1().DaemonSets(b.f.Namespace.Name).Create(ctx, obj, metav1.CreateOptions{}) + // Cleanup not really needed, but speeds up namespace shutdown. + ginkgo.DeferCleanup(func(ctx context.Context) { + err := b.f.ClientSet.AppsV1().DaemonSets(b.f.Namespace.Name).Delete(ctx, obj.Name, metav1.DeleteOptions{}) + framework.ExpectNoError(err, "delete daemonset") + }) default: framework.Fail(fmt.Sprintf("internal error, unsupported type %T", obj), 1) } diff --git a/test/e2e/feature/feature.go b/test/e2e/feature/feature.go index bc43582c740..c5521fae8ca 100644 --- a/test/e2e/feature/feature.go +++ b/test/e2e/feature/feature.go @@ -91,6 +91,18 @@ var ( // TODO: document the feature (owning SIG, when to use this feature for a test) Downgrade = framework.WithFeature(framework.ValidFeatures.Add("Downgrade")) + // owning-sig: sig-node + // kep: https://kep.k8s.io/4381 + // test-infra jobs: + // - "dra-alpha" in https://testgrid.k8s.io/sig-node-dynamic-resource-allocation + // + // This label is used for tests which need: + // - the DynamicResourceAllocation *and* DRAAdminAccess feature gates + // - the resource.k8s.io API group + // - a container runtime where support for CDI (https://github.com/cncf-tags/container-device-interface) + // is enabled such that passing CDI device IDs through CRI fields is supported + DRAAdminAccess = framework.WithFeature(framework.ValidFeatures.Add("DRAAdminAccess")) + // owning-sig: sig-node // kep: https://kep.k8s.io/4381 // test-infra jobs: diff --git a/test/e2e_node/dra_test.go b/test/e2e_node/dra_test.go index dcdbeb715d1..72ce7013762 100644 --- a/test/e2e_node/dra_test.go +++ b/test/e2e_node/dra_test.go @@ -47,6 +47,7 @@ import ( "k8s.io/client-go/kubernetes" "k8s.io/klog/v2" admissionapi "k8s.io/pod-security-admission/api" + "k8s.io/utils/ptr" "k8s.io/kubernetes/test/e2e/feature" "k8s.io/kubernetes/test/e2e/framework" @@ -673,10 +674,11 @@ func createTestObjects(ctx context.Context, clientSet kubernetes.Interface, node config := make([]resourceapi.DeviceAllocationConfiguration, len(driverNames)) for i, driverName := range driverNames { results[i] = resourceapi.DeviceRequestAllocationResult{ - Driver: driverName, - Pool: "some-pool", - Device: "some-device", - Request: claim.Spec.Devices.Requests[0].Name, + Driver: driverName, + Pool: "some-pool", + Device: "some-device", + Request: claim.Spec.Devices.Requests[0].Name, + AdminAccess: ptr.To(false), } config[i] = resourceapi.DeviceAllocationConfiguration{ Source: resourceapi.AllocationConfigSourceClaim, diff --git a/test/featuregates_linter/test_data/versioned_feature_list.yaml b/test/featuregates_linter/test_data/versioned_feature_list.yaml index 04f70a2450f..b542b85b85c 100644 --- a/test/featuregates_linter/test_data/versioned_feature_list.yaml +++ b/test/featuregates_linter/test_data/versioned_feature_list.yaml @@ -388,6 +388,12 @@ lockToDefault: false preRelease: Deprecated version: "1.31" +- name: DRAAdminAccess + versionedSpecs: + - default: false + lockToDefault: false + preRelease: Alpha + version: "1.32" - name: DynamicResourceAllocation versionedSpecs: - default: false diff --git a/test/integration/scheduler_perf/dra.go b/test/integration/scheduler_perf/dra.go index be884c2c8ee..d85e624309b 100644 --- a/test/integration/scheduler_perf/dra.go +++ b/test/integration/scheduler_perf/dra.go @@ -30,9 +30,11 @@ import ( "k8s.io/apimachinery/pkg/api/resource" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/labels" + utilfeature "k8s.io/apiserver/pkg/util/feature" "k8s.io/client-go/informers" "k8s.io/client-go/util/workqueue" "k8s.io/dynamic-resource-allocation/structured" + "k8s.io/kubernetes/pkg/features" "k8s.io/kubernetes/pkg/scheduler/util/assumecache" "k8s.io/kubernetes/test/utils/ktesting" "k8s.io/utils/ptr" @@ -304,7 +306,7 @@ claims: continue } - allocator, err := structured.NewAllocator(tCtx, []*resourceapi.ResourceClaim{claim}, claimLister, classLister, sliceLister) + allocator, err := structured.NewAllocator(tCtx, utilfeature.DefaultFeatureGate.Enabled(features.DRAAdminAccess), []*resourceapi.ResourceClaim{claim}, claimLister, classLister, sliceLister) tCtx.ExpectNoError(err, "create allocator") rand.Shuffle(len(nodes), func(i, j int) { diff --git a/test/integration/util/util.go b/test/integration/util/util.go index 626a1a4191f..c4a5386d9a0 100644 --- a/test/integration/util/util.go +++ b/test/integration/util/util.go @@ -132,7 +132,7 @@ func CreateResourceClaimController(ctx context.Context, tb ktesting.TB, clientSe podInformer := informerFactory.Core().V1().Pods() claimInformer := informerFactory.Resource().V1alpha3().ResourceClaims() claimTemplateInformer := informerFactory.Resource().V1alpha3().ResourceClaimTemplates() - claimController, err := resourceclaim.NewController(klog.FromContext(ctx), clientSet, podInformer, claimInformer, claimTemplateInformer) + claimController, err := resourceclaim.NewController(klog.FromContext(ctx), true /* admin access */, clientSet, podInformer, claimInformer, claimTemplateInformer) if err != nil { tb.Fatalf("Error creating claim controller: %v", err) }