Change:
Variables field is to ValidatingAdmissionPolicy in 1.28. Currently the usage of variables would expect a operator associated. e.g. (thanks to Mariam Fahmy for raising the issue) Above will fail with error: Meanwhile the will pass. The reason is when variables be evaluated to non dyn type(like bool or string), it will require special handling(like casting) since we use dyn type everywhere. It might confuse user.
This issue is currently awaiting triage. If a SIG or subproject determines this is a relevant issue, they will accept it by applying the label and provide further guidance. The label can be by org members by writing in a comment. <detailsInstructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/triage accepted /sig api-machinery /cc
Tests I'd expect to see verify this behavior: PASS: A variable is of type bool, an expression returns the variable PASS: A variable is of type string, a messageExpression returns the variable PASS: A variable is of type dyn, at runtime the dyn is a bool, an expression returns the variable PASS: A variable is of typedyn, at runtime the dyn is a string, an messageExpression returns the variable PASS: A expression is of type dyn, at runtime the dyn is of type bool PASS: A messageExpression is of type dyn, at runtime the dyn is of type string COMPILE ERROR: A variable is of type bool, an expression tries to perform a non-bool (e.g. string replace) operation on the variable, this should resultsin a compile error COMPILE ERROR: A variable is of type string, an expression tries to perform a non-string (e.g. less than) operation on the variable, this should result in a compile error RUNTIME ERROR: A variable is of type dyn, at runtime the dyn is a bool, an expression tries to perform a non-bool (e.g. string replace) operation on the variable, this should result in a runtime error I'd also expect to see some cases covering variables of other types (maps, lists, duration, quantity, ...) to make sure we're over-fitting for bool and string.
/retitle ValidatingAdmissionPolicy: variable expression type not recognized as return type in expression that evaluates the variable
/assign

Discussion:
Program            cel.Program Error              *apiservercel.Error ExpressionAccessor ExpressionAccessor <ins> OutputType         *cel.Type </ins> } // Compiler provides a CEL expression compiler configured with the desired admission related CEL variables and

return CompilationResult{ Program:            prog, ExpressionAccessor: expressionAccessor, <ins> OutputType:         ast.OutputType(), </ins> } }

"github.com/google/cel-go/cel" "github.com/google/cel-go/common/types" "github.com/google/cel-go/common/types/ref" <ins> "github.com/google/cel-go/common/types/traits" </ins> v1 "k8s.io/api/admission/v1" corev1 "k8s.io/api/core/v1"

} func (c *CompositedCompiler) CompileAndStoreVariable(variable NamedExpressionAccessor, options OptionalVariableDeclarations, mode environment.Type) CompilationResult { <del> c.CompositionEnv.AddField(variable.GetName()) </del> result := c.Compiler.CompileCELExpression(variable, options, mode) <ins> c.CompositionEnv.AddField(variable.GetName(), result.OutputType) </ins> c.CompositionEnv.CompiledVariables[variable.GetName()] = result return result }

CompiledVariables map[string]CompilationResult } <del> func (c *CompositionEnv) AddField(name string) { c.MapType.Fields[name] = apiservercel.NewDeclField(name, apiservercel.DynType, true, nil, nil) </del> <ins> func (c *CompositionEnv) AddField(name string, celType *cel.Type) { c.MapType.Fields[name] = apiservercel.NewDeclField(name, convertCelTypeToDeclType(celType), true, nil, nil) </ins> } func NewCompositionEnv(typeName string, baseEnvSet *environment.EnvSet) (*CompositionEnv, error) {

} return v } <ins> // convertCelTypeToDeclType converts a cel.Type to DeclType, for the use of // the TypeProvider and the cost estimator. // List and map types are created on-demand with their parameters converted recursively. func convertCelTypeToDeclType(celType *cel.Type) *apiservercel.DeclType { if celType == nil { return apiservercel.DynType } switch celType { case cel.AnyType: return apiservercel.AnyType case cel.BoolType: return apiservercel.BoolType case cel.BytesType: return apiservercel.BytesType case cel.DoubleType: return apiservercel.DoubleType case cel.DurationType: return apiservercel.DurationType case cel.IntType: return apiservercel.IntType case cel.NullType: return apiservercel.NullType case cel.StringType: return apiservercel.StringType case cel.TimestampType: return apiservercel.TimestampType case cel.UintType: return apiservercel.UintType default: if celType.HasTrait(traits.ContainerType) && celType.HasTrait(traits.IndexerType) { parameters := celType.Parameters() switch len(parameters) { case 1: elemType := convertCelTypeToDeclType(parameters[0]) return apiservercel.NewListType(elemType, -1) case 2: keyType := convertCelTypeToDeclType(parameters[0]) valueType := convertCelTypeToDeclType(parameters[1]) return apiservercel.NewMapType(keyType, valueType, -1) } } return apiservercel.DynType } } </ins>

expectedResult: true, }, { <del> name: "delayed compile error", </del> <ins> name: "early compile error", </ins> variables: []NamedExpressionAccessor{ &testVariable{ name:       "name",

attributes:           endpointCreateAttributes(), expression:           "variables.name == 'endpoints1'", expectErr:            true, <del> expectedErrorMessage: `composited variable "name" fails to compile:`, </del> <ins> expectedErrorMessage: `found no matching overload for '_==_' applied to '(int, string)'`, </ins> }, { name: "delayed eval error", variables: []NamedExpressionAccessor{ &testVariable{ <del> name:       "name", expression: "object.spec.subsets[114514].addresses.size()", // array index out of bound </del> <ins> name:       "count", expression: "object.subsets[114514].addresses.size()", // array index out of bound </ins> }, }, attributes:           endpointCreateAttributes(), <del> expression:           "variables.name == 'endpoints1'", </del> <ins> expression:           "variables.count == 810", </ins> expectErr:            true, <del> expectedErrorMessage: `composited variable "name" fails to evaluate:`, </del> <ins> expectedErrorMessage: `composited variable "count" fails to evaluate: index out of bounds: 114514`, </ins> }, { name: "out of budget during lazy evaluation",

expectedResult:    true, runtimeCostBudget: 10, // enough for one lazy evaluation but not two, should pass }, <ins> { name: "single boolean variable in expression", variables: []NamedExpressionAccessor{ &testVariable{ name:       "fortuneTelling", expression: "true", }, }, attributes:     endpointCreateAttributes(), expression:     "variables.fortuneTelling", expectedResult: true, }, { name: "variable of a list", variables: []NamedExpressionAccessor{ &testVariable{ name:       "list", expression: "[1, 2, 3, 4]", }, }, attributes:     endpointCreateAttributes(), expression:     "variables.list.sum() == 10", expectedResult: true, }, { name: "variable of a map", variables: []NamedExpressionAccessor{ &testVariable{ name:       "dict", expression: `{"foo": "bar"}`, }, }, attributes:     endpointCreateAttributes(), expression:     "variables.dict['foo'].contains('bar')", expectedResult: true, }, { name: "variable of a list but confused as a map", variables: []NamedExpressionAccessor{ &testVariable{ name:       "list", expression: "[1, 2, 3, 4]", }, }, attributes:           endpointCreateAttributes(), expression:           "variables.list['invalid'] == 'invalid'", expectErr:            true, expectedErrorMessage: "found no matching overload for '_[_]' applied to '(list(int), string)'", }, { name: "list of strings, but element is confused as an integer", variables: []NamedExpressionAccessor{ &testVariable{ name:       "list", expression: "['1', '2', '3', '4']", }, }, attributes:           endpointCreateAttributes(), expression:           "variables.list[0] == 1", expectErr:            true, expectedErrorMessage: "found no matching overload for '_==_' applied to '(string, int)'", }, </ins> } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) {

Expression: "object.spec.replicas", }). WithVariable(admissionregistrationv1beta1.Variable{ <del> Name:       "replicasReminder", // a bit artificial but good for testing purpose Expression: "variables.replicas % 2", </del> <ins> Name:       "oddReplicas", Expression: "variables.replicas % 2 == 1", </ins> }). WithValidation(admissionregistrationv1beta1.Validation{ <del> Expression: "variables.replicas > 1 && variables.replicasReminder == 1", </del> <ins> Expression: "variables.replicas > 1", }). WithValidation(admissionregistrationv1beta1.Validation{ Expression: "variables.oddReplicas", </ins> }). Build() policy, err := client.AdmissionregistrationV1beta1().ValidatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{})