Move CEL env initialization out of package init()

This ensures compatibility version and feature gates can be initialized
before cached CEL environments are created.
This commit is contained in:
Jordan Liggitt 2024-06-29 21:45:55 -04:00
parent 1d2ad282cf
commit 03d48b7683
No known key found for this signature in database
7 changed files with 75 additions and 32 deletions

View File

@ -21,6 +21,7 @@ import (
"reflect"
"regexp"
"strings"
"sync"
genericvalidation "k8s.io/apimachinery/pkg/api/validation"
"k8s.io/apimachinery/pkg/api/validation/path"
@ -1066,9 +1067,9 @@ func validateMatchConditionsExpression(expression string, opts validationOptions
}
var compiler plugincel.Compiler
if opts.strictCostEnforcement {
compiler = strictStatelessCELCompiler
compiler = getStrictStatelessCELCompiler()
} else {
compiler = nonStrictStatelessCELCompiler
compiler = getNonStrictStatelessCELCompiler()
}
return validateCELCondition(compiler, &matchconditions.MatchCondition{
Expression: expression,
@ -1270,15 +1271,34 @@ func validateFieldRef(fieldRef string, fldPath *field.Path) field.ErrorList {
// variable composition is not allowed, for example, when validating MatchConditions.
// strictStatelessCELCompiler is a cel Compiler that enforces strict cost enforcement.
// nonStrictStatelessCELCompiler is a cel Compiler that does not enforce strict cost enforcement.
var strictStatelessCELCompiler = plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
var nonStrictStatelessCELCompiler = plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), false))
var (
lazyStrictStatelessCELCompilerInit sync.Once
lazyStrictStatelessCELCompiler plugincel.Compiler
lazyNonStrictStatelessCELCompilerInit sync.Once
lazyNonStrictStatelessCELCompiler plugincel.Compiler
)
func getStrictStatelessCELCompiler() plugincel.Compiler {
lazyStrictStatelessCELCompilerInit.Do(func() {
lazyStrictStatelessCELCompiler = plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
})
return lazyStrictStatelessCELCompiler
}
func getNonStrictStatelessCELCompiler() plugincel.Compiler {
lazyNonStrictStatelessCELCompilerInit.Do(func() {
lazyNonStrictStatelessCELCompiler = plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), false))
})
return lazyNonStrictStatelessCELCompiler
}
func createCompiler(allowComposition, strictCost bool) plugincel.Compiler {
if !allowComposition {
if strictCost {
return strictStatelessCELCompiler
return getStrictStatelessCELCompiler()
} else {
return nonStrictStatelessCELCompiler
return getNonStrictStatelessCELCompiler()
}
}
compiler, err := plugincel.NewCompositedCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), strictCost))

View File

@ -3425,14 +3425,16 @@ func TestValidateValidatingAdmissionPolicyUpdate(t *testing.T) {
t.Fatal(err)
}
if strictCost {
strictStatelessCELCompiler = plugincel.NewCompiler(extended)
originalCompiler := getStrictStatelessCELCompiler()
lazyStrictStatelessCELCompiler = plugincel.NewCompiler(extended)
defer func() {
strictStatelessCELCompiler = plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), strictCost))
lazyStrictStatelessCELCompiler = originalCompiler
}()
} else {
nonStrictStatelessCELCompiler = plugincel.NewCompiler(extended)
originalCompiler := getNonStrictStatelessCELCompiler()
lazyNonStrictStatelessCELCompiler = plugincel.NewCompiler(extended)
defer func() {
nonStrictStatelessCELCompiler = plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), strictCost))
lazyNonStrictStatelessCELCompiler = originalCompiler
}()
}

View File

@ -153,7 +153,7 @@ func validateSelector(opts Options, selector string, fldPath *field.Path) field.
if opts.StoredExpressions {
envType = environment.StoredExpressions
}
result := namedresourcescel.Compiler.CompileCELExpression(selector, envType)
result := namedresourcescel.GetCompiler().CompileCELExpression(selector, envType)
if result.Error != nil {
allErrs = append(allErrs, convertCELErrorToValidationError(fldPath, selector, result.Error))
}

View File

@ -68,7 +68,7 @@ func AddAllocation(m *Model, result *resourceapi.NamedResourcesAllocationResult)
func NewClaimController(filter *resourceapi.NamedResourcesFilter, requests []*resourceapi.NamedResourcesRequest) (*Controller, error) {
c := &Controller{}
if filter != nil {
compilation := cel.Compiler.CompileCELExpression(filter.Selector, environment.StoredExpressions)
compilation := cel.GetCompiler().CompileCELExpression(filter.Selector, environment.StoredExpressions)
if compilation.Error != nil {
// Shouldn't happen because of validation.
return nil, fmt.Errorf("compile class filter CEL expression: %w", compilation.Error)
@ -76,7 +76,7 @@ func NewClaimController(filter *resourceapi.NamedResourcesFilter, requests []*re
c.filter = &compilation
}
for _, request := range requests {
compilation := cel.Compiler.CompileCELExpression(request.Selector, environment.StoredExpressions)
compilation := cel.GetCompiler().CompileCELExpression(request.Selector, environment.StoredExpressions)
if compilation.Error != nil {
// Shouldn't happen because of validation.
return nil, fmt.Errorf("compile request CEL expression: %w", compilation.Error)

View File

@ -19,6 +19,7 @@ package validating
import (
"context"
"io"
"sync"
v1 "k8s.io/api/admissionregistration/v1"
"k8s.io/apimachinery/pkg/api/meta"
@ -44,24 +45,35 @@ const (
)
var (
compositionEnvTemplateWithStrictCost *cel.CompositionEnv = func() *cel.CompositionEnv {
compositionEnvTemplateWithStrictCost, err := cel.NewCompositionEnv(cel.VariablesTypeName, environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
if err != nil {
panic(err)
}
lazyCompositionEnvTemplateWithStrictCostInit sync.Once
lazyCompositionEnvTemplateWithStrictCost *cel.CompositionEnv
return compositionEnvTemplateWithStrictCost
}()
compositionEnvTemplateWithoutStrictCost *cel.CompositionEnv = func() *cel.CompositionEnv {
compositionEnvTemplateWithoutStrictCost, err := cel.NewCompositionEnv(cel.VariablesTypeName, environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), false))
if err != nil {
panic(err)
}
return compositionEnvTemplateWithoutStrictCost
}()
lazyCompositionEnvTemplateWithoutStrictCostInit sync.Once
lazyCompositionEnvTemplateWithoutStrictCost *cel.CompositionEnv
)
func getCompositionEnvTemplateWithStrictCost() *cel.CompositionEnv {
lazyCompositionEnvTemplateWithStrictCostInit.Do(func() {
env, err := cel.NewCompositionEnv(cel.VariablesTypeName, environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
if err != nil {
panic(err)
}
lazyCompositionEnvTemplateWithStrictCost = env
})
return lazyCompositionEnvTemplateWithStrictCost
}
func getCompositionEnvTemplateWithoutStrictCost() *cel.CompositionEnv {
lazyCompositionEnvTemplateWithoutStrictCostInit.Do(func() {
env, err := cel.NewCompositionEnv(cel.VariablesTypeName, environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), false))
if err != nil {
panic(err)
}
lazyCompositionEnvTemplateWithoutStrictCost = env
})
return lazyCompositionEnvTemplateWithoutStrictCost
}
// Register registers a plugin
func Register(plugins *admission.Plugins) {
plugins.Register(PluginName, func(configFile io.Reader) (admission.Interface, error) {
@ -131,9 +143,9 @@ func compilePolicy(policy *Policy) Validator {
matchConditions := policy.Spec.MatchConditions
var compositionEnvTemplate *cel.CompositionEnv
if strictCost {
compositionEnvTemplate = compositionEnvTemplateWithStrictCost
compositionEnvTemplate = getCompositionEnvTemplateWithStrictCost()
} else {
compositionEnvTemplate = compositionEnvTemplateWithoutStrictCost
compositionEnvTemplate = getCompositionEnvTemplateWithoutStrictCost()
}
filterCompiler := cel.NewCompositedCompilerFromTemplate(compositionEnvTemplate)
filterCompiler.CompileAndStoreVariables(convertv1beta1Variables(policy.Spec.Variables), optionalVars, environment.StoredExpressions)

View File

@ -20,6 +20,7 @@ import (
"context"
"fmt"
"reflect"
"sync"
"github.com/blang/semver/v4"
"github.com/google/cel-go/cel"
@ -38,9 +39,17 @@ const (
)
var (
Compiler = newCompiler()
lazyCompilerInit sync.Once
lazyCompiler *compiler
)
func GetCompiler() *compiler {
lazyCompilerInit.Do(func() {
lazyCompiler = newCompiler()
})
return lazyCompiler
}
// CompilationResult represents a compiled expression.
type CompilationResult struct {
Program cel.Program

View File

@ -124,7 +124,7 @@ attributes.stringslice["stringslice"].isSorted()`,
} {
t.Run(name, func(t *testing.T) {
_, ctx := ktesting.NewTestContext(t)
result := Compiler.CompileCELExpression(scenario.expression, environment.StoredExpressions)
result := GetCompiler().CompileCELExpression(scenario.expression, environment.StoredExpressions)
if scenario.expectCompileError != "" && result.Error == nil {
t.Fatalf("expected compile error %q, got none", scenario.expectCompileError)
}