Change:
pointed out to me that a CEL x-kubernetes-validations rule in a CRD was hitting the estimated cost limit when performing a simple "string has prefix" check on an en enum field. Putting a limit in the OpenAPI of the enum string field fixed the problem, but having to add a limit like this is counter-intuitive and unnecessary, since the allowed enum values already make the max allowed length obvious. The apiserver automatically computes the max length of an enum as the length of the longest allowed enum value. I believe we already refuse evaluate CEL rules on values that fail OpenAPI value validation, since we rely in the value validations to establish all length limits. I haven't tested it locally, but something like this should work (given enough nested arrays): As a workaround, one can manually set on the string enum, e.g.: 25+ All No response No response No response No response
/sig api-machinery /triage accepted /assign
/assign
I wasn't sure if we special-cased the maxLength / maxItems / maxProperties failures, or if all openapi validation had to pass before we started messing with CEL evaluation
it looks like we currently special-case specific openapi errors that short-circuit CEL validation, I'm not sure an enum validation failure would currently prevent evaluation:
The enum err is converted to () error which is not short-circuit here. But for defaulting case, all error would be short-circuit. I would suggest to add error into the blockingErr list to short-circuit CEL validation. Would this make sense?
yeah, that sounds good if we're going to rely on it to catch things that are longer than the maximum length enum value

Discussion:
} } <ins> // genEnumWithRuleAndValues creates a function that accepts an optional maxLength // with given validation rule and a set of enum values, following the convention of existing tests. // The test has two checks, first with maxLength unset to check if maxLength can be concluded from enums, // second with maxLength set to ensure it takes precedence. func genEnumWithRuleAndValues(rule string, values ...string) func(maxLength *int64) *schema.Structural { enums := make([]schema.JSON, 0, len(values)) for _, v := range values { enums = append(enums, schema.JSON{Object: v}) } return func(maxLength *int64) *schema.Structural { return &schema.Structural{ Generic: schema.Generic{ Type: "string", }, ValueValidation: &schema.ValueValidation{ MaxLength: maxLength, Enum:      enums, }, Extensions: schema.Extensions{ XValidations: apiextensions.ValidationRules{ { Rule: rule, }, }, }, } } } </ins> func genBytesWithRule(rule string) func(maxLength *int64) *schema.Structural { return func(maxLength *int64) *schema.Structural { return &schema.Structural{

setMaxElements:   42, expectedSetCost:  8, }, <ins> { name:             "enums with maxLength equals to the longest possible value", schemaGenerator:  genEnumWithRuleAndValues("self.contains('A')", "A", "B", "C", "LongValue"), expectedCalcCost: 2, setMaxElements:   1000, expectedSetCost:  401, }, </ins> } for _, testCase := range cases { t.Run(testCase.name, func(t *testing.T) {

} } <del> // OpenAPIv3 type/maxLength/maxItems/MaxProperties/required/wrong type field validation failures are viewed as blocking err for CEL validation </del> <ins> // OpenAPIv3 type/maxLength/maxItems/MaxProperties/required/enum violation/wrong type field validation failures are viewed as blocking err for CEL validation </ins> func hasBlockingErr(errs field.ErrorList) (bool, *field.Error) { for _, err := range errs { <del> if err.Type == field.ErrorTypeRequired || err.Type == field.ErrorTypeTooLong || err.Type == field.ErrorTypeTooMany || err.Type == field.ErrorTypeTypeInvalid { </del> <ins> if err.Type == field.ErrorTypeNotSupported || err.Type == field.ErrorTypeRequired || err.Type == field.ErrorTypeTooLong || err.Type == field.ErrorTypeTooMany || err.Type == field.ErrorTypeTypeInvalid { </ins> return true, field.Invalid(nil, nil, "some validation rules were not checked because the object was invalid; correct the existing errors to complete validation") } }

// unicode code point can be up to 4 bytes long) strWithMaxLength.MaxElements = zeroIfNegative(*s.MaxLength()) * 4 } else { <del> strWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s) </del> <ins> if len(s.Enum()) > 0 { strWithMaxLength.MaxElements = estimateMaxStringEnumLength(s) } else { strWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s) } </ins> } return strWithMaxLength case "boolean":

} } <ins> // estimateMaxStringLengthPerRequest estimates the maximum string length (in characters) // that has a set of enum values. // The result of the estimation is the length of the longest possible value. func estimateMaxStringEnumLength(s Schema) int64 { var maxLength int64 for _, v := range s.Enum() { if s, ok := v.(string); ok && int64(len(s)) > maxLength { maxLength = int64(len(s)) } } return maxLength } </ins> // estimateMaxArrayItemsPerRequest estimates the maximum number of array items with // the provided minimum serialized size that can fit into a single request. func estimateMaxArrayItemsFromMinSize(minSize int64) int64 {