@@ -203,6 +203,48 @@ device.attributes["dra.example.com"]["version"].isGreaterThan(semver("0.0.1"))
203
203
expectMatch : true ,
204
204
expectCost : 12 ,
205
205
},
206
+ "check_attribute_domains" : {
207
+ expression : `device.attributes.exists_one(x, x == "dra.example.com")` ,
208
+ attributes : map [resourceapi.QualifiedName ]resourceapi.DeviceAttribute {"services" : {StringValue : ptr .To ("some_example_value" )}},
209
+ driver : "dra.example.com" ,
210
+ expectMatch : true ,
211
+ expectCost : 164 ,
212
+ },
213
+ "check_attribute_ids" : {
214
+ expression : `device.attributes["dra.example.com"].exists_one(x, x == "services")` ,
215
+ attributes : map [resourceapi.QualifiedName ]resourceapi.DeviceAttribute {"services" : {StringValue : ptr .To ("some_example_value" )}},
216
+ driver : "dra.example.com" ,
217
+ expectMatch : true ,
218
+ expectCost : 133 ,
219
+ },
220
+ "split_attribute" : {
221
+ expression : `device.attributes["dra.example.com"].services.split("example").size() >= 2` ,
222
+ attributes : map [resourceapi.QualifiedName ]resourceapi.DeviceAttribute {"services" : {StringValue : ptr .To ("some_example_value" )}},
223
+ driver : "dra.example.com" ,
224
+ expectMatch : true ,
225
+ expectCost : 19 ,
226
+ },
227
+ "regexp_attribute" : {
228
+ expression : `device.attributes["dra.example.com"].services.matches("[^a]?sym")` ,
229
+ attributes : map [resourceapi.QualifiedName ]resourceapi.DeviceAttribute {"services" : {StringValue : ptr .To ("asymetric" )}},
230
+ driver : "dra.example.com" ,
231
+ expectMatch : true ,
232
+ expectCost : 18 ,
233
+ },
234
+ "check_capacity_domains" : {
235
+ expression : `device.capacity.exists_one(x, x == "dra.example.com")` ,
236
+ capacity : map [resourceapi.QualifiedName ]resourceapi.DeviceCapacity {"memory" : {Value : resource .MustParse ("1Mi" )}},
237
+ driver : "dra.example.com" ,
238
+ expectMatch : true ,
239
+ expectCost : 164 ,
240
+ },
241
+ "check_capacity_ids" : {
242
+ expression : `device.capacity["dra.example.com"].exists_one(x, x == "memory")` ,
243
+ capacity : map [resourceapi.QualifiedName ]resourceapi.DeviceCapacity {"memory" : {Value : resource .MustParse ("1Mi" )}},
244
+ driver : "dra.example.com" ,
245
+ expectMatch : true ,
246
+ expectCost : 133 ,
247
+ },
206
248
"expensive" : {
207
249
// The worst-case is based on the maximum number of
208
250
// attributes and the maximum attribute name length.
@@ -214,21 +256,18 @@ device.attributes["dra.example.com"]["version"].isGreaterThan(semver("0.0.1"))
214
256
attribute := resourceapi.DeviceAttribute {
215
257
StringValue : ptr .To ("abc" ),
216
258
}
217
- // If the cost estimate was accurate, using exactly as many attributes
218
- // as allowed at most should exceed the limit. In practice, the estimate
219
- // is an upper bound and significantly more attributes are needed before
220
- // the runtime cost becomes too large.
221
- for i := 0 ; i < 1000 * resourceapi .ResourceSliceMaxAttributesAndCapacitiesPerDevice ; i ++ {
259
+ for i := 0 ; i < resourceapi .ResourceSliceMaxAttributesAndCapacitiesPerDevice ; i ++ {
222
260
suffix := fmt .Sprintf ("-%d" , i )
223
261
name := prefix + strings .Repeat ("x" , resourceapi .DeviceMaxIDLength - len (suffix )) + suffix
224
262
attributes [resourceapi .QualifiedName (name )] = attribute
225
263
}
226
264
return attributes
227
265
}(),
228
- expression : `device.attributes["dra.example.com"].map(s, s.lowerAscii()).map(s, s.size()).sum() == 0` ,
266
+ // From https://github.com/kubernetes/kubernetes/blob/50fc400f178d2078d0ca46aee955ee26375fc437/test/integration/apiserver/cel/validatingadmissionpolicy_test.go#L2150.
267
+ expression : `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(x, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(y, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z2, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z3, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z4, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].all(z5, int('1'.find('[0-9]*')) < 100)))))))` ,
229
268
driver : "dra.example.com" ,
230
269
expectMatchError : "actual cost limit exceeded" ,
231
- expectCost : 18446744073709551615 , // Exceeds limit!
270
+ expectCost : 85555551 , // Exceed limit!
232
271
},
233
272
}
234
273
@@ -238,50 +277,50 @@ func TestCEL(t *testing.T) {
238
277
_ , ctx := ktesting .NewTestContext (t )
239
278
result := GetCompiler ().CompileCELExpression (scenario .expression , Options {})
240
279
if scenario .expectCompileError != "" && result .Error == nil {
241
- t .Fatalf ("expected compile error %q, got none" , scenario .expectCompileError )
280
+ t .Fatalf ("FAILURE: expected compile error %q, got none" , scenario .expectCompileError )
242
281
}
243
282
if result .Error != nil {
244
283
if scenario .expectCompileError == "" {
245
- t .Fatalf ("unexpected compile error: %v" , result .Error )
284
+ t .Fatalf ("FAILURE: unexpected compile error: %v" , result .Error )
246
285
}
247
286
if ! strings .Contains (result .Error .Error (), scenario .expectCompileError ) {
248
- t .Fatalf ("expected compile error to contain %q, but got instead: %v" , scenario .expectCompileError , result .Error )
287
+ t .Fatalf ("FAILURE: expected compile error to contain %q, but got instead: %v" , scenario .expectCompileError , result .Error )
249
288
}
250
289
return
251
290
}
252
291
if scenario .expectCompileError != "" {
253
- t .Fatalf ("expected compile error %q, got none" , scenario .expectCompileError )
292
+ t .Fatalf ("FAILURE: expected compile error %q, got none" , scenario .expectCompileError )
254
293
}
255
294
if expect , actual := scenario .expectCost , result .MaxCost ; expect != actual {
256
- t .Errorf ("expected CEL cost %d, got %d instead" , expect , actual )
295
+ t .Errorf ("ERROR: expected CEL cost %d, got %d instead (%.0f%% of limit %d) " , expect , actual , float64 ( actual ) * 100.0 / float64 ( resourceapi . CELSelectorExpressionMaxCost ), resourceapi . CELSelectorExpressionMaxCost )
257
296
}
258
297
259
298
match , details , err := result .DeviceMatches (ctx , Device {Attributes : scenario .attributes , Capacity : scenario .capacity , Driver : scenario .driver })
260
299
// details.ActualCost can be called for nil details, no need to check.
261
300
actualCost := ptr .Deref (details .ActualCost (), 0 )
262
301
if scenario .expectCost > 0 {
263
- t .Logf ("actual cost %d, %d%% of worst-case estimate" , actualCost , actualCost * 100 / scenario .expectCost )
302
+ t .Logf ("actual cost %d, %d%% of worst-case estimate %d " , actualCost , actualCost * 100 / scenario . expectCost , scenario .expectCost )
264
303
} else {
265
304
t .Logf ("actual cost %d, expected zero costs" , actualCost )
266
- if actualCost > 0 {
267
- t . Errorf ( "expected zero costs for (presumably) constant expression %q, got instead %d" , scenario . expression , actualCost )
268
- }
305
+ }
306
+ if actualCost > result . MaxCost {
307
+ t . Errorf ( "ERROR: cost estimate %d underestimated the evaluation cost of %d" , result . MaxCost , actualCost )
269
308
}
270
309
271
310
if err != nil {
272
311
if scenario .expectMatchError == "" {
273
- t .Fatalf ("unexpected evaluation error: %v" , err )
312
+ t .Fatalf ("FAILURE: unexpected evaluation error: %v" , err )
274
313
}
275
314
if ! strings .Contains (err .Error (), scenario .expectMatchError ) {
276
- t .Fatalf ("expected evaluation error to contain %q, but got instead: %v" , scenario .expectMatchError , err )
315
+ t .Fatalf ("FAILURE: expected evaluation error to contain %q, but got instead: %v" , scenario .expectMatchError , err )
277
316
}
278
317
return
279
318
}
280
319
if scenario .expectMatchError != "" {
281
- t .Fatalf ("expected match error %q, got none" , scenario .expectMatchError )
320
+ t .Fatalf ("FAILURE: expected match error %q, got none" , scenario .expectMatchError )
282
321
}
283
322
if match != scenario .expectMatch {
284
- t .Fatalf ("expected result %v, got %v" , scenario .expectMatch , match )
323
+ t .Fatalf ("FAILURE: expected result %v, got %v" , scenario .expectMatch , match )
285
324
}
286
325
})
287
326
}
0 commit comments