diff --git a/cmd/kube-controller-manager/app/core.go b/cmd/kube-controller-manager/app/core.go index 4bb284c0604..a6b9f5bb6e3 100644 --- a/cmd/kube-controller-manager/app/core.go +++ b/cmd/kube-controller-manager/app/core.go @@ -408,7 +408,10 @@ 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), + resourceclaim.Features{ + AdminAccess: utilfeature.DefaultFeatureGate.Enabled(features.DRAAdminAccess), + PrioritizedList: utilfeature.DefaultFeatureGate.Enabled(features.DRAPrioritizedList), + }, controllerContext.ClientBuilder.ClientOrDie("resource-claim-controller"), controllerContext.InformerFactory.Core().V1().Pods(), controllerContext.InformerFactory.Resource().V1beta1().ResourceClaims(), diff --git a/pkg/controller/resourceclaim/controller.go b/pkg/controller/resourceclaim/controller.go index 3df0211078a..6fb1365ee7e 100644 --- a/pkg/controller/resourceclaim/controller.go +++ b/pkg/controller/resourceclaim/controller.go @@ -71,8 +71,8 @@ const ( // Controller creates ResourceClaims for ResourceClaimTemplates in a pod spec. type Controller struct { - // adminAccessEnabled matches the DRAAdminAccess feature gate state. - adminAccessEnabled bool + // features defines the feature gates that are enabled. + features Features // kubeClient is the kube API client used to communicate with the API // server. @@ -118,25 +118,31 @@ const ( podKeyPrefix = "pod:" ) +// Features defines which features should be enabled in the controller. +type Features struct { + AdminAccess bool + PrioritizedList bool +} + // NewController creates a ResourceClaim controller. func NewController( logger klog.Logger, - adminAccessEnabled bool, + features Features, kubeClient clientset.Interface, podInformer v1informers.PodInformer, claimInformer resourceinformers.ResourceClaimInformer, templateInformer resourceinformers.ResourceClaimTemplateInformer) (*Controller, error) { ec := &Controller{ - 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, + features: features, + 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"}, @@ -617,10 +623,14 @@ 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) { + if !ec.features.AdminAccess && needsAdminAccess(template) { return errors.New("admin access is requested, but the feature is disabled") } + if !ec.features.PrioritizedList && hasPrioritizedList(template) { + return errors.New("template includes a prioritized list of subrequests, but the feature is disabled") + } + // Create the ResourceClaim with pod as owner, with a generated name that uses // - as base. isTrue := true @@ -688,6 +698,15 @@ func needsAdminAccess(claimTemplate *resourceapi.ResourceClaimTemplate) bool { return false } +func hasPrioritizedList(claimTemplate *resourceapi.ResourceClaimTemplate) bool { + for _, request := range claimTemplate.Spec.Spec.Devices.Requests { + if len(request.FirstAvailable) > 0 { + 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 f2b5b78cbc9..8aa5d5316ea 100644 --- a/pkg/controller/resourceclaim/controller_test.go +++ b/pkg/controller/resourceclaim/controller_test.go @@ -82,18 +82,19 @@ var ( func TestSyncHandler(t *testing.T) { tests := []struct { - 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 string + key string + adminAccessEnabled bool + prioritizedListEnabled 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 +391,11 @@ func TestSyncHandler(t *testing.T) { claimInformer := informerFactory.Resource().V1beta1().ResourceClaims() templateInformer := informerFactory.Resource().V1beta1().ResourceClaimTemplates() - ec, err := NewController(tCtx.Logger(), tc.adminAccessEnabled, fakeKubeClient, podInformer, claimInformer, templateInformer) + features := Features{ + AdminAccess: tc.adminAccessEnabled, + PrioritizedList: tc.prioritizedListEnabled, + } + ec, err := NewController(tCtx.Logger(), features, fakeKubeClient, podInformer, claimInformer, templateInformer) if err != nil { t.Fatalf("error creating ephemeral controller : %v", err) } @@ -465,7 +470,7 @@ func TestResourceClaimEventHandler(t *testing.T) { templateInformer := informerFactory.Resource().V1beta1().ResourceClaimTemplates() claimClient := fakeKubeClient.ResourceV1beta1().ResourceClaims(testNamespace) - _, err := NewController(tCtx.Logger(), false /* admin access */, fakeKubeClient, podInformer, claimInformer, templateInformer) + _, err := NewController(tCtx.Logger(), Features{}, fakeKubeClient, podInformer, claimInformer, templateInformer) tCtx.ExpectNoError(err, "creating ephemeral controller") informerFactory.Start(tCtx.Done()) diff --git a/test/integration/util/util.go b/test/integration/util/util.go index 7eeb288771f..370b2010ccb 100644 --- a/test/integration/util/util.go +++ b/test/integration/util/util.go @@ -132,7 +132,11 @@ func CreateResourceClaimController(ctx context.Context, tb ktesting.TB, clientSe podInformer := informerFactory.Core().V1().Pods() claimInformer := informerFactory.Resource().V1beta1().ResourceClaims() claimTemplateInformer := informerFactory.Resource().V1beta1().ResourceClaimTemplates() - claimController, err := resourceclaim.NewController(klog.FromContext(ctx), true /* admin access */, clientSet, podInformer, claimInformer, claimTemplateInformer) + features := resourceclaim.Features{ + AdminAccess: true, + PrioritizedList: true, + } + claimController, err := resourceclaim.NewController(klog.FromContext(ctx), features, clientSet, podInformer, claimInformer, claimTemplateInformer) if err != nil { tb.Fatalf("Error creating claim controller: %v", err) }