Merge pull request #121085 from jiahuif-forks/fix/crd-validation-expressions/enum-cost

CRD Validation Expresions: set maxLength to longest enum.
This commit is contained in:
Kubernetes Prow Robot 2023-10-17 23:28:39 +02:00 committed by GitHub
commit 0304c567b6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 56 additions and 3 deletions

View File

@ -989,6 +989,35 @@ func genStringWithRule(rule string) func(maxLength *int64) *schema.Structural {
}
}
// 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,
},
},
},
}
}
}
func genBytesWithRule(rule string) func(maxLength *int64) *schema.Structural {
return func(maxLength *int64) *schema.Structural {
return &schema.Structural{
@ -1744,6 +1773,13 @@ func TestCostEstimation(t *testing.T) {
setMaxElements: 42,
expectedSetCost: 8,
},
{
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,
},
}
for _, testCase := range cases {
t.Run(testCase.name, func(t *testing.T) {

View File

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

View File

@ -165,7 +165,11 @@ func SchemaDeclType(s Schema, isResourceRoot bool) *apiservercel.DeclType {
// unicode code point can be up to 4 bytes long)
strWithMaxLength.MaxElements = zeroIfNegative(*s.MaxLength()) * 4
} else {
strWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s)
if len(s.Enum()) > 0 {
strWithMaxLength.MaxElements = estimateMaxStringEnumLength(s)
} else {
strWithMaxLength.MaxElements = estimateMaxStringLengthPerRequest(s)
}
}
return strWithMaxLength
case "boolean":
@ -239,6 +243,19 @@ func estimateMaxStringLengthPerRequest(s Schema) int64 {
}
}
// 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
}
// 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 {