Skip to content

Commit f3fef01

Browse files
committed
DRA API: AdminAccess in DeviceRequestAllocationResult
Drivers need to know that because admin access may also grant additional permissions. The allocator needs to ignore such results when determining which devices are considered as allocated. In both cases it is conceptually cleaner to not rely on the content of the ClaimSpec.
1 parent 66b3dc1 commit f3fef01

24 files changed

+495
-208
lines changed

api/openapi-spec/swagger.json

Lines changed: 6 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: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pkg/apis/resource/types.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,17 @@ type DeviceRequestAllocationResult struct {
782782
//
783783
// +required
784784
Device string
785+
786+
// AdminAccess is a copy of the AdminAccess value in the
787+
// request which caused this device to be allocated.
788+
//
789+
// New allocations are required to have this set. Old allocations made
790+
// by Kubernetes 1.31 do not have it yet. Clients which want to
791+
// support Kubernetes 1.31 need to look up the request and retrieve
792+
// the value from there if this field is not set.
793+
//
794+
// +required
795+
AdminAccess *bool
785796
}
786797

787798
// DeviceAllocationConfiguration gets embedded in an AllocationResult.

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

Lines changed: 2 additions & 0 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: 12 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 {
@@ -340,6 +343,9 @@ func validateDeviceRequestAllocationResult(result resource.DeviceRequestAllocati
340343
allErrs = append(allErrs, validateDriverName(result.Driver, fldPath.Child("driver"))...)
341344
allErrs = append(allErrs, validatePoolName(result.Pool, fldPath.Child("pool"))...)
342345
allErrs = append(allErrs, validateDeviceName(result.Device, fldPath.Child("device"))...)
346+
if result.AdminAccess == nil {
347+
allErrs = append(allErrs, field.Required(fldPath.Child("adminAccess"), ""))
348+
}
343349
return allErrs
344350
}
345351

pkg/apis/resource/validation/validation_resourceclaim_test.go

Lines changed: 51 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -408,14 +408,17 @@ func TestValidateClaimStatusUpdate(t *testing.T) {
408408
Allocation: &resource.AllocationResult{
409409
Devices: resource.DeviceAllocationResult{
410410
Results: []resource.DeviceRequestAllocationResult{{
411-
Request: goodName,
412-
Driver: goodName,
413-
Pool: goodName,
414-
Device: goodName,
411+
Request: goodName,
412+
Driver: goodName,
413+
Pool: goodName,
414+
Device: goodName,
415+
AdminAccess: ptr.To(false), // Required for new allocations.
415416
}},
416417
},
417418
},
418419
}
420+
validAllocatedClaimOld := validAllocatedClaim.DeepCopy()
421+
validAllocatedClaimOld.Status.Allocation.Devices.Results[0].AdminAccess = nil // Not required in 1.31.
419422

420423
scenarios := map[string]struct {
421424
oldClaim *resource.ResourceClaim
@@ -439,10 +442,11 @@ func TestValidateClaimStatusUpdate(t *testing.T) {
439442
claim.Status.Allocation = &resource.AllocationResult{
440443
Devices: resource.DeviceAllocationResult{
441444
Results: []resource.DeviceRequestAllocationResult{{
442-
Request: goodName,
443-
Driver: goodName,
444-
Pool: goodName,
445-
Device: goodName,
445+
Request: goodName,
446+
Driver: goodName,
447+
Pool: goodName,
448+
Device: goodName,
449+
AdminAccess: ptr.To(false),
446450
}},
447451
},
448452
}
@@ -459,10 +463,31 @@ func TestValidateClaimStatusUpdate(t *testing.T) {
459463
claim.Status.Allocation = &resource.AllocationResult{
460464
Devices: resource.DeviceAllocationResult{
461465
Results: []resource.DeviceRequestAllocationResult{{
462-
Request: badName,
463-
Driver: goodName,
464-
Pool: goodName,
465-
Device: goodName,
466+
Request: badName,
467+
Driver: goodName,
468+
Pool: goodName,
469+
Device: goodName,
470+
AdminAccess: ptr.To(false),
471+
}},
472+
},
473+
}
474+
return claim
475+
},
476+
},
477+
"invalid-add-allocation-missing-admin-access": {
478+
wantFailures: field.ErrorList{
479+
field.Required(field.NewPath("status", "allocation", "devices", "results").Index(0).Child("adminAccess"), ""),
480+
},
481+
oldClaim: validClaim,
482+
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
483+
claim.Status.Allocation = &resource.AllocationResult{
484+
Devices: resource.DeviceAllocationResult{
485+
Results: []resource.DeviceRequestAllocationResult{{
486+
Request: goodName,
487+
Driver: goodName,
488+
Pool: goodName,
489+
Device: goodName,
490+
AdminAccess: nil, // Intentionally not set.
466491
}},
467492
},
468493
}
@@ -495,6 +520,20 @@ func TestValidateClaimStatusUpdate(t *testing.T) {
495520
return claim
496521
},
497522
},
523+
"add-reservation-old-claim": {
524+
oldClaim: validAllocatedClaimOld,
525+
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {
526+
for i := 0; i < resource.ResourceClaimReservedForMaxSize; i++ {
527+
claim.Status.ReservedFor = append(claim.Status.ReservedFor,
528+
resource.ResourceClaimConsumerReference{
529+
Resource: "pods",
530+
Name: fmt.Sprintf("foo-%d", i),
531+
UID: types.UID(fmt.Sprintf("%d", i)),
532+
})
533+
}
534+
return claim
535+
},
536+
},
498537
"add-reservation-and-allocation": {
499538
oldClaim: validClaim,
500539
update: func(claim *resource.ResourceClaim) *resource.ResourceClaim {

pkg/apis/resource/zz_generated.deepcopy.go

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

pkg/generated/openapi/zz_generated.openapi.go

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

pkg/scheduler/framework/plugins/dynamicresources/dynamicresources_test.go

Lines changed: 78 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,10 +135,11 @@ var (
135135
allocationResult = &resourceapi.AllocationResult{
136136
Devices: resourceapi.DeviceAllocationResult{
137137
Results: []resourceapi.DeviceRequestAllocationResult{{
138-
Driver: driver,
139-
Pool: nodeName,
140-
Device: "instance-1",
141-
Request: "req-1",
138+
Driver: driver,
139+
Pool: nodeName,
140+
Device: "instance-1",
141+
Request: "req-1",
142+
AdminAccess: ptr.To(false),
142143
}},
143144
},
144145
NodeSelector: func() *v1.NodeSelector {
@@ -178,6 +179,19 @@ func reserve(claim *resourceapi.ResourceClaim, pod *v1.Pod) *resourceapi.Resourc
178179
Obj()
179180
}
180181

182+
func adminAccess(claim *resourceapi.ResourceClaim) *resourceapi.ResourceClaim {
183+
claim = claim.DeepCopy()
184+
for i := range claim.Spec.Devices.Requests {
185+
claim.Spec.Devices.Requests[i].AdminAccess = true
186+
}
187+
if claim.Status.Allocation != nil {
188+
for i := range claim.Status.Allocation.Devices.Results {
189+
claim.Status.Allocation.Devices.Results[i].AdminAccess = ptr.To(true)
190+
}
191+
}
192+
return claim
193+
}
194+
181195
func breakCELInClaim(claim *resourceapi.ResourceClaim) *resourceapi.ResourceClaim {
182196
claim = claim.DeepCopy()
183197
for i := range claim.Spec.Devices.Requests {
@@ -556,6 +570,66 @@ func TestPlugin(t *testing.T) {
556570
},
557571
},
558572

573+
"request-admin-access": {
574+
// Because the pending claim asks for admin access, allocation succeeds despite resources
575+
// being exhausted.
576+
pod: podWithClaimName,
577+
claims: []*resourceapi.ResourceClaim{adminAccess(pendingClaim), otherAllocatedClaim},
578+
classes: []*resourceapi.DeviceClass{deviceClass},
579+
objs: []apiruntime.Object{workerNodeSlice},
580+
want: want{
581+
reserve: result{
582+
inFlightClaim: adminAccess(allocatedClaim),
583+
},
584+
prebind: result{
585+
assumedClaim: reserve(adminAccess(allocatedClaim), podWithClaimName),
586+
changes: change{
587+
claim: func(claim *resourceapi.ResourceClaim) *resourceapi.ResourceClaim {
588+
if claim.Name == claimName {
589+
claim = claim.DeepCopy()
590+
claim.Finalizers = allocatedClaim.Finalizers
591+
claim.Status = adminAccess(inUseClaim).Status
592+
}
593+
return claim
594+
},
595+
},
596+
},
597+
postbind: result{
598+
assumedClaim: reserve(adminAccess(allocatedClaim), podWithClaimName),
599+
},
600+
},
601+
},
602+
603+
"structured-ignore-allocated-admin-access": {
604+
// The allocated claim uses admin access, so a second claim may use
605+
// the same device.
606+
pod: podWithClaimName,
607+
claims: []*resourceapi.ResourceClaim{pendingClaim, adminAccess(otherAllocatedClaim)},
608+
classes: []*resourceapi.DeviceClass{deviceClass},
609+
objs: []apiruntime.Object{workerNodeSlice},
610+
want: want{
611+
reserve: result{
612+
inFlightClaim: allocatedClaim,
613+
},
614+
prebind: result{
615+
assumedClaim: reserve(allocatedClaim, podWithClaimName),
616+
changes: change{
617+
claim: func(claim *resourceapi.ResourceClaim) *resourceapi.ResourceClaim {
618+
if claim.Name == claimName {
619+
claim = claim.DeepCopy()
620+
claim.Finalizers = allocatedClaim.Finalizers
621+
claim.Status = inUseClaim.Status
622+
}
623+
return claim
624+
},
625+
},
626+
},
627+
postbind: result{
628+
assumedClaim: reserve(allocatedClaim, podWithClaimName),
629+
},
630+
},
631+
},
632+
559633
"claim-parameters-CEL-runtime-error": {
560634
pod: podWithClaimName,
561635
claims: []*resourceapi.ResourceClaim{breakCELInClaim(pendingClaim)},

0 commit comments

Comments
 (0)