Skip to content

Commit daef8c2

Browse files
authored
Merge pull request kubernetes#127266 from pohly/dra-admin-access-in-status
DRA API: AdminAccess in DeviceRequestAllocationResult + DRAAdminAccess feature gate
2 parents 5fcef4f + 4419568 commit daef8c2

File tree

43 files changed

+1067
-270
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1067
-270
lines changed

api/openapi-spec/swagger.json

Lines changed: 5 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

api/openapi-spec/v3/apis__resource.k8s.io__v1alpha3_openapi.json

Lines changed: 5 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cmd/kube-controller-manager/app/core.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -407,6 +407,7 @@ func newResourceClaimControllerDescriptor() *ControllerDescriptor {
407407
func startResourceClaimController(ctx context.Context, controllerContext ControllerContext, controllerName string) (controller.Interface, bool, error) {
408408
ephemeralController, err := resourceclaim.NewController(
409409
klog.FromContext(ctx),
410+
utilfeature.DefaultFeatureGate.Enabled(features.DRAAdminAccess),
410411
controllerContext.ClientBuilder.ClientOrDie("resource-claim-controller"),
411412
controllerContext.InformerFactory.Core().V1().Pods(),
412413
controllerContext.InformerFactory.Resource().V1alpha3().ResourceClaims(),

pkg/apis/resource/types.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -442,9 +442,13 @@ type DeviceRequest struct {
442442
// all ordinary claims to the device with respect to access modes and
443443
// any resource allocations.
444444
//
445+
// This is an alpha field and requires enabling the DRAAdminAccess
446+
// feature gate. Admin access is disabled if this field is unset or
447+
// set to false, otherwise it is enabled.
448+
//
445449
// +optional
446-
// +default=false
447-
AdminAccess bool
450+
// +featureGate=DRAAdminAccess
451+
AdminAccess *bool
448452
}
449453

450454
const (
@@ -782,6 +786,18 @@ type DeviceRequestAllocationResult struct {
782786
//
783787
// +required
784788
Device string
789+
790+
// AdminAccess indicates that this device was allocated for
791+
// administrative access. See the corresponding request field
792+
// for a definition of mode.
793+
//
794+
// This is an alpha field and requires enabling the DRAAdminAccess
795+
// feature gate. Admin access is disabled if this field is unset or
796+
// set to false, otherwise it is enabled.
797+
//
798+
// +optional
799+
// +featureGate=DRAAdminAccess
800+
AdminAccess *bool
785801
}
786802

787803
// DeviceAllocationConfiguration gets embedded in an AllocationResult.

pkg/apis/resource/v1alpha3/zz_generated.conversion.go

Lines changed: 4 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/resource/validation/validation.go

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,6 @@ func validateOpaqueConfiguration(config resource.OpaqueDeviceConfiguration, fldP
261261

262262
func validateResourceClaimStatusUpdate(status, oldStatus *resource.ResourceClaimStatus, claimDeleted bool, requestNames sets.Set[string], fldPath *field.Path) field.ErrorList {
263263
var allErrs field.ErrorList
264-
allErrs = append(allErrs, validateAllocationResult(status.Allocation, fldPath.Child("allocation"), requestNames)...)
265264
allErrs = append(allErrs, validateSet(status.ReservedFor, resource.ResourceClaimReservedForMaxSize,
266265
validateResourceClaimUserReference,
267266
func(consumer resource.ResourceClaimConsumerReference) (types.UID, string) { return consumer.UID, "uid" },
@@ -285,9 +284,14 @@ func validateResourceClaimStatusUpdate(status, oldStatus *resource.ResourceClaim
285284
}
286285
}
287286

288-
// Updates to a populated status.Allocation are not allowed
287+
// Updates to a populated status.Allocation are not allowed.
288+
// Unmodified fields don't need to be validated again and,
289+
// in this particular case, must not be validated again because
290+
// validation for new results is tighter than it was before.
289291
if oldStatus.Allocation != nil && status.Allocation != nil {
290292
allErrs = append(allErrs, apimachineryvalidation.ValidateImmutableField(status.Allocation, oldStatus.Allocation, fldPath.Child("allocation"))...)
293+
} else if status.Allocation != nil {
294+
allErrs = append(allErrs, validateAllocationResult(status.Allocation, fldPath.Child("allocation"), requestNames)...)
291295
}
292296

293297
return allErrs
@@ -307,11 +311,10 @@ func validateResourceClaimUserReference(ref resource.ResourceClaimConsumerRefere
307311
return allErrs
308312
}
309313

314+
// validateAllocationResult enforces constraints for *new* results, which in at
315+
// least one case (admin access) are more strict than before. Therefore it
316+
// may not be called to re-validate results which were stored earlier.
310317
func validateAllocationResult(allocation *resource.AllocationResult, fldPath *field.Path, requestNames sets.Set[string]) field.ErrorList {
311-
if allocation == nil {
312-
return nil
313-
}
314-
315318
var allErrs field.ErrorList
316319
allErrs = append(allErrs, validateDeviceAllocationResult(allocation.Devices, fldPath.Child("devices"), requestNames)...)
317320
if allocation.NodeSelector != nil {

pkg/apis/resource/validation/validation_resourceclaim_test.go

Lines changed: 54 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,11 @@ import (
2727
"k8s.io/apimachinery/pkg/runtime"
2828
"k8s.io/apimachinery/pkg/types"
2929
"k8s.io/apimachinery/pkg/util/validation/field"
30+
utilfeature "k8s.io/apiserver/pkg/util/feature"
31+
featuregatetesting "k8s.io/component-base/featuregate/testing"
3032
"k8s.io/kubernetes/pkg/apis/core"
3133
"k8s.io/kubernetes/pkg/apis/resource"
34+
"k8s.io/kubernetes/pkg/features"
3235
"k8s.io/utils/pointer"
3336
"k8s.io/utils/ptr"
3437
)
@@ -408,16 +411,20 @@ func TestValidateClaimStatusUpdate(t *testing.T) {
408411
Allocation: &resource.AllocationResult{
409412
Devices: resource.DeviceAllocationResult{
410413
Results: []resource.DeviceRequestAllocationResult{{
411-
Request: goodName,
412-
Driver: goodName,
413-
Pool: goodName,
414-
Device: goodName,
414+
Request: goodName,
415+
Driver: goodName,
416+
Pool: goodName,
417+
Device: goodName,
418+
AdminAccess: ptr.To(false), // Required for new allocations.
415419
}},
416420
},
417421
},
418422
}
423+
validAllocatedClaimOld := validAllocatedClaim.DeepCopy()
424+
validAllocatedClaimOld.Status.Allocation.Devices.Results[0].AdminAccess = nil // Not required in 1.31.
419425

420426
scenarios := map[string]struct {
427+
adminAccess bool
421428
oldClaim *resource.ResourceClaim
422429
update func(claim *resource.ResourceClaim) *resource.ResourceClaim
423430
wantFailures field.ErrorList
@@ -439,10 +446,11 @@ func TestValidateClaimStatusUpdate(t *testing.T) {
439446
claim.Status.Allocation = &resource.AllocationResult{
440447
Devices: resource.DeviceAllocationResult{
441448
Results: []resource.DeviceRequestAllocationResult{{
442-
Request: goodName,
443-
Driver: goodName,
444-
Pool: goodName,
445-
Device: goodName,
449+
Request: goodName,
450+
Driver: goodName,
451+
Pool: goodName,
452+
Device: goodName,
453+
AdminAccess: ptr.To(false),
446454
}},
447455
},
448456
}
@@ -459,10 +467,29 @@ func TestValidateClaimStatusUpdate(t *testing.T) {
459467
claim.Status.Allocation = &resource.AllocationResult{
460468
Devices: resource.DeviceAllocationResult{
461469
Results: []resource.DeviceRequestAllocationResult{{
462-
Request: badName,
463-
Driver: goodName,
464-
Pool: goodName,
465-
Device: goodName,
470+
Request: badName,
471+
Driver: goodName,
472+
Pool: goodName,
473+
Device: goodName,
474+
AdminAccess: ptr.To(false),
475+
}},
476+
},
477+
}
478+
return claim
479+
},
480+
},
481+
"okay-add-allocation-missing-admin-access": {
482+
adminAccess: false,
483+
oldClaim: validClaim,
484+
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
485+
claim.Status.Allocation = &resource.AllocationResult{
486+
Devices: resource.DeviceAllocationResult{
487+
Results: []resource.DeviceRequestAllocationResult{{
488+
Request: goodName,
489+
Driver: goodName,
490+
Pool: goodName,
491+
Device: goodName,
492+
AdminAccess: nil, // Intentionally not set.
466493
}},
467494
},
468495
}
@@ -495,6 +522,20 @@ func TestValidateClaimStatusUpdate(t *testing.T) {
495522
return claim
496523
},
497524
},
525+
"add-reservation-old-claim": {
526+
oldClaim: validAllocatedClaimOld,
527+
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
528+
for i := 0; i < resource.ResourceClaimReservedForMaxSize; i++ {
529+
claim.Status.ReservedFor = append(claim.Status.ReservedFor,
530+
resource.ResourceClaimConsumerReference{
531+
Resource: "pods",
532+
Name: fmt.Sprintf("foo-%d", i),
533+
UID: types.UID(fmt.Sprintf("%d", i)),
534+
})
535+
}
536+
return claim
537+
},
538+
},
498539
"add-reservation-and-allocation": {
499540
oldClaim: validClaim,
500541
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
@@ -652,6 +693,7 @@ func TestValidateClaimStatusUpdate(t *testing.T) {
652693

653694
for name, scenario := range scenarios {
654695
t.Run(name, func(t *testing.T) {
696+
featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DRAAdminAccess, scenario.adminAccess)
655697
scenario.oldClaim.ResourceVersion = "1"
656698
errs := ValidateResourceClaimStatusUpdate(scenario.update(scenario.oldClaim.DeepCopy()), scenario.oldClaim)
657699
assert.Equal(t, scenario.wantFailures, errs)

pkg/apis/resource/zz_generated.deepcopy.go

Lines changed: 13 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/controller/resourceclaim/controller.go

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,9 @@ const (
7171

7272
// Controller creates ResourceClaims for ResourceClaimTemplates in a pod spec.
7373
type Controller struct {
74+
// adminAccessEnabled matches the DRAAdminAccess feature gate state.
75+
adminAccessEnabled bool
76+
7477
// kubeClient is the kube API client used to communicate with the API
7578
// server.
7679
kubeClient clientset.Interface
@@ -118,20 +121,22 @@ const (
118121
// NewController creates a ResourceClaim controller.
119122
func NewController(
120123
logger klog.Logger,
124+
adminAccessEnabled bool,
121125
kubeClient clientset.Interface,
122126
podInformer v1informers.PodInformer,
123127
claimInformer resourceinformers.ResourceClaimInformer,
124128
templateInformer resourceinformers.ResourceClaimTemplateInformer) (*Controller, error) {
125129

126130
ec := &Controller{
127-
kubeClient: kubeClient,
128-
podLister: podInformer.Lister(),
129-
podIndexer: podInformer.Informer().GetIndexer(),
130-
podSynced: podInformer.Informer().HasSynced,
131-
claimLister: claimInformer.Lister(),
132-
claimsSynced: claimInformer.Informer().HasSynced,
133-
templateLister: templateInformer.Lister(),
134-
templatesSynced: templateInformer.Informer().HasSynced,
131+
adminAccessEnabled: adminAccessEnabled,
132+
kubeClient: kubeClient,
133+
podLister: podInformer.Lister(),
134+
podIndexer: podInformer.Informer().GetIndexer(),
135+
podSynced: podInformer.Informer().HasSynced,
136+
claimLister: claimInformer.Lister(),
137+
claimsSynced: claimInformer.Informer().HasSynced,
138+
templateLister: templateInformer.Lister(),
139+
templatesSynced: templateInformer.Informer().HasSynced,
135140
queue: workqueue.NewTypedRateLimitingQueueWithConfig(
136141
workqueue.DefaultTypedControllerRateLimiter[string](),
137142
workqueue.TypedRateLimitingQueueConfig[string]{Name: "resource_claim"},
@@ -612,6 +617,10 @@ func (ec *Controller) handleClaim(ctx context.Context, pod *v1.Pod, podClaim v1.
612617
return fmt.Errorf("resource claim template %q: %v", *templateName, err)
613618
}
614619

620+
if !ec.adminAccessEnabled && needsAdminAccess(template) {
621+
return errors.New("admin access is requested, but the feature is disabled")
622+
}
623+
615624
// Create the ResourceClaim with pod as owner, with a generated name that uses
616625
// <pod>-<claim name> as base.
617626
isTrue := true
@@ -670,6 +679,15 @@ func (ec *Controller) handleClaim(ctx context.Context, pod *v1.Pod, podClaim v1.
670679
return nil
671680
}
672681

682+
func needsAdminAccess(claimTemplate *resourceapi.ResourceClaimTemplate) bool {
683+
for _, request := range claimTemplate.Spec.Spec.Devices.Requests {
684+
if ptr.Deref(request.AdminAccess, false) {
685+
return true
686+
}
687+
}
688+
return false
689+
}
690+
673691
// findPodResourceClaim looks for an existing ResourceClaim with the right
674692
// annotation (ties it to the pod claim) and the right ownership (ties it to
675693
// the pod).

0 commit comments

Comments
 (0)