Skip to content

Commit 5e9c693

Browse files
cici37k8s-publishing-bot
authored andcommittedMay 1, 2024
Adding the feature gates to fix cost for VAP and webhook matchConditions.
Kubernetes-commit: 3d896724760a957e8059ff80e9f399248eacac66
1 parent cb47ad4 commit 5e9c693

File tree

5 files changed

+55
-10
lines changed

5 files changed

+55
-10
lines changed
 

‎pkg/apis/apiextensions/validation/validation.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,8 @@ func ValidateCustomResourceDefinition(ctx context.Context, obj *apiextensions.Cu
9292
requirePrunedDefaults: true,
9393
requireAtomicSetType: true,
9494
requireMapListKeysMapSetValidation: true,
95-
celEnvironmentSet: environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()),
95+
// strictCost is always true to enforce cost limits.
96+
celEnvironmentSet: environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true),
9697
}
9798

9899
allErrs := genericvalidation.ValidateObjectMeta(&obj.ObjectMeta, false, nameValidationFn, field.NewPath("metadata"))
@@ -231,7 +232,8 @@ func ValidateCustomResourceDefinitionUpdate(ctx context.Context, obj, oldObj *ap
231232
requireMapListKeysMapSetValidation: requireMapListKeysMapSetValidation(&oldObj.Spec),
232233
preexistingExpressions: findPreexistingExpressions(&oldObj.Spec),
233234
versionsWithUnchangedSchemas: findVersionsWithUnchangedSchemas(obj, oldObj),
234-
celEnvironmentSet: environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()),
235+
// strictCost is always true to enforce cost limits.
236+
celEnvironmentSet: environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true),
235237
}
236238
return validateCustomResourceDefinitionUpdate(ctx, obj, oldObj, opts)
237239
}

‎pkg/apis/apiextensions/validation/validation_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -7312,7 +7312,7 @@ func TestValidateCustomResourceDefinitionValidationRuleCompatibility(t *testing.
73127312
}
73137313

73147314
// Include the test library, which includes the test() function in the storage environment during test
7315-
base := environment.MustBaseEnvSet(version.MajorMinor(1, 998))
7315+
base := environment.MustBaseEnvSet(version.MajorMinor(1, 998), true)
73167316
envSet, err := base.Extend(environment.VersionedOptions{
73177317
IntroducedVersion: version.MajorMinor(1, 999),
73187318
EnvOptions: []cel.EnvOption{library.Test()},
@@ -10104,7 +10104,7 @@ func TestValidateCustomResourceDefinitionValidation(t *testing.T) {
1010410104
t.Run(tt.name, func(t *testing.T) {
1010510105
ctx := context.TODO()
1010610106
if tt.opts.celEnvironmentSet == nil {
10107-
tt.opts.celEnvironmentSet = environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion())
10107+
tt.opts.celEnvironmentSet = environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true)
1010810108
}
1010910109
got := validateCustomResourceDefinitionValidation(ctx, &tt.input, tt.statusEnabled, tt.opts, field.NewPath("spec", "validation"))
1011010110

@@ -10635,7 +10635,7 @@ func TestCelContext(t *testing.T) {
1063510635
celContext := RootCELContext(tt.schema)
1063610636
celContext.converter = converter
1063710637
opts := validationOptions{
10638-
celEnvironmentSet: environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()),
10638+
celEnvironmentSet: environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true),
1063910639
}
1064010640
openAPIV3Schema := &specStandardValidatorV3{
1064110641
allowDefaults: opts.allowDefaults,

‎pkg/apiserver/schema/cel/compilation_test.go

+3-3
Original file line numberDiff line numberDiff line change
@@ -850,7 +850,7 @@ func TestCelCompilation(t *testing.T) {
850850

851851
for _, tt := range cases {
852852
t.Run(tt.name, func(t *testing.T) {
853-
env, err := environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()).Extend(
853+
env, err := environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true).Extend(
854854
environment.VersionedOptions{
855855
IntroducedVersion: version.MajorMinor(1, 999),
856856
EnvOptions: []celgo.EnvOption{celgo.Lib(&fakeLib{})},
@@ -1327,7 +1327,7 @@ func genMapWithCustomItemRule(item *schema.Structural, rule string) func(maxProp
13271327
// if expectedCostExceedsLimit is non-zero. Typically, only expectedCost or expectedCostExceedsLimit is non-zero, not both.
13281328
func schemaChecker(schema *schema.Structural, expectedCost uint64, expectedCostExceedsLimit uint64, t *testing.T) func(t *testing.T) {
13291329
return func(t *testing.T) {
1330-
compilationResults, err := Compile(schema, model.SchemaDeclType(schema, false), celconfig.PerCallLimit, environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()), NewExpressionsEnvLoader())
1330+
compilationResults, err := Compile(schema, model.SchemaDeclType(schema, false), celconfig.PerCallLimit, environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true), NewExpressionsEnvLoader())
13311331
if err != nil {
13321332
t.Fatalf("Expected no error, got: %v", err)
13331333
}
@@ -1885,7 +1885,7 @@ func TestCostEstimation(t *testing.T) {
18851885
}
18861886

18871887
func BenchmarkCompile(b *testing.B) {
1888-
env := environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()) // prepare the environment
1888+
env := environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true) // prepare the environment
18891889
s := genArrayWithRule("number", "true")(nil)
18901890
b.ReportAllocs()
18911891
b.ResetTimer()

‎pkg/apiserver/schema/cel/validation.go

+2-1
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,8 @@ func NewValidator(s *schema.Structural, isResourceRoot bool, perCallLimit uint64
9090
// exist. declType is expected to be a CEL DeclType corresponding to the structural schema.
9191
// perCallLimit was added for testing purpose only. Callers should always use const PerCallLimit from k8s.io/apiserver/pkg/apis/cel/config.go as input.
9292
func validator(s *schema.Structural, isResourceRoot bool, declType *cel.DeclType, perCallLimit uint64) *Validator {
93-
compiledRules, err := Compile(s, declType, perCallLimit, environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()), StoredExpressionsEnvLoader())
93+
// strictCost is always true to enforce cost limits.
94+
compiledRules, err := Compile(s, declType, perCallLimit, environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true), StoredExpressionsEnvLoader())
9495
var itemsValidator, additionalPropertiesValidator *Validator
9596
var propertiesValidators map[string]Validator
9697
if s.Items != nil {

‎pkg/apiserver/schema/cel/validation_test.go

+43-1
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ func TestValidationExpressions(t *testing.T) {
6161
costBudget int64
6262
isRoot bool
6363
expectSkipped bool
64+
expectedCost int64
6465
}{
6566
// tests where val1 and val2 are equal but val3 is different
6667
// equality, comparisons and type specific functions
@@ -2006,14 +2007,48 @@ func TestValidationExpressions(t *testing.T) {
20062007
`quantity(self.val1).isInteger()`,
20072008
},
20082009
},
2010+
{name: "cost for extended lib calculated correctly: isSorted",
2011+
obj: objs("20", "200M"),
2012+
schema: schemas(stringType, stringType),
2013+
valid: []string{
2014+
"[1,2,3,4].isSorted()",
2015+
},
2016+
expectedCost: 4,
2017+
},
2018+
{name: "cost for extended lib calculated correctly: url",
2019+
obj: objs("20", "200M"),
2020+
schema: schemas(stringType, stringType),
2021+
valid: []string{
2022+
"url('https:://kubernetes.io/').getHostname() != 'test'",
2023+
},
2024+
expectedCost: 4,
2025+
},
2026+
{name: "cost for extended lib calculated correctly: split",
2027+
obj: objs("20", "200M"),
2028+
schema: schemas(stringType, stringType),
2029+
valid: []string{
2030+
"size('abc 123 def 123'.split(' ')) > 0",
2031+
},
2032+
expectedCost: 5,
2033+
},
2034+
{name: "cost for extended lib calculated correctly: join",
2035+
obj: objs("20", "200M"),
2036+
schema: schemas(stringType, stringType),
2037+
valid: []string{
2038+
"size(['aa', 'bb', 'cc', 'd', 'e', 'f', 'g', 'h', 'i', 'j'].join(' ')) > 0",
2039+
},
2040+
expectedCost: 7,
2041+
},
20092042
}
20102043

20112044
for i := range tests {
20122045
i := i
20132046
t.Run(tests[i].name, func(t *testing.T) {
20142047
t.Parallel()
20152048
tt := tests[i]
2016-
tt.costBudget = celconfig.RuntimeCELCostBudget
2049+
if tt.costBudget == 0 {
2050+
tt.costBudget = celconfig.RuntimeCELCostBudget
2051+
}
20172052
ctx := context.TODO()
20182053
for j := range tt.valid {
20192054
validRule := tt.valid[j]
@@ -2032,6 +2067,13 @@ func TestValidationExpressions(t *testing.T) {
20322067
for _, err := range errs {
20332068
t.Errorf("unexpected error: %v", err)
20342069
}
2070+
2071+
if tt.expectedCost != 0 {
2072+
if remainingBudget != tt.costBudget-tt.expectedCost {
2073+
t.Errorf("expected cost to be %d, but got %d", tt.expectedCost, tt.costBudget-remainingBudget)
2074+
}
2075+
return
2076+
}
20352077
if tt.expectSkipped {
20362078
// Skipped validations should have no cost. The only possible false positive here would be the CEL expression 'true'.
20372079
if remainingBudget != tt.costBudget {

0 commit comments

Comments
 (0)
Please sign in to comment.