diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/typechecking.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/typechecking.go index cd7327bf4c9..d4a1bf82556 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/typechecking.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/typechecking.go @@ -188,7 +188,7 @@ func (c *TypeChecker) compiler(ctx *TypeCheckingContext, typeOverwrite typeOverw return nil, err } compiler := &plugincel.CompositedCompiler{ - Compiler: &typeCheckingCompiler{typeOverwrite: typeOverwrite}, + Compiler: &typeCheckingCompiler{typeOverwrite: typeOverwrite, compositionEnv: env}, CompositionEnv: env, } return compiler, nil @@ -449,7 +449,8 @@ func createVariableOpts(declType *apiservercel.DeclType, variables ...string) [] } type typeCheckingCompiler struct { - typeOverwrite typeOverwrite + compositionEnv *plugincel.CompositionEnv + typeOverwrite typeOverwrite } // CompileCELExpression compiles the given expression. @@ -468,20 +469,18 @@ func (c *typeCheckingCompiler) CompileCELExpression(expressionAccessor plugincel ExpressionAccessor: expressionAccessor, } } - envSet, err := buildEnvSet(options.HasParams, options.HasAuthorizer, c.typeOverwrite) - if err != nil { - return resultError(fmt.Sprintf("fail to build env set: %v", err), apiservercel.ErrorTypeInternal) - } - env, err := envSet.Env(mode) + env, err := c.compositionEnv.Env(mode) if err != nil { return resultError(fmt.Sprintf("fail to build env: %v", err), apiservercel.ErrorTypeInternal) } - _, issues := env.Compile(expressionAccessor.GetExpression()) + ast, issues := env.Compile(expressionAccessor.GetExpression()) if issues != nil { return resultError(issues.String(), apiservercel.ErrorTypeInvalid) } - // type checker does not require the program, an empty result indicates successful compilation. - return plugincel.CompilationResult{} + // type checker does not require the program, however the type must still be set. + return plugincel.CompilationResult{ + OutputType: ast.OutputType(), + } } var _ plugincel.Compiler = (*typeCheckingCompiler)(nil) diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/typechecking_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/typechecking_test.go index e4b9a5d153d..c5e63b27116 100644 --- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/typechecking_test.go +++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/typechecking_test.go @@ -413,6 +413,95 @@ func TestTypeCheck(t *testing.T) { toContain("found no matching overload for 'allowed' applied to 'kubernetes.authorization.Authorizer"), }, }, + { + name: "variables valid", + policy: &v1beta1.ValidatingAdmissionPolicy{Spec: v1beta1.ValidatingAdmissionPolicySpec{ + Variables: []v1beta1.Variable{ + { + Name: "works", + Expression: "true", + }, + }, + Validations: []v1beta1.Validation{ + { + Expression: "variables.works", + }, + }, + MatchConstraints: deploymentPolicy.Spec.MatchConstraints, + }, + }, + schemaToReturn: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "foo": *spec.Int64Property(), + }, + }, + }, + assertions: []assertionFunc{toHaveLengthOf(0)}, + }, + { + name: "variables missing field", + policy: &v1beta1.ValidatingAdmissionPolicy{Spec: v1beta1.ValidatingAdmissionPolicySpec{ + Variables: []v1beta1.Variable{ + { + Name: "works", + Expression: "true", + }, + }, + Validations: []v1beta1.Validation{ + { + Expression: "variables.nonExisting", + }, + }, + MatchConstraints: deploymentPolicy.Spec.MatchConstraints, + }, + }, + schemaToReturn: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "foo": *spec.Int64Property(), + }, + }, + }, + assertions: []assertionFunc{ + toHaveLengthOf(1), + toHaveFieldRef("spec.validations[0].expression"), + toContain("undefined field 'nonExisting'"), + }, + }, + { + name: "variables field wrong type", + policy: &v1beta1.ValidatingAdmissionPolicy{Spec: v1beta1.ValidatingAdmissionPolicySpec{ + Variables: []v1beta1.Variable{ + { + Name: "name", + Expression: "'something'", + }, + }, + Validations: []v1beta1.Validation{ + { + Expression: "variables.name == object.foo", // foo is int64 + }, + }, + MatchConstraints: deploymentPolicy.Spec.MatchConstraints, + }, + }, + schemaToReturn: &spec.Schema{ + SchemaProps: spec.SchemaProps{ + Type: []string{"object"}, + Properties: map[string]spec.Schema{ + "foo": *spec.Int64Property(), + }, + }, + }, + assertions: []assertionFunc{ + toHaveLengthOf(1), + toHaveFieldRef("spec.validations[0].expression"), + toContain("found no matching overload for '_==_' applied to '(string, int)"), + }, + }, } { t.Run(tc.name, func(t *testing.T) { typeChecker := buildTypeChecker(tc.schemaToReturn)