@@ -119,10 +119,41 @@ func validateDeviceClaim(deviceClaim *resource.DeviceClaim, fldPath *field.Path,
119
119
return allErrs
120
120
}
121
121
122
- func gatherRequestNames (deviceClaim * resource.DeviceClaim ) sets.Set [string ] {
123
- requestNames := sets .New [string ]()
122
+ type requestNames map [string ]sets.Set [string ]
123
+
124
+ func (r requestNames ) Has (s string ) bool {
125
+ segments := strings .Split (s , "/" )
126
+ // If there are more than one / in the string, we
127
+ // know there can't be any match.
128
+ if len (segments ) > 2 {
129
+ return false
130
+ }
131
+ // If the first segment doesn't have a match, we
132
+ // don't need to check the other one.
133
+ subRequestNames , found := r [segments [0 ]]
134
+ if ! found {
135
+ return false
136
+ }
137
+ if len (segments ) == 1 {
138
+ return true
139
+ }
140
+ // If the first segment matched and we have another one,
141
+ // check for a match for that too.
142
+ return subRequestNames .Has (segments [1 ])
143
+ }
144
+
145
+ func gatherRequestNames (deviceClaim * resource.DeviceClaim ) requestNames {
146
+ requestNames := make (requestNames )
124
147
for _ , request := range deviceClaim .Requests {
125
- requestNames .Insert (request .Name )
148
+ if len (request .FirstAvailable ) == 0 {
149
+ requestNames [request .Name ] = nil
150
+ continue
151
+ }
152
+ subRequestNames := sets .New [string ]()
153
+ for _ , subRequest := range request .FirstAvailable {
154
+ subRequestNames .Insert (subRequest .Name )
155
+ }
156
+ requestNames [request .Name ] = subRequestNames
126
157
}
127
158
return requestNames
128
159
}
@@ -138,31 +169,81 @@ func gatherAllocatedDevices(allocationResult *resource.DeviceAllocationResult) s
138
169
139
170
func validateDeviceRequest (request resource.DeviceRequest , fldPath * field.Path , stored bool ) field.ErrorList {
140
171
allErrs := validateRequestName (request .Name , fldPath .Child ("name" ))
141
- if request .DeviceClassName == "" {
142
- allErrs = append (allErrs , field .Required (fldPath .Child ("deviceClassName" ), "" ))
143
- } else {
144
- allErrs = append (allErrs , validateDeviceClassName (request .DeviceClassName , fldPath .Child ("deviceClassName" ))... )
172
+ if len (request .FirstAvailable ) > 0 {
173
+ if request .DeviceClassName != "" {
174
+ allErrs = append (allErrs , field .Invalid (fldPath .Child ("deviceClassName" ), request .DeviceClassName , "must not be specified when firstAvailable is set" ))
175
+ }
176
+ if request .Selectors != nil {
177
+ allErrs = append (allErrs , field .Invalid (fldPath .Child ("selectors" ), request .Selectors , "must not be specified when firstAvailable is set" ))
178
+ }
179
+ if request .AllocationMode != "" {
180
+ allErrs = append (allErrs , field .Invalid (fldPath .Child ("allocationMode" ), request .AllocationMode , "must not be specified when firstAvailable is set" ))
181
+ }
182
+ if request .Count != 0 {
183
+ allErrs = append (allErrs , field .Invalid (fldPath .Child ("count" ), request .Count , "must not be specified when firstAvailable is set" ))
184
+ }
185
+ if request .AdminAccess != nil {
186
+ allErrs = append (allErrs , field .Invalid (fldPath .Child ("adminAccess" ), request .AdminAccess , "must not be specified when firstAvailable is set" ))
187
+ }
188
+ allErrs = append (allErrs , validateSet (request .FirstAvailable , resource .FirstAvailableDeviceRequestMaxSize ,
189
+ func (subRequest resource.DeviceSubRequest , fldPath * field.Path ) field.ErrorList {
190
+ return validateDeviceSubRequest (subRequest , fldPath , stored )
191
+ },
192
+ func (subRequest resource.DeviceSubRequest ) (string , string ) {
193
+ return subRequest .Name , "name"
194
+ },
195
+ fldPath .Child ("firstAvailable" ))... )
196
+ return allErrs
145
197
}
146
- allErrs = append (allErrs , validateSlice (request .Selectors , resource .DeviceSelectorsMaxSize ,
147
- func (selector resource.DeviceSelector , fldPath * field.Path ) field.ErrorList {
148
- return validateSelector (selector , fldPath , stored )
149
- },
150
- fldPath .Child ("selectors" ))... )
151
- switch request .AllocationMode {
198
+ allErrs = append (allErrs , validateDeviceClass (request .DeviceClassName , fldPath .Child ("deviceClassName" ))... )
199
+ allErrs = append (allErrs , validateSelectorSlice (request .Selectors , fldPath .Child ("selectors" ), stored )... )
200
+ allErrs = append (allErrs , validateDeviceAllocationMode (request .AllocationMode , request .Count , fldPath .Child ("allocationMode" ), fldPath .Child ("count" ))... )
201
+ return allErrs
202
+ }
203
+
204
+ func validateDeviceSubRequest (subRequest resource.DeviceSubRequest , fldPath * field.Path , stored bool ) field.ErrorList {
205
+ allErrs := validateRequestName (subRequest .Name , fldPath .Child ("name" ))
206
+ allErrs = append (allErrs , validateDeviceClass (subRequest .DeviceClassName , fldPath .Child ("deviceClassName" ))... )
207
+ allErrs = append (allErrs , validateSelectorSlice (subRequest .Selectors , fldPath .Child ("selectors" ), stored )... )
208
+ allErrs = append (allErrs , validateDeviceAllocationMode (subRequest .AllocationMode , subRequest .Count , fldPath .Child ("allocationMode" ), fldPath .Child ("count" ))... )
209
+ return allErrs
210
+ }
211
+
212
+ func validateDeviceAllocationMode (deviceAllocationMode resource.DeviceAllocationMode , count int64 , allocModeFldPath , countFldPath * field.Path ) field.ErrorList {
213
+ var allErrs field.ErrorList
214
+ switch deviceAllocationMode {
152
215
case resource .DeviceAllocationModeAll :
153
- if request . Count != 0 {
154
- allErrs = append (allErrs , field .Invalid (fldPath . Child ( "count" ), request . Count , fmt .Sprintf ("must not be specified when allocationMode is '%s'" , request . AllocationMode )))
216
+ if count != 0 {
217
+ allErrs = append (allErrs , field .Invalid (countFldPath , count , fmt .Sprintf ("must not be specified when allocationMode is '%s'" , deviceAllocationMode )))
155
218
}
156
219
case resource .DeviceAllocationModeExactCount :
157
- if request . Count <= 0 {
158
- allErrs = append (allErrs , field .Invalid (fldPath . Child ( "count" ), request . Count , "must be greater than zero" ))
220
+ if count <= 0 {
221
+ allErrs = append (allErrs , field .Invalid (countFldPath , count , "must be greater than zero" ))
159
222
}
160
223
default :
161
- allErrs = append (allErrs , field .NotSupported (fldPath .Child ("allocationMode" ), request .AllocationMode , []resource.DeviceAllocationMode {resource .DeviceAllocationModeAll , resource .DeviceAllocationModeExactCount }))
224
+ allErrs = append (allErrs , field .NotSupported (allocModeFldPath , deviceAllocationMode , []resource.DeviceAllocationMode {resource .DeviceAllocationModeAll , resource .DeviceAllocationModeExactCount }))
225
+ }
226
+ return allErrs
227
+ }
228
+
229
+ func validateDeviceClass (deviceClass string , fldPath * field.Path ) field.ErrorList {
230
+ var allErrs field.ErrorList
231
+ if deviceClass == "" {
232
+ allErrs = append (allErrs , field .Required (fldPath , "" ))
233
+ } else {
234
+ allErrs = append (allErrs , validateDeviceClassName (deviceClass , fldPath )... )
162
235
}
163
236
return allErrs
164
237
}
165
238
239
+ func validateSelectorSlice (selectors []resource.DeviceSelector , fldPath * field.Path , stored bool ) field.ErrorList {
240
+ return validateSlice (selectors , resource .DeviceSelectorsMaxSize ,
241
+ func (selector resource.DeviceSelector , fldPath * field.Path ) field.ErrorList {
242
+ return validateSelector (selector , fldPath , stored )
243
+ },
244
+ fldPath )
245
+ }
246
+
166
247
func validateSelector (selector resource.DeviceSelector , fldPath * field.Path , stored bool ) field.ErrorList {
167
248
var allErrs field.ErrorList
168
249
if selector .CEL == nil {
@@ -210,7 +291,7 @@ func convertCELErrorToValidationError(fldPath *field.Path, expression string, er
210
291
return field .InternalError (fldPath , fmt .Errorf ("unsupported error type: %w" , err ))
211
292
}
212
293
213
- func validateDeviceConstraint (constraint resource.DeviceConstraint , fldPath * field.Path , requestNames sets. Set [ string ] ) field.ErrorList {
294
+ func validateDeviceConstraint (constraint resource.DeviceConstraint , fldPath * field.Path , requestNames requestNames ) field.ErrorList {
214
295
var allErrs field.ErrorList
215
296
allErrs = append (allErrs , validateSet (constraint .Requests , resource .DeviceRequestsMaxSize ,
216
297
func (name string , fldPath * field.Path ) field.ErrorList {
@@ -225,7 +306,7 @@ func validateDeviceConstraint(constraint resource.DeviceConstraint, fldPath *fie
225
306
return allErrs
226
307
}
227
308
228
- func validateDeviceClaimConfiguration (config resource.DeviceClaimConfiguration , fldPath * field.Path , requestNames sets. Set [ string ] , stored bool ) field.ErrorList {
309
+ func validateDeviceClaimConfiguration (config resource.DeviceClaimConfiguration , fldPath * field.Path , requestNames requestNames , stored bool ) field.ErrorList {
229
310
var allErrs field.ErrorList
230
311
allErrs = append (allErrs , validateSet (config .Requests , resource .DeviceRequestsMaxSize ,
231
312
func (name string , fldPath * field.Path ) field.ErrorList {
@@ -235,10 +316,20 @@ func validateDeviceClaimConfiguration(config resource.DeviceClaimConfiguration,
235
316
return allErrs
236
317
}
237
318
238
- func validateRequestNameRef (name string , fldPath * field.Path , requestNames sets.Set [string ]) field.ErrorList {
239
- allErrs := validateRequestName (name , fldPath )
319
+ func validateRequestNameRef (name string , fldPath * field.Path , requestNames requestNames ) field.ErrorList {
320
+ var allErrs field.ErrorList
321
+ segments := strings .Split (name , "/" )
322
+ if len (segments ) > 2 {
323
+ allErrs = append (allErrs , field .Invalid (fldPath , name , "must be the name of a request in the claim or the name of a request and a subrequest separated by '/'" ))
324
+ return allErrs
325
+ }
326
+
327
+ for i := range segments {
328
+ allErrs = append (allErrs , validateRequestName (segments [i ], fldPath )... )
329
+ }
330
+
240
331
if ! requestNames .Has (name ) {
241
- allErrs = append (allErrs , field .Invalid (fldPath , name , "must be the name of a request in the claim" ))
332
+ allErrs = append (allErrs , field .Invalid (fldPath , name , "must be the name of a request in the claim or the name of a request and a subrequest separated by '/' " ))
242
333
}
243
334
return allErrs
244
335
}
@@ -260,7 +351,7 @@ func validateOpaqueConfiguration(config resource.OpaqueDeviceConfiguration, fldP
260
351
return allErrs
261
352
}
262
353
263
- func validateResourceClaimStatusUpdate (status , oldStatus * resource.ResourceClaimStatus , claimDeleted bool , requestNames sets. Set [ string ] , fldPath * field.Path ) field.ErrorList {
354
+ func validateResourceClaimStatusUpdate (status , oldStatus * resource.ResourceClaimStatus , claimDeleted bool , requestNames requestNames , fldPath * field.Path ) field.ErrorList {
264
355
var allErrs field.ErrorList
265
356
allErrs = append (allErrs , validateSet (status .ReservedFor , resource .ResourceClaimReservedForMaxSize ,
266
357
validateResourceClaimUserReference ,
@@ -328,7 +419,7 @@ func validateResourceClaimUserReference(ref resource.ResourceClaimConsumerRefere
328
419
// validateAllocationResult enforces constraints for *new* results, which in at
329
420
// least one case (admin access) are more strict than before. Therefore it
330
421
// may not be called to re-validate results which were stored earlier.
331
- func validateAllocationResult (allocation * resource.AllocationResult , fldPath * field.Path , requestNames sets. Set [ string ] , stored bool ) field.ErrorList {
422
+ func validateAllocationResult (allocation * resource.AllocationResult , fldPath * field.Path , requestNames requestNames , stored bool ) field.ErrorList {
332
423
var allErrs field.ErrorList
333
424
allErrs = append (allErrs , validateDeviceAllocationResult (allocation .Devices , fldPath .Child ("devices" ), requestNames , stored )... )
334
425
if allocation .NodeSelector != nil {
@@ -337,7 +428,7 @@ func validateAllocationResult(allocation *resource.AllocationResult, fldPath *fi
337
428
return allErrs
338
429
}
339
430
340
- func validateDeviceAllocationResult (allocation resource.DeviceAllocationResult , fldPath * field.Path , requestNames sets. Set [ string ] , stored bool ) field.ErrorList {
431
+ func validateDeviceAllocationResult (allocation resource.DeviceAllocationResult , fldPath * field.Path , requestNames requestNames , stored bool ) field.ErrorList {
341
432
var allErrs field.ErrorList
342
433
allErrs = append (allErrs , validateSlice (allocation .Results , resource .AllocationResultsMaxSize ,
343
434
func (result resource.DeviceRequestAllocationResult , fldPath * field.Path ) field.ErrorList {
@@ -351,7 +442,7 @@ func validateDeviceAllocationResult(allocation resource.DeviceAllocationResult,
351
442
return allErrs
352
443
}
353
444
354
- func validateDeviceRequestAllocationResult (result resource.DeviceRequestAllocationResult , fldPath * field.Path , requestNames sets. Set [ string ] ) field.ErrorList {
445
+ func validateDeviceRequestAllocationResult (result resource.DeviceRequestAllocationResult , fldPath * field.Path , requestNames requestNames ) field.ErrorList {
355
446
var allErrs field.ErrorList
356
447
allErrs = append (allErrs , validateRequestNameRef (result .Request , fldPath .Child ("request" ), requestNames )... )
357
448
allErrs = append (allErrs , validateDriverName (result .Driver , fldPath .Child ("driver" ))... )
@@ -360,7 +451,7 @@ func validateDeviceRequestAllocationResult(result resource.DeviceRequestAllocati
360
451
return allErrs
361
452
}
362
453
363
- func validateDeviceAllocationConfiguration (config resource.DeviceAllocationConfiguration , fldPath * field.Path , requestNames sets. Set [ string ] , stored bool ) field.ErrorList {
454
+ func validateDeviceAllocationConfiguration (config resource.DeviceAllocationConfiguration , fldPath * field.Path , requestNames requestNames , stored bool ) field.ErrorList {
364
455
var allErrs field.ErrorList
365
456
allErrs = append (allErrs , validateAllocationConfigSource (config .Source , fldPath .Child ("source" ))... )
366
457
allErrs = append (allErrs , validateSet (config .Requests , resource .DeviceRequestsMaxSize ,
0 commit comments