Change:
Noticed a copy & paste issue when reading through the CEL code. Basically here: This should be instead of: The impact of this issue should be that messageExpressions are always evaluated with the CEL environment of the compatibility version. But stored (i.e. pre-existing) messageExpressions should be evaluated with a CEL environment with the "max" version instead. For users of Kubernetes this means that messageExpressions deployed with one minor version of Kubernetes might not work anymore if they rollback e.g. by one minor version. messageExpressions should be treated the same way as rules in that regard. Deploy Kubernetes 1.30 Deploy CRD that uses messageExpression that is using Sets (which became available with Kubernetes 1.30 that is using compatibilty version 1.29) Rollback to Kubernetes 1.29 Update something in the CRD =CRD update will fail because messageExpression is evaluated with the wrong CEL environment (it should still work) No response <detailsAt least on Kubernetes main, but I would assume this isuse is there since CEL was implemented </details<details</details
There are no sig labels on this issue. Please add an appropriate label by using one of the following commands: - Please see the for a listing of the SIGs, working groups, and committees available. <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
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
cc Will open a PR in a bit

Discussion:
for _, v := range s.XValidations { expressions.rules.Insert(v.Rule) if len(v.MessageExpression) > 0 { <del> expressions.messageExpressions.Insert(v.Rule) </del> <ins> expressions.messageExpressions.Insert(v.MessageExpression) </ins> } } return false

} func TestValidateCustomResourceDefinitionValidationRuleCompatibility(t *testing.T) { <del> allValidationsErrors := []validationMatch{ </del> <ins> allRuleValidationsErrors := []validationMatch{ </ins> invalid("spec", "validation", "openAPIV3Schema", "properties[x]", "x-kubernetes-validations[0]", "rule"), invalid("spec", "validation", "openAPIV3Schema", "properties[obj]", "x-kubernetes-validations[0]", "rule"), invalid("spec", "validation", "openAPIV3Schema", "properties[obj]", "properties[a]", "x-kubernetes-validations[0]", "rule"),

invalid("spec", "validation", "openAPIV3Schema", "properties[map]", "x-kubernetes-validations[0]", "rule"), invalid("spec", "validation", "openAPIV3Schema", "properties[map]", "additionalProperties", "x-kubernetes-validations[0]", "rule"), } <ins> allMessageExpressionValidationsErrors := []validationMatch{ invalid("spec", "validation", "openAPIV3Schema", "properties[x]", "x-kubernetes-validations[0]", "messageExpression"), invalid("spec", "validation", "openAPIV3Schema", "properties[obj]", "x-kubernetes-validations[0]", "messageExpression"), invalid("spec", "validation", "openAPIV3Schema", "properties[obj]", "properties[a]", "x-kubernetes-validations[0]", "messageExpression"), invalid("spec", "validation", "openAPIV3Schema", "properties[array]", "x-kubernetes-validations[0]", "messageExpression"), invalid("spec", "validation", "openAPIV3Schema", "properties[array]", "items", "x-kubernetes-validations[0]", "messageExpression"), invalid("spec", "validation", "openAPIV3Schema", "properties[map]", "x-kubernetes-validations[0]", "messageExpression"), invalid("spec", "validation", "openAPIV3Schema", "properties[map]", "additionalProperties", "x-kubernetes-validations[0]", "messageExpression"), } </ins> tests := []struct { <del> name        string storedRule  string updatedRule string errors      []validationMatch </del> <ins> name                     string storedRule               string updatedRule              string storedMessageExpression  string updatedMessageExpression string errors                   []validationMatch </ins> }{ { <del> name:        "functions declared for storage mode allowed if expression is unchanged from what is stored", storedRule:  "test() == true", updatedRule: "test() == true", </del> <ins> name:                     "functions declared for storage mode allowed if expressions are unchanged from what is stored", storedRule:               "test() == true", updatedRule:              "test() == true", storedMessageExpression:  "'test: %s'.format([test()])", updatedMessageExpression: "'test: %s'.format([test()])", }, { name:                     "functions declared for storage mode not allowed if rule expression is changed", storedRule:               "test() == false", updatedRule:              "test() == true", // rule was changed storedMessageExpression:  "'test: %s'.format([test()])", updatedMessageExpression: "'test: %s'.format([test()])", errors:                   allRuleValidationsErrors, </ins> }, { <del> name:        "functions declared for storage mode not allowed if expression is changed", storedRule:  "test() == false", updatedRule: "test() == true", errors:      allValidationsErrors, </del> <ins> name:                     "functions declared for storage mode not allowed if message expression is changed", storedRule:               "test() == true", updatedRule:              "test() == true", storedMessageExpression:  "'test: %s'.format([test()])", updatedMessageExpression: "'test - updated: %s'.format([test()])", // messageExpression was changed errors:                   allMessageExpressionValidationsErrors, </ins> }, }

} for _, tc := range tests { <del> fn := func(rule string) *apiextensions.CustomResourceDefinition { </del> <ins> fn := func(rule, messageExpression string) *apiextensions.CustomResourceDefinition { </ins> validationRules := []apiextensions.ValidationRule{ { <del> Rule: rule, </del> <ins> Rule:              rule, MessageExpression: messageExpression, </ins> }, } return &apiextensions.CustomResourceDefinition{

Status: apiextensions.CustomResourceDefinitionStatus{StoredVersions: []string{"version"}}, } } <del> old := fn(tc.storedRule) resource := fn(tc.updatedRule) </del> <ins> old := fn(tc.storedRule, tc.storedMessageExpression) resource := fn(tc.updatedRule, tc.updatedMessageExpression) </ins> t.Run(tc.name, func(t *testing.T) { ctx := context.TODO()