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:
		@@ -21,6 +21,7 @@ import (
 | 
				
			|||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	genericvalidation "k8s.io/apimachinery/pkg/api/validation"
 | 
						genericvalidation "k8s.io/apimachinery/pkg/api/validation"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/api/validation/path"
 | 
						"k8s.io/apimachinery/pkg/api/validation/path"
 | 
				
			||||||
@@ -1066,9 +1067,9 @@ func validateMatchConditionsExpression(expression string, opts validationOptions
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	var compiler plugincel.Compiler
 | 
						var compiler plugincel.Compiler
 | 
				
			||||||
	if opts.strictCostEnforcement {
 | 
						if opts.strictCostEnforcement {
 | 
				
			||||||
		compiler = strictStatelessCELCompiler
 | 
							compiler = getStrictStatelessCELCompiler()
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		compiler = nonStrictStatelessCELCompiler
 | 
							compiler = getNonStrictStatelessCELCompiler()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return validateCELCondition(compiler, &matchconditions.MatchCondition{
 | 
						return validateCELCondition(compiler, &matchconditions.MatchCondition{
 | 
				
			||||||
		Expression: expression,
 | 
							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.
 | 
					// variable composition is not allowed, for example, when validating MatchConditions.
 | 
				
			||||||
// strictStatelessCELCompiler is a cel Compiler that enforces strict cost enforcement.
 | 
					// strictStatelessCELCompiler is a cel Compiler that enforces strict cost enforcement.
 | 
				
			||||||
// nonStrictStatelessCELCompiler is a cel Compiler that does not enforce 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 (
 | 
				
			||||||
var nonStrictStatelessCELCompiler = plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), false))
 | 
						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 {
 | 
					func createCompiler(allowComposition, strictCost bool) plugincel.Compiler {
 | 
				
			||||||
	if !allowComposition {
 | 
						if !allowComposition {
 | 
				
			||||||
		if strictCost {
 | 
							if strictCost {
 | 
				
			||||||
			return strictStatelessCELCompiler
 | 
								return getStrictStatelessCELCompiler()
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			return nonStrictStatelessCELCompiler
 | 
								return getNonStrictStatelessCELCompiler()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	compiler, err := plugincel.NewCompositedCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), strictCost))
 | 
						compiler, err := plugincel.NewCompositedCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), strictCost))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3425,14 +3425,16 @@ func TestValidateValidatingAdmissionPolicyUpdate(t *testing.T) {
 | 
				
			|||||||
		t.Fatal(err)
 | 
							t.Fatal(err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if strictCost {
 | 
						if strictCost {
 | 
				
			||||||
		strictStatelessCELCompiler = plugincel.NewCompiler(extended)
 | 
							originalCompiler := getStrictStatelessCELCompiler()
 | 
				
			||||||
 | 
							lazyStrictStatelessCELCompiler = plugincel.NewCompiler(extended)
 | 
				
			||||||
		defer func() {
 | 
							defer func() {
 | 
				
			||||||
			strictStatelessCELCompiler = plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), strictCost))
 | 
								lazyStrictStatelessCELCompiler = originalCompiler
 | 
				
			||||||
		}()
 | 
							}()
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		nonStrictStatelessCELCompiler = plugincel.NewCompiler(extended)
 | 
							originalCompiler := getNonStrictStatelessCELCompiler()
 | 
				
			||||||
 | 
							lazyNonStrictStatelessCELCompiler = plugincel.NewCompiler(extended)
 | 
				
			||||||
		defer func() {
 | 
							defer func() {
 | 
				
			||||||
			nonStrictStatelessCELCompiler = plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), strictCost))
 | 
								lazyNonStrictStatelessCELCompiler = originalCompiler
 | 
				
			||||||
		}()
 | 
							}()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -153,7 +153,7 @@ func validateSelector(opts Options, selector string, fldPath *field.Path) field.
 | 
				
			|||||||
		if opts.StoredExpressions {
 | 
							if opts.StoredExpressions {
 | 
				
			||||||
			envType = environment.StoredExpressions
 | 
								envType = environment.StoredExpressions
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		result := namedresourcescel.Compiler.CompileCELExpression(selector, envType)
 | 
							result := namedresourcescel.GetCompiler().CompileCELExpression(selector, envType)
 | 
				
			||||||
		if result.Error != nil {
 | 
							if result.Error != nil {
 | 
				
			||||||
			allErrs = append(allErrs, convertCELErrorToValidationError(fldPath, selector, result.Error))
 | 
								allErrs = append(allErrs, convertCELErrorToValidationError(fldPath, selector, result.Error))
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -68,7 +68,7 @@ func AddAllocation(m *Model, result *resourceapi.NamedResourcesAllocationResult)
 | 
				
			|||||||
func NewClaimController(filter *resourceapi.NamedResourcesFilter, requests []*resourceapi.NamedResourcesRequest) (*Controller, error) {
 | 
					func NewClaimController(filter *resourceapi.NamedResourcesFilter, requests []*resourceapi.NamedResourcesRequest) (*Controller, error) {
 | 
				
			||||||
	c := &Controller{}
 | 
						c := &Controller{}
 | 
				
			||||||
	if filter != nil {
 | 
						if filter != nil {
 | 
				
			||||||
		compilation := cel.Compiler.CompileCELExpression(filter.Selector, environment.StoredExpressions)
 | 
							compilation := cel.GetCompiler().CompileCELExpression(filter.Selector, environment.StoredExpressions)
 | 
				
			||||||
		if compilation.Error != nil {
 | 
							if compilation.Error != nil {
 | 
				
			||||||
			// Shouldn't happen because of validation.
 | 
								// Shouldn't happen because of validation.
 | 
				
			||||||
			return nil, fmt.Errorf("compile class filter CEL expression: %w", compilation.Error)
 | 
								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
 | 
							c.filter = &compilation
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for _, request := range requests {
 | 
						for _, request := range requests {
 | 
				
			||||||
		compilation := cel.Compiler.CompileCELExpression(request.Selector, environment.StoredExpressions)
 | 
							compilation := cel.GetCompiler().CompileCELExpression(request.Selector, environment.StoredExpressions)
 | 
				
			||||||
		if compilation.Error != nil {
 | 
							if compilation.Error != nil {
 | 
				
			||||||
			// Shouldn't happen because of validation.
 | 
								// Shouldn't happen because of validation.
 | 
				
			||||||
			return nil, fmt.Errorf("compile request CEL expression: %w", compilation.Error)
 | 
								return nil, fmt.Errorf("compile request CEL expression: %w", compilation.Error)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,6 +19,7 @@ package validating
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	v1 "k8s.io/api/admissionregistration/v1"
 | 
						v1 "k8s.io/api/admissionregistration/v1"
 | 
				
			||||||
	"k8s.io/apimachinery/pkg/api/meta"
 | 
						"k8s.io/apimachinery/pkg/api/meta"
 | 
				
			||||||
@@ -44,24 +45,35 @@ const (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	compositionEnvTemplateWithStrictCost *cel.CompositionEnv = func() *cel.CompositionEnv {
 | 
						lazyCompositionEnvTemplateWithStrictCostInit sync.Once
 | 
				
			||||||
		compositionEnvTemplateWithStrictCost, err := cel.NewCompositionEnv(cel.VariablesTypeName, environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), true))
 | 
						lazyCompositionEnvTemplateWithStrictCost     *cel.CompositionEnv
 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			panic(err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		return compositionEnvTemplateWithStrictCost
 | 
						lazyCompositionEnvTemplateWithoutStrictCostInit sync.Once
 | 
				
			||||||
	}()
 | 
						lazyCompositionEnvTemplateWithoutStrictCost     *cel.CompositionEnv
 | 
				
			||||||
	compositionEnvTemplateWithoutStrictCost *cel.CompositionEnv = func() *cel.CompositionEnv {
 | 
					 | 
				
			||||||
		compositionEnvTemplateWithoutStrictCost, err := cel.NewCompositionEnv(cel.VariablesTypeName, environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion(), false))
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			panic(err)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
		return compositionEnvTemplateWithoutStrictCost
 | 
					 | 
				
			||||||
	}()
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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
 | 
					// Register registers a plugin
 | 
				
			||||||
func Register(plugins *admission.Plugins) {
 | 
					func Register(plugins *admission.Plugins) {
 | 
				
			||||||
	plugins.Register(PluginName, func(configFile io.Reader) (admission.Interface, error) {
 | 
						plugins.Register(PluginName, func(configFile io.Reader) (admission.Interface, error) {
 | 
				
			||||||
@@ -131,9 +143,9 @@ func compilePolicy(policy *Policy) Validator {
 | 
				
			|||||||
	matchConditions := policy.Spec.MatchConditions
 | 
						matchConditions := policy.Spec.MatchConditions
 | 
				
			||||||
	var compositionEnvTemplate *cel.CompositionEnv
 | 
						var compositionEnvTemplate *cel.CompositionEnv
 | 
				
			||||||
	if strictCost {
 | 
						if strictCost {
 | 
				
			||||||
		compositionEnvTemplate = compositionEnvTemplateWithStrictCost
 | 
							compositionEnvTemplate = getCompositionEnvTemplateWithStrictCost()
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		compositionEnvTemplate = compositionEnvTemplateWithoutStrictCost
 | 
							compositionEnvTemplate = getCompositionEnvTemplateWithoutStrictCost()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	filterCompiler := cel.NewCompositedCompilerFromTemplate(compositionEnvTemplate)
 | 
						filterCompiler := cel.NewCompositedCompilerFromTemplate(compositionEnvTemplate)
 | 
				
			||||||
	filterCompiler.CompileAndStoreVariables(convertv1beta1Variables(policy.Spec.Variables), optionalVars, environment.StoredExpressions)
 | 
						filterCompiler.CompileAndStoreVariables(convertv1beta1Variables(policy.Spec.Variables), optionalVars, environment.StoredExpressions)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,7 @@ import (
 | 
				
			|||||||
	"context"
 | 
						"context"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/blang/semver/v4"
 | 
						"github.com/blang/semver/v4"
 | 
				
			||||||
	"github.com/google/cel-go/cel"
 | 
						"github.com/google/cel-go/cel"
 | 
				
			||||||
@@ -38,9 +39,17 @@ const (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	Compiler = newCompiler()
 | 
						lazyCompilerInit sync.Once
 | 
				
			||||||
 | 
						lazyCompiler     *compiler
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func GetCompiler() *compiler {
 | 
				
			||||||
 | 
						lazyCompilerInit.Do(func() {
 | 
				
			||||||
 | 
							lazyCompiler = newCompiler()
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
						return lazyCompiler
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CompilationResult represents a compiled expression.
 | 
					// CompilationResult represents a compiled expression.
 | 
				
			||||||
type CompilationResult struct {
 | 
					type CompilationResult struct {
 | 
				
			||||||
	Program     cel.Program
 | 
						Program     cel.Program
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -124,7 +124,7 @@ attributes.stringslice["stringslice"].isSorted()`,
 | 
				
			|||||||
	} {
 | 
						} {
 | 
				
			||||||
		t.Run(name, func(t *testing.T) {
 | 
							t.Run(name, func(t *testing.T) {
 | 
				
			||||||
			_, ctx := ktesting.NewTestContext(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 {
 | 
								if scenario.expectCompileError != "" && result.Error == nil {
 | 
				
			||||||
				t.Fatalf("expected compile error %q, got none", scenario.expectCompileError)
 | 
									t.Fatalf("expected compile error %q, got none", scenario.expectCompileError)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user