add support of variables for Type Checking.
This commit is contained in:
		@@ -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)
 | 
			
		||||
 
 | 
			
		||||
@@ -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)
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user