add support of variables for Type Checking.

This commit is contained in:
Jiahui Feng 2024-02-01 15:28:21 -08:00
parent 21ba0d59d3
commit dc832c6e59
2 changed files with 98 additions and 10 deletions

View File

@ -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)

View File

@ -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)