diff --git a/api/openapi-spec/swagger.json b/api/openapi-spec/swagger.json
index c40a03b15b6..d94c7eebb8b 100644
--- a/api/openapi-spec/swagger.json
+++ b/api/openapi-spec/swagger.json
@@ -770,6 +770,19 @@
},
"type": "array",
"x-kubernetes-list-type": "atomic"
+ },
+ "variables": {
+ "description": "Variables contain definitions of variables that can be used in composition of other expressions. Each variable is defined as a named CEL expression. The variables defined here will be available under `variables` in other expressions of the policy except MatchConditions because MatchConditions are evaluated before the rest of the policy.\n\nThe expression of a variable can refer to other variables defined earlier in the list but not those after. Thus, Variables must be sorted by the order of first appearance and acyclic.",
+ "items": {
+ "$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.Variable"
+ },
+ "type": "array",
+ "x-kubernetes-list-map-keys": [
+ "name"
+ ],
+ "x-kubernetes-list-type": "map",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
}
},
"type": "object"
@@ -804,7 +817,7 @@
"description": "Validation specifies the CEL expression which is used to apply the validation.",
"properties": {
"expression": {
- "description": "Expression represents the expression which will be evaluated by CEL. ref: https://github.com/google/cel-spec CEL expressions have access to the contents of the API request/response, organized into CEL variables as well as some other useful variables:\n\n- 'object' - The object from the incoming request. The value is null for DELETE requests. - 'oldObject' - The existing object. The value is null for CREATE requests. - 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)). - 'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind. - 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.\n See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n- 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the\n request resource.\n\nThe `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` are always accessible from the root of the object. No other metadata properties are accessible.\n\nOnly property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible. Accessible property names are escaped according to the following rules when accessed in the expression: - '__' escapes to '__underscores__' - '.' escapes to '__dot__' - '-' escapes to '__dash__' - '/' escapes to '__slash__' - Property names that exactly match a CEL RESERVED keyword escape to '__{keyword}__'. The keywords are:\n\t \"true\", \"false\", \"null\", \"in\", \"as\", \"break\", \"const\", \"continue\", \"else\", \"for\", \"function\", \"if\",\n\t \"import\", \"let\", \"loop\", \"package\", \"namespace\", \"return\".\nExamples:\n - Expression accessing a property named \"namespace\": {\"Expression\": \"object.__namespace__ > 0\"}\n - Expression accessing a property named \"x-prop\": {\"Expression\": \"object.x__dash__prop > 0\"}\n - Expression accessing a property named \"redact__d\": {\"Expression\": \"object.redact__underscores__d > 0\"}\n\nEquality on arrays with list type of 'set' or 'map' ignores element order, i.e. [1, 2] == [2, 1]. Concatenation on arrays with x-kubernetes-list-type use the semantics of the list type:\n - 'set': `X + Y` performs a union where the array positions of all elements in `X` are preserved and\n non-intersecting elements in `Y` are appended, retaining their partial order.\n - 'map': `X + Y` performs a merge where the array positions of all keys in `X` are preserved but the values\n are overwritten by values in `Y` when the key sets of `X` and `Y` intersect. Elements in `Y` with\n non-intersecting keys are appended, retaining their partial order.\nRequired.",
+ "description": "Expression represents the expression which will be evaluated by CEL. ref: https://github.com/google/cel-spec CEL expressions have access to the contents of the API request/response, organized into CEL variables as well as some other useful variables:\n\n- 'object' - The object from the incoming request. The value is null for DELETE requests. - 'oldObject' - The existing object. The value is null for CREATE requests. - 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)). - 'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind. - 'variables' - Map of composited variables, from its name to its lazily evaluated value.\n For example, a variable named 'foo' can be accessed as 'variables.foo'.\n- 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.\n See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n- 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the\n request resource.\n\nThe `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` are always accessible from the root of the object. No other metadata properties are accessible.\n\nOnly property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible. Accessible property names are escaped according to the following rules when accessed in the expression: - '__' escapes to '__underscores__' - '.' escapes to '__dot__' - '-' escapes to '__dash__' - '/' escapes to '__slash__' - Property names that exactly match a CEL RESERVED keyword escape to '__{keyword}__'. The keywords are:\n\t \"true\", \"false\", \"null\", \"in\", \"as\", \"break\", \"const\", \"continue\", \"else\", \"for\", \"function\", \"if\",\n\t \"import\", \"let\", \"loop\", \"package\", \"namespace\", \"return\".\nExamples:\n - Expression accessing a property named \"namespace\": {\"Expression\": \"object.__namespace__ > 0\"}\n - Expression accessing a property named \"x-prop\": {\"Expression\": \"object.x__dash__prop > 0\"}\n - Expression accessing a property named \"redact__d\": {\"Expression\": \"object.redact__underscores__d > 0\"}\n\nEquality on arrays with list type of 'set' or 'map' ignores element order, i.e. [1, 2] == [2, 1]. Concatenation on arrays with x-kubernetes-list-type use the semantics of the list type:\n - 'set': `X + Y` performs a union where the array positions of all elements in `X` are preserved and\n non-intersecting elements in `Y` are appended, retaining their partial order.\n - 'map': `X + Y` performs a merge where the array positions of all keys in `X` are preserved but the values\n are overwritten by values in `Y` when the key sets of `X` and `Y` intersect. Elements in `Y` with\n non-intersecting keys are appended, retaining their partial order.\nRequired.",
"type": "string"
},
"message": {
@@ -825,6 +838,24 @@
],
"type": "object"
},
+ "io.k8s.api.admissionregistration.v1alpha1.Variable": {
+ "description": "Variable is the definition of a variable that is used for composition.",
+ "properties": {
+ "expression": {
+ "description": "Expression is the expression that will be evaluated as the value of the variable. The CEL expression has access to the same identifiers as the CEL expressions in Validation.",
+ "type": "string"
+ },
+ "name": {
+ "description": "Name is the name of the variable. The name must be a valid CEL identifier and unique among all variables. The variable can be accessed in other expressions through `variables` For example, if name is \"foo\", the variable will be available as `variables.foo`",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "expression"
+ ],
+ "type": "object"
+ },
"io.k8s.api.apiserverinternal.v1alpha1.ServerStorageVersion": {
"description": "An API server instance reports the version it can decode and the version it encodes objects to when persisting objects in the backend.",
"properties": {
diff --git a/api/openapi-spec/v3/apis__admissionregistration.k8s.io__v1alpha1_openapi.json b/api/openapi-spec/v3/apis__admissionregistration.k8s.io__v1alpha1_openapi.json
index 3a5fc656d4a..eee337179da 100644
--- a/api/openapi-spec/v3/apis__admissionregistration.k8s.io__v1alpha1_openapi.json
+++ b/api/openapi-spec/v3/apis__admissionregistration.k8s.io__v1alpha1_openapi.json
@@ -490,6 +490,24 @@
},
"type": "array",
"x-kubernetes-list-type": "atomic"
+ },
+ "variables": {
+ "description": "Variables contain definitions of variables that can be used in composition of other expressions. Each variable is defined as a named CEL expression. The variables defined here will be available under `variables` in other expressions of the policy except MatchConditions because MatchConditions are evaluated before the rest of the policy.\n\nThe expression of a variable can refer to other variables defined earlier in the list but not those after. Thus, Variables must be sorted by the order of first appearance and acyclic.",
+ "items": {
+ "allOf": [
+ {
+ "$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.Variable"
+ }
+ ],
+ "default": {}
+ },
+ "type": "array",
+ "x-kubernetes-list-map-keys": [
+ "name"
+ ],
+ "x-kubernetes-list-type": "map",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge"
}
},
"type": "object"
@@ -534,7 +552,7 @@
"properties": {
"expression": {
"default": "",
- "description": "Expression represents the expression which will be evaluated by CEL. ref: https://github.com/google/cel-spec CEL expressions have access to the contents of the API request/response, organized into CEL variables as well as some other useful variables:\n\n- 'object' - The object from the incoming request. The value is null for DELETE requests. - 'oldObject' - The existing object. The value is null for CREATE requests. - 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)). - 'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind. - 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.\n See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n- 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the\n request resource.\n\nThe `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` are always accessible from the root of the object. No other metadata properties are accessible.\n\nOnly property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible. Accessible property names are escaped according to the following rules when accessed in the expression: - '__' escapes to '__underscores__' - '.' escapes to '__dot__' - '-' escapes to '__dash__' - '/' escapes to '__slash__' - Property names that exactly match a CEL RESERVED keyword escape to '__{keyword}__'. The keywords are:\n\t \"true\", \"false\", \"null\", \"in\", \"as\", \"break\", \"const\", \"continue\", \"else\", \"for\", \"function\", \"if\",\n\t \"import\", \"let\", \"loop\", \"package\", \"namespace\", \"return\".\nExamples:\n - Expression accessing a property named \"namespace\": {\"Expression\": \"object.__namespace__ > 0\"}\n - Expression accessing a property named \"x-prop\": {\"Expression\": \"object.x__dash__prop > 0\"}\n - Expression accessing a property named \"redact__d\": {\"Expression\": \"object.redact__underscores__d > 0\"}\n\nEquality on arrays with list type of 'set' or 'map' ignores element order, i.e. [1, 2] == [2, 1]. Concatenation on arrays with x-kubernetes-list-type use the semantics of the list type:\n - 'set': `X + Y` performs a union where the array positions of all elements in `X` are preserved and\n non-intersecting elements in `Y` are appended, retaining their partial order.\n - 'map': `X + Y` performs a merge where the array positions of all keys in `X` are preserved but the values\n are overwritten by values in `Y` when the key sets of `X` and `Y` intersect. Elements in `Y` with\n non-intersecting keys are appended, retaining their partial order.\nRequired.",
+ "description": "Expression represents the expression which will be evaluated by CEL. ref: https://github.com/google/cel-spec CEL expressions have access to the contents of the API request/response, organized into CEL variables as well as some other useful variables:\n\n- 'object' - The object from the incoming request. The value is null for DELETE requests. - 'oldObject' - The existing object. The value is null for CREATE requests. - 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)). - 'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind. - 'variables' - Map of composited variables, from its name to its lazily evaluated value.\n For example, a variable named 'foo' can be accessed as 'variables.foo'.\n- 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.\n See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n- 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the\n request resource.\n\nThe `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` are always accessible from the root of the object. No other metadata properties are accessible.\n\nOnly property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible. Accessible property names are escaped according to the following rules when accessed in the expression: - '__' escapes to '__underscores__' - '.' escapes to '__dot__' - '-' escapes to '__dash__' - '/' escapes to '__slash__' - Property names that exactly match a CEL RESERVED keyword escape to '__{keyword}__'. The keywords are:\n\t \"true\", \"false\", \"null\", \"in\", \"as\", \"break\", \"const\", \"continue\", \"else\", \"for\", \"function\", \"if\",\n\t \"import\", \"let\", \"loop\", \"package\", \"namespace\", \"return\".\nExamples:\n - Expression accessing a property named \"namespace\": {\"Expression\": \"object.__namespace__ > 0\"}\n - Expression accessing a property named \"x-prop\": {\"Expression\": \"object.x__dash__prop > 0\"}\n - Expression accessing a property named \"redact__d\": {\"Expression\": \"object.redact__underscores__d > 0\"}\n\nEquality on arrays with list type of 'set' or 'map' ignores element order, i.e. [1, 2] == [2, 1]. Concatenation on arrays with x-kubernetes-list-type use the semantics of the list type:\n - 'set': `X + Y` performs a union where the array positions of all elements in `X` are preserved and\n non-intersecting elements in `Y` are appended, retaining their partial order.\n - 'map': `X + Y` performs a merge where the array positions of all keys in `X` are preserved but the values\n are overwritten by values in `Y` when the key sets of `X` and `Y` intersect. Elements in `Y` with\n non-intersecting keys are appended, retaining their partial order.\nRequired.",
"type": "string"
},
"message": {
@@ -555,6 +573,26 @@
],
"type": "object"
},
+ "io.k8s.api.admissionregistration.v1alpha1.Variable": {
+ "description": "Variable is the definition of a variable that is used for composition.",
+ "properties": {
+ "expression": {
+ "default": "",
+ "description": "Expression is the expression that will be evaluated as the value of the variable. The CEL expression has access to the same identifiers as the CEL expressions in Validation.",
+ "type": "string"
+ },
+ "name": {
+ "default": "",
+ "description": "Name is the name of the variable. The name must be a valid CEL identifier and unique among all variables. The variable can be accessed in other expressions through `variables` For example, if name is \"foo\", the variable will be available as `variables.foo`",
+ "type": "string"
+ }
+ },
+ "required": [
+ "name",
+ "expression"
+ ],
+ "type": "object"
+ },
"io.k8s.apimachinery.pkg.apis.meta.v1.APIResource": {
"description": "APIResource specifies the name of a resource and whether it is namespaced.",
"properties": {
diff --git a/pkg/apis/admissionregistration/types.go b/pkg/apis/admissionregistration/types.go
index a6a4bf85640..c80511352dc 100644
--- a/pkg/apis/admissionregistration/types.go
+++ b/pkg/apis/admissionregistration/types.go
@@ -247,6 +247,16 @@ type ValidatingAdmissionPolicySpec struct {
// A maximum of 20 auditAnnotation are allowed per ValidatingAdmissionPolicy.
// +optional
AuditAnnotations []AuditAnnotation
+
+ // Variables contain definitions of variables that can be used in composition of other expressions.
+ // Each variable is defined as a named CEL expression.
+ // The variables defined here will be available under `variables` in other expressions of the policy
+ // except MatchConditions because MatchConditions are evaluated before the rest of the policy.
+ //
+ // The expression of a variable can refer to other variables defined earlier in the list but not those after.
+ // Thus, Variables must be sorted by the order of first appearance and acyclic.
+ // +optional
+ Variables []Variable
}
// ParamKind is a tuple of Group Kind and Version.
@@ -271,6 +281,8 @@ type Validation struct {
//'oldObject' - The existing object. The value is null for CREATE requests.
//'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)).
//'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind.
+ //'variables' - Map of composited variables, from its name to its lazily evaluated value.
+ // For example, a variable named 'foo' can be accessed as 'variables.foo'
// - 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.
// See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz
// - 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the
@@ -334,6 +346,19 @@ type Validation struct {
MessageExpression string
}
+// Variable is the definition of a variable that is used for composition. A variable is defined as a named expression.
+// +structType=atomic
+type Variable struct {
+ // Name is the name of the variable. The name must be a valid CEL identifier and unique among all variables.
+ // The variable can be accessed in other expressions through `variables`
+ // For example, if name is "foo", the variable will be available as `variables.foo`
+ Name string
+
+ // Expression is the expression that will be evaluated as the value of the variable.
+ // The CEL expression has access to the same identifiers as the CEL expressions in Validation.
+ Expression string
+}
+
// AuditAnnotation describes how to produce an audit annotation for an API request.
type AuditAnnotation struct {
// key specifies the audit annotation key. The audit annotation keys of
@@ -1065,6 +1090,8 @@ type MatchCondition struct {
// See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz
// 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the
// request resource.
+ // 'variables' - Map of composited variables, from its name to its lazily evaluated value.
+ // For example, a variable named 'foo' can be access as 'variables.foo'
// Documentation on CEL: https://kubernetes.io/docs/reference/using-api/cel/
//
// Required.
diff --git a/pkg/apis/admissionregistration/v1alpha1/zz_generated.conversion.go b/pkg/apis/admissionregistration/v1alpha1/zz_generated.conversion.go
index 4e415bf000f..34c7f723083 100644
--- a/pkg/apis/admissionregistration/v1alpha1/zz_generated.conversion.go
+++ b/pkg/apis/admissionregistration/v1alpha1/zz_generated.conversion.go
@@ -199,6 +199,16 @@ func RegisterConversions(s *runtime.Scheme) error {
}); err != nil {
return err
}
+ if err := s.AddGeneratedConversionFunc((*v1alpha1.Variable)(nil), (*admissionregistration.Variable)(nil), func(a, b interface{}, scope conversion.Scope) error {
+ return Convert_v1alpha1_Variable_To_admissionregistration_Variable(a.(*v1alpha1.Variable), b.(*admissionregistration.Variable), scope)
+ }); err != nil {
+ return err
+ }
+ if err := s.AddGeneratedConversionFunc((*admissionregistration.Variable)(nil), (*v1alpha1.Variable)(nil), func(a, b interface{}, scope conversion.Scope) error {
+ return Convert_admissionregistration_Variable_To_v1alpha1_Variable(a.(*admissionregistration.Variable), b.(*v1alpha1.Variable), scope)
+ }); err != nil {
+ return err
+ }
return nil
}
@@ -625,6 +635,7 @@ func autoConvert_v1alpha1_ValidatingAdmissionPolicySpec_To_admissionregistration
out.FailurePolicy = (*admissionregistration.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy))
out.AuditAnnotations = *(*[]admissionregistration.AuditAnnotation)(unsafe.Pointer(&in.AuditAnnotations))
out.MatchConditions = *(*[]admissionregistration.MatchCondition)(unsafe.Pointer(&in.MatchConditions))
+ out.Variables = *(*[]admissionregistration.Variable)(unsafe.Pointer(&in.Variables))
return nil
}
@@ -648,6 +659,7 @@ func autoConvert_admissionregistration_ValidatingAdmissionPolicySpec_To_v1alpha1
out.MatchConditions = *(*[]v1alpha1.MatchCondition)(unsafe.Pointer(&in.MatchConditions))
out.FailurePolicy = (*v1alpha1.FailurePolicyType)(unsafe.Pointer(in.FailurePolicy))
out.AuditAnnotations = *(*[]v1alpha1.AuditAnnotation)(unsafe.Pointer(&in.AuditAnnotations))
+ out.Variables = *(*[]v1alpha1.Variable)(unsafe.Pointer(&in.Variables))
return nil
}
@@ -705,3 +717,25 @@ func autoConvert_admissionregistration_Validation_To_v1alpha1_Validation(in *adm
func Convert_admissionregistration_Validation_To_v1alpha1_Validation(in *admissionregistration.Validation, out *v1alpha1.Validation, s conversion.Scope) error {
return autoConvert_admissionregistration_Validation_To_v1alpha1_Validation(in, out, s)
}
+
+func autoConvert_v1alpha1_Variable_To_admissionregistration_Variable(in *v1alpha1.Variable, out *admissionregistration.Variable, s conversion.Scope) error {
+ out.Name = in.Name
+ out.Expression = in.Expression
+ return nil
+}
+
+// Convert_v1alpha1_Variable_To_admissionregistration_Variable is an autogenerated conversion function.
+func Convert_v1alpha1_Variable_To_admissionregistration_Variable(in *v1alpha1.Variable, out *admissionregistration.Variable, s conversion.Scope) error {
+ return autoConvert_v1alpha1_Variable_To_admissionregistration_Variable(in, out, s)
+}
+
+func autoConvert_admissionregistration_Variable_To_v1alpha1_Variable(in *admissionregistration.Variable, out *v1alpha1.Variable, s conversion.Scope) error {
+ out.Name = in.Name
+ out.Expression = in.Expression
+ return nil
+}
+
+// Convert_admissionregistration_Variable_To_v1alpha1_Variable is an autogenerated conversion function.
+func Convert_admissionregistration_Variable_To_v1alpha1_Variable(in *admissionregistration.Variable, out *v1alpha1.Variable, s conversion.Scope) error {
+ return autoConvert_admissionregistration_Variable_To_v1alpha1_Variable(in, out, s)
+}
diff --git a/pkg/apis/admissionregistration/validation/validation.go b/pkg/apis/admissionregistration/validation/validation.go
index 167b5eced79..1f54ab725f2 100644
--- a/pkg/apis/admissionregistration/validation/validation.go
+++ b/pkg/apis/admissionregistration/validation/validation.go
@@ -26,6 +26,7 @@ import (
"k8s.io/apimachinery/pkg/api/validation/path"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metav1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets"
utilvalidation "k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
@@ -723,6 +724,14 @@ func validateValidatingAdmissionPolicy(p *admissionregistration.ValidatingAdmiss
func validateValidatingAdmissionPolicySpec(meta metav1.ObjectMeta, spec *admissionregistration.ValidatingAdmissionPolicySpec, opts validationOptions, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
+ var compiler plugincel.Compiler // composition compiler is stateful, create one lazily per policy
+ getCompiler := func() plugincel.Compiler {
+ if compiler == nil {
+ needsComposition := len(spec.Variables) > 0
+ compiler = createCompiler(needsComposition)
+ }
+ return compiler
+ }
if spec.FailurePolicy == nil {
allErrors = append(allErrors, field.Required(fldPath.Child("failurePolicy"), ""))
} else if !supportedFailurePolicies.Has(string(*spec.FailurePolicy)) {
@@ -744,12 +753,17 @@ func validateValidatingAdmissionPolicySpec(meta metav1.ObjectMeta, spec *admissi
if !opts.ignoreMatchConditions {
allErrors = append(allErrors, validateMatchConditions(spec.MatchConditions, opts, fldPath.Child("matchConditions"))...)
}
+ if len(spec.Variables) > 0 {
+ for i, variable := range spec.Variables {
+ allErrors = append(allErrors, validateVariable(getCompiler(), &variable, spec.ParamKind, opts, fldPath.Child("variables").Index(i))...)
+ }
+ }
if len(spec.Validations) == 0 && len(spec.AuditAnnotations) == 0 {
allErrors = append(allErrors, field.Required(fldPath.Child("validations"), "validations or auditAnnotations must contain at least one item"))
allErrors = append(allErrors, field.Required(fldPath.Child("auditAnnotations"), "validations or auditAnnotations must contain at least one item"))
} else {
for i, validation := range spec.Validations {
- allErrors = append(allErrors, validateValidation(&validation, spec.ParamKind, opts, fldPath.Child("validations").Index(i))...)
+ allErrors = append(allErrors, validateValidation(getCompiler(), &validation, spec.ParamKind, opts, fldPath.Child("validations").Index(i))...)
}
if spec.AuditAnnotations != nil {
keys := sets.NewString()
@@ -757,7 +771,7 @@ func validateValidatingAdmissionPolicySpec(meta metav1.ObjectMeta, spec *admissi
allErrors = append(allErrors, field.Invalid(fldPath.Child("auditAnnotations"), spec.AuditAnnotations, fmt.Sprintf("must not have more than %d auditAnnotations", maxAuditAnnotations)))
}
for i, auditAnnotation := range spec.AuditAnnotations {
- allErrors = append(allErrors, validateAuditAnnotation(meta, &auditAnnotation, spec.ParamKind, opts, fldPath.Child("auditAnnotations").Index(i))...)
+ allErrors = append(allErrors, validateAuditAnnotation(getCompiler(), meta, &auditAnnotation, spec.ParamKind, opts, fldPath.Child("auditAnnotations").Index(i))...)
if keys.Has(auditAnnotation.Key) {
allErrors = append(allErrors, field.Duplicate(fldPath.Child("auditAnnotations").Index(i).Child("key"), auditAnnotation.Key))
}
@@ -935,7 +949,42 @@ func validateMatchCondition(v *admissionregistration.MatchCondition, opts valida
return allErrors
}
-func validateValidation(v *admissionregistration.Validation, paramKind *admissionregistration.ParamKind, opts validationOptions, fldPath *field.Path) field.ErrorList {
+func validateVariable(compiler plugincel.Compiler, v *admissionregistration.Variable, paramKind *admissionregistration.ParamKind, opts validationOptions, fldPath *field.Path) field.ErrorList {
+ var allErrors field.ErrorList
+ if len(v.Name) == 0 || strings.TrimSpace(v.Name) == "" {
+ allErrors = append(allErrors, field.Required(fldPath.Child("name"), "name is not specified"))
+ } else {
+ if !isCELIdentifier(v.Name) {
+ allErrors = append(allErrors, field.Invalid(fldPath.Child("name"), v.Name, "name is not a valid CEL identifier"))
+ }
+ }
+ if len(v.Expression) == 0 || strings.TrimSpace(v.Expression) == "" {
+ allErrors = append(allErrors, field.Required(fldPath.Child("expression"), "expression is not specified"))
+ } else {
+ if compiler, ok := compiler.(*plugincel.CompositedCompiler); ok {
+ envType := environment.NewExpressions
+ if opts.preexistingExpressions.validationExpressions.Has(v.Expression) {
+ envType = environment.StoredExpressions
+ }
+ variable := &validatingadmissionpolicy.Variable{
+ Name: v.Name,
+ Expression: v.Expression,
+ }
+ result := compiler.CompileAndStoreVariable(variable, plugincel.OptionalVariableDeclarations{
+ HasParams: paramKind != nil,
+ HasAuthorizer: true,
+ }, envType)
+ if result.Error != nil {
+ allErrors = append(allErrors, convertCELErrorToValidationError(fldPath.Child("expression"), variable, result.Error))
+ }
+ } else {
+ allErrors = append(allErrors, field.InternalError(fldPath, fmt.Errorf("variable composition is not allowed")))
+ }
+ }
+ return allErrors
+}
+
+func validateValidation(compiler plugincel.Compiler, v *admissionregistration.Validation, paramKind *admissionregistration.ParamKind, opts validationOptions, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
trimmedExpression := strings.TrimSpace(v.Expression)
trimmedMsg := strings.TrimSpace(v.Message)
@@ -943,14 +992,14 @@ func validateValidation(v *admissionregistration.Validation, paramKind *admissio
if len(trimmedExpression) == 0 {
allErrors = append(allErrors, field.Required(fldPath.Child("expression"), "expression is not specified"))
} else {
- allErrors = append(allErrors, validateValidationExpression(v.Expression, paramKind != nil, opts, fldPath.Child("expression"))...)
+ allErrors = append(allErrors, validateValidationExpression(compiler, v.Expression, paramKind != nil, opts, fldPath.Child("expression"))...)
}
if len(v.MessageExpression) > 0 && len(trimmedMessageExpression) == 0 {
allErrors = append(allErrors, field.Invalid(fldPath.Child("messageExpression"), v.MessageExpression, "must be non-empty if specified"))
} else if len(trimmedMessageExpression) != 0 {
// use v.MessageExpression instead of trimmedMessageExpression so that
// the compiler output shows the correct column.
- allErrors = append(allErrors, validateMessageExpression(v.MessageExpression, opts, fldPath.Child("messageExpression"))...)
+ allErrors = append(allErrors, validateMessageExpression(compiler, v.MessageExpression, opts, fldPath.Child("messageExpression"))...)
}
if len(v.Message) > 0 && len(trimmedMsg) == 0 {
allErrors = append(allErrors, field.Invalid(fldPath.Child("message"), v.Message, "message must be non-empty if specified"))
@@ -965,31 +1014,35 @@ func validateValidation(v *admissionregistration.Validation, paramKind *admissio
return allErrors
}
-func validateCELCondition(expression plugincel.ExpressionAccessor, variables plugincel.OptionalVariableDeclarations, envType environment.Type, fldPath *field.Path) field.ErrorList {
+func validateCELCondition(compiler plugincel.Compiler, expression plugincel.ExpressionAccessor, variables plugincel.OptionalVariableDeclarations, envType environment.Type, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
-
result := compiler.CompileCELExpression(expression, variables, envType)
if result.Error != nil {
- switch result.Error.Type {
- case cel.ErrorTypeRequired:
- allErrors = append(allErrors, field.Required(fldPath, result.Error.Detail))
- case cel.ErrorTypeInvalid:
- allErrors = append(allErrors, field.Invalid(fldPath, expression.GetExpression(), result.Error.Detail))
- case cel.ErrorTypeInternal:
- allErrors = append(allErrors, field.InternalError(fldPath, result.Error))
- default:
- allErrors = append(allErrors, field.InternalError(fldPath, fmt.Errorf("unsupported error type: %w", result.Error)))
- }
+ allErrors = append(allErrors, convertCELErrorToValidationError(fldPath, expression, result.Error))
}
return allErrors
}
-func validateValidationExpression(expression string, hasParams bool, opts validationOptions, fldPath *field.Path) field.ErrorList {
+func convertCELErrorToValidationError(fldPath *field.Path, expression plugincel.ExpressionAccessor, err error) *field.Error {
+ if celErr, ok := err.(*cel.Error); ok {
+ switch celErr.Type {
+ case cel.ErrorTypeRequired:
+ return field.Required(fldPath, celErr.Detail)
+ case cel.ErrorTypeInvalid:
+ return field.Invalid(fldPath, expression.GetExpression(), celErr.Detail)
+ case cel.ErrorTypeInternal:
+ return field.InternalError(fldPath, celErr)
+ }
+ }
+ return field.InternalError(fldPath, fmt.Errorf("unsupported error type: %w", err))
+}
+
+func validateValidationExpression(compiler plugincel.Compiler, expression string, hasParams bool, opts validationOptions, fldPath *field.Path) field.ErrorList {
envType := environment.NewExpressions
if opts.preexistingExpressions.validationExpressions.Has(expression) {
envType = environment.StoredExpressions
}
- return validateCELCondition(&validatingadmissionpolicy.ValidationCondition{
+ return validateCELCondition(compiler, &validatingadmissionpolicy.ValidationCondition{
Expression: expression,
}, plugincel.OptionalVariableDeclarations{
HasParams: hasParams,
@@ -1002,7 +1055,7 @@ func validateMatchConditionsExpression(expression string, opts validationOptions
if opts.preexistingExpressions.matchConditionExpressions.Has(expression) {
envType = environment.StoredExpressions
}
- return validateCELCondition(&matchconditions.MatchCondition{
+ return validateCELCondition(statelessCELCompiler, &matchconditions.MatchCondition{
Expression: expression,
}, plugincel.OptionalVariableDeclarations{
HasParams: opts.allowParamsInMatchConditions,
@@ -1010,12 +1063,12 @@ func validateMatchConditionsExpression(expression string, opts validationOptions
}, envType, fldPath)
}
-func validateMessageExpression(expression string, opts validationOptions, fldPath *field.Path) field.ErrorList {
+func validateMessageExpression(compiler plugincel.Compiler, expression string, opts validationOptions, fldPath *field.Path) field.ErrorList {
envType := environment.NewExpressions
if opts.preexistingExpressions.validationMessageExpressions.Has(expression) {
envType = environment.StoredExpressions
}
- return validateCELCondition(&validatingadmissionpolicy.MessageExpressionCondition{
+ return validateCELCondition(compiler, &validatingadmissionpolicy.MessageExpressionCondition{
MessageExpression: expression,
}, plugincel.OptionalVariableDeclarations{
HasParams: opts.allowParamsInMatchConditions,
@@ -1023,7 +1076,7 @@ func validateMessageExpression(expression string, opts validationOptions, fldPat
}, envType, fldPath)
}
-func validateAuditAnnotation(meta metav1.ObjectMeta, v *admissionregistration.AuditAnnotation, paramKind *admissionregistration.ParamKind, opts validationOptions, fldPath *field.Path) field.ErrorList {
+func validateAuditAnnotation(compiler plugincel.Compiler, meta metav1.ObjectMeta, v *admissionregistration.AuditAnnotation, paramKind *admissionregistration.ParamKind, opts validationOptions, fldPath *field.Path) field.ErrorList {
var allErrors field.ErrorList
if len(meta.GetName()) != 0 {
name := meta.GetName()
@@ -1166,4 +1219,38 @@ func validateFieldRef(fieldRef string, fldPath *field.Path) field.ErrorList {
return nil
}
-var compiler = plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()))
+// statelessCELCompiler does not support variable composition (and thus is stateless). It should be used when
+// variable composition is not allowed, for example, when validating MatchConditions.
+var statelessCELCompiler = plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()))
+
+func createCompiler(allowComposition bool) plugincel.Compiler {
+ if !allowComposition {
+ return statelessCELCompiler
+ }
+ compiler, err := plugincel.NewCompositedCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()))
+ if err != nil {
+ // should never happen, but cannot panic either.
+ utilruntime.HandleError(err)
+ return plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()))
+ }
+ return compiler
+}
+
+var celIdentRegex = regexp.MustCompile("^[_a-zA-Z][_a-zA-Z0-9]*$")
+var celReserved = sets.NewString("true", "false", "null", "in",
+ "as", "break", "const", "continue", "else",
+ "for", "function", "if", "import", "let",
+ "loop", "package", "namespace", "return",
+ "var", "void", "while")
+
+func isCELIdentifier(name string) bool {
+ // IDENT ::= [_a-zA-Z][_a-zA-Z0-9]* - RESERVED
+ // BOOL_LIT ::= "true" | "false"
+ // NULL_LIT ::= "null"
+ // RESERVED ::= BOOL_LIT | NULL_LIT | "in"
+ // | "as" | "break" | "const" | "continue" | "else"
+ // | "for" | "function" | "if" | "import" | "let"
+ // | "loop" | "package" | "namespace" | "return"
+ // | "var" | "void" | "while"
+ return celIdentRegex.MatchString(name) && !celReserved.Has(name)
+}
diff --git a/pkg/apis/admissionregistration/validation/validation_test.go b/pkg/apis/admissionregistration/validation/validation_test.go
index 60c177c4098..2c7a13d770f 100644
--- a/pkg/apis/admissionregistration/validation/validation_test.go
+++ b/pkg/apis/admissionregistration/validation/validation_test.go
@@ -2864,7 +2864,128 @@ func TestValidateValidatingAdmissionPolicy(t *testing.T) {
},
},
expectedError: `undeclared reference to 'params'`,
- }}
+ }, {
+ name: "variable composition empty name",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Variables: []admissionregistration.Variable{
+ {
+ Name: " ",
+ Expression: "true",
+ },
+ },
+ Validations: []admissionregistration.Validation{
+ {
+ Expression: "true",
+ },
+ },
+ },
+ },
+ expectedError: `spec.variables[0].name: Required value: name is not specified`,
+ }, {
+ name: "variable composition name is not a valid identifier",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Variables: []admissionregistration.Variable{
+ {
+ Name: "4ever",
+ Expression: "true",
+ },
+ },
+ Validations: []admissionregistration.Validation{
+ {
+ Expression: "true",
+ },
+ },
+ },
+ },
+ expectedError: `spec.variables[0].name: Invalid value: "4ever": name is not a valid CEL identifier`,
+ }, {
+ name: "variable composition cannot compile",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Variables: []admissionregistration.Variable{
+ {
+ Name: "foo",
+ Expression: "114 + '514'", // compile error: type confusion
+ },
+ },
+ Validations: []admissionregistration.Validation{
+ {
+ Expression: "true",
+ },
+ },
+ },
+ },
+ expectedError: `spec.variables[0].expression: Invalid value: "114 + '514'": compilation failed: ERROR: :1:5: found no matching overload for '_+_' applied to '(int, string)`,
+ }, {
+ name: "validation referred to non-existing variable",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Variables: []admissionregistration.Variable{
+ {
+ Name: "foo",
+ Expression: "1 + 1",
+ },
+ {
+ Name: "bar",
+ Expression: "variables.foo + 1",
+ },
+ },
+ Validations: []admissionregistration.Validation{
+ {
+ Expression: "variables.foo > 1", // correct
+ },
+ {
+ Expression: "variables.replicas == 2", // replicas undefined
+ },
+ },
+ },
+ },
+ expectedError: `spec.validations[1].expression: Invalid value: "variables.replicas == 2": compilation failed: ERROR: :1:10: undefined field 'replicas'`,
+ }, {
+ name: "variables wrong order",
+ config: &admissionregistration.ValidatingAdmissionPolicy{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: "config",
+ },
+ Spec: admissionregistration.ValidatingAdmissionPolicySpec{
+ Variables: []admissionregistration.Variable{
+ {
+ Name: "correct",
+ Expression: "object",
+ },
+ {
+ Name: "bar", // should go below foo
+ Expression: "variables.foo + 1",
+ },
+ {
+ Name: "foo",
+ Expression: "1 + 1",
+ },
+ },
+ Validations: []admissionregistration.Validation{
+ {
+ Expression: "variables.foo > 1", // correct
+ },
+ },
+ },
+ },
+ expectedError: `spec.variables[1].expression: Invalid value: "variables.foo + 1": compilation failed: ERROR: :1:10: undefined field 'foo'`,
+ },
+ }
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
errs := ValidateValidatingAdmissionPolicy(test.config)
@@ -3298,9 +3419,9 @@ func TestValidateValidatingAdmissionPolicyUpdate(t *testing.T) {
if err != nil {
t.Fatal(err)
}
- compiler = plugincel.NewCompiler(extended)
+ statelessCELCompiler = plugincel.NewCompiler(extended)
defer func() {
- compiler = plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()))
+ statelessCELCompiler = plugincel.NewCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()))
}()
for _, test := range tests {
diff --git a/pkg/apis/admissionregistration/zz_generated.deepcopy.go b/pkg/apis/admissionregistration/zz_generated.deepcopy.go
index 179ec47171a..e3d2118b2b1 100644
--- a/pkg/apis/admissionregistration/zz_generated.deepcopy.go
+++ b/pkg/apis/admissionregistration/zz_generated.deepcopy.go
@@ -595,6 +595,11 @@ func (in *ValidatingAdmissionPolicySpec) DeepCopyInto(out *ValidatingAdmissionPo
*out = make([]AuditAnnotation, len(*in))
copy(*out, *in)
}
+ if in.Variables != nil {
+ in, out := &in.Variables, &out.Variables
+ *out = make([]Variable, len(*in))
+ copy(*out, *in)
+ }
return
}
@@ -787,6 +792,22 @@ func (in *Validation) DeepCopy() *Validation {
return out
}
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Variable) DeepCopyInto(out *Variable) {
+ *out = *in
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Variable.
+func (in *Variable) DeepCopy() *Variable {
+ if in == nil {
+ return nil
+ }
+ out := new(Variable)
+ in.DeepCopyInto(out)
+ return out
+}
+
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
func (in *WebhookClientConfig) DeepCopyInto(out *WebhookClientConfig) {
*out = *in
diff --git a/pkg/generated/openapi/zz_generated.openapi.go b/pkg/generated/openapi/zz_generated.openapi.go
index 34bf9911eef..05c9cba517a 100644
--- a/pkg/generated/openapi/zz_generated.openapi.go
+++ b/pkg/generated/openapi/zz_generated.openapi.go
@@ -62,6 +62,7 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicySpec": schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicySpec(ref),
"k8s.io/api/admissionregistration/v1alpha1.ValidatingAdmissionPolicyStatus": schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicyStatus(ref),
"k8s.io/api/admissionregistration/v1alpha1.Validation": schema_k8sio_api_admissionregistration_v1alpha1_Validation(ref),
+ "k8s.io/api/admissionregistration/v1alpha1.Variable": schema_k8sio_api_admissionregistration_v1alpha1_Variable(ref),
"k8s.io/api/admissionregistration/v1beta1.MatchCondition": schema_k8sio_api_admissionregistration_v1beta1_MatchCondition(ref),
"k8s.io/api/admissionregistration/v1beta1.MutatingWebhook": schema_k8sio_api_admissionregistration_v1beta1_MutatingWebhook(ref),
"k8s.io/api/admissionregistration/v1beta1.MutatingWebhookConfiguration": schema_k8sio_api_admissionregistration_v1beta1_MutatingWebhookConfiguration(ref),
@@ -2661,11 +2662,35 @@ func schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicySp
},
},
},
+ "variables": {
+ VendorExtensible: spec.VendorExtensible{
+ Extensions: spec.Extensions{
+ "x-kubernetes-list-map-keys": []interface{}{
+ "name",
+ },
+ "x-kubernetes-list-type": "map",
+ "x-kubernetes-patch-merge-key": "name",
+ "x-kubernetes-patch-strategy": "merge",
+ },
+ },
+ SchemaProps: spec.SchemaProps{
+ Description: "Variables contain definitions of variables that can be used in composition of other expressions. Each variable is defined as a named CEL expression. The variables defined here will be available under `variables` in other expressions of the policy except MatchConditions because MatchConditions are evaluated before the rest of the policy.\n\nThe expression of a variable can refer to other variables defined earlier in the list but not those after. Thus, Variables must be sorted by the order of first appearance and acyclic.",
+ Type: []string{"array"},
+ Items: &spec.SchemaOrArray{
+ Schema: &spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Default: map[string]interface{}{},
+ Ref: ref("k8s.io/api/admissionregistration/v1alpha1.Variable"),
+ },
+ },
+ },
+ },
+ },
},
},
},
Dependencies: []string{
- "k8s.io/api/admissionregistration/v1alpha1.AuditAnnotation", "k8s.io/api/admissionregistration/v1alpha1.MatchCondition", "k8s.io/api/admissionregistration/v1alpha1.MatchResources", "k8s.io/api/admissionregistration/v1alpha1.ParamKind", "k8s.io/api/admissionregistration/v1alpha1.Validation"},
+ "k8s.io/api/admissionregistration/v1alpha1.AuditAnnotation", "k8s.io/api/admissionregistration/v1alpha1.MatchCondition", "k8s.io/api/admissionregistration/v1alpha1.MatchResources", "k8s.io/api/admissionregistration/v1alpha1.ParamKind", "k8s.io/api/admissionregistration/v1alpha1.Validation", "k8s.io/api/admissionregistration/v1alpha1.Variable"},
}
}
@@ -2728,7 +2753,7 @@ func schema_k8sio_api_admissionregistration_v1alpha1_Validation(ref common.Refer
Properties: map[string]spec.Schema{
"expression": {
SchemaProps: spec.SchemaProps{
- Description: "Expression represents the expression which will be evaluated by CEL. ref: https://github.com/google/cel-spec CEL expressions have access to the contents of the API request/response, organized into CEL variables as well as some other useful variables:\n\n- 'object' - The object from the incoming request. The value is null for DELETE requests. - 'oldObject' - The existing object. The value is null for CREATE requests. - 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)). - 'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind. - 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.\n See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n- 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the\n request resource.\n\nThe `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` are always accessible from the root of the object. No other metadata properties are accessible.\n\nOnly property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible. Accessible property names are escaped according to the following rules when accessed in the expression: - '__' escapes to '__underscores__' - '.' escapes to '__dot__' - '-' escapes to '__dash__' - '/' escapes to '__slash__' - Property names that exactly match a CEL RESERVED keyword escape to '__{keyword}__'. The keywords are:\n\t \"true\", \"false\", \"null\", \"in\", \"as\", \"break\", \"const\", \"continue\", \"else\", \"for\", \"function\", \"if\",\n\t \"import\", \"let\", \"loop\", \"package\", \"namespace\", \"return\".\nExamples:\n - Expression accessing a property named \"namespace\": {\"Expression\": \"object.__namespace__ > 0\"}\n - Expression accessing a property named \"x-prop\": {\"Expression\": \"object.x__dash__prop > 0\"}\n - Expression accessing a property named \"redact__d\": {\"Expression\": \"object.redact__underscores__d > 0\"}\n\nEquality on arrays with list type of 'set' or 'map' ignores element order, i.e. [1, 2] == [2, 1]. Concatenation on arrays with x-kubernetes-list-type use the semantics of the list type:\n - 'set': `X + Y` performs a union where the array positions of all elements in `X` are preserved and\n non-intersecting elements in `Y` are appended, retaining their partial order.\n - 'map': `X + Y` performs a merge where the array positions of all keys in `X` are preserved but the values\n are overwritten by values in `Y` when the key sets of `X` and `Y` intersect. Elements in `Y` with\n non-intersecting keys are appended, retaining their partial order.\nRequired.",
+ Description: "Expression represents the expression which will be evaluated by CEL. ref: https://github.com/google/cel-spec CEL expressions have access to the contents of the API request/response, organized into CEL variables as well as some other useful variables:\n\n- 'object' - The object from the incoming request. The value is null for DELETE requests. - 'oldObject' - The existing object. The value is null for CREATE requests. - 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)). - 'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind. - 'variables' - Map of composited variables, from its name to its lazily evaluated value.\n For example, a variable named 'foo' can be accessed as 'variables.foo'.\n- 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.\n See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n- 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the\n request resource.\n\nThe `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` are always accessible from the root of the object. No other metadata properties are accessible.\n\nOnly property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible. Accessible property names are escaped according to the following rules when accessed in the expression: - '__' escapes to '__underscores__' - '.' escapes to '__dot__' - '-' escapes to '__dash__' - '/' escapes to '__slash__' - Property names that exactly match a CEL RESERVED keyword escape to '__{keyword}__'. The keywords are:\n\t \"true\", \"false\", \"null\", \"in\", \"as\", \"break\", \"const\", \"continue\", \"else\", \"for\", \"function\", \"if\",\n\t \"import\", \"let\", \"loop\", \"package\", \"namespace\", \"return\".\nExamples:\n - Expression accessing a property named \"namespace\": {\"Expression\": \"object.__namespace__ > 0\"}\n - Expression accessing a property named \"x-prop\": {\"Expression\": \"object.x__dash__prop > 0\"}\n - Expression accessing a property named \"redact__d\": {\"Expression\": \"object.redact__underscores__d > 0\"}\n\nEquality on arrays with list type of 'set' or 'map' ignores element order, i.e. [1, 2] == [2, 1]. Concatenation on arrays with x-kubernetes-list-type use the semantics of the list type:\n - 'set': `X + Y` performs a union where the array positions of all elements in `X` are preserved and\n non-intersecting elements in `Y` are appended, retaining their partial order.\n - 'map': `X + Y` performs a merge where the array positions of all keys in `X` are preserved but the values\n are overwritten by values in `Y` when the key sets of `X` and `Y` intersect. Elements in `Y` with\n non-intersecting keys are appended, retaining their partial order.\nRequired.",
Default: "",
Type: []string{"string"},
Format: "",
@@ -2762,6 +2787,36 @@ func schema_k8sio_api_admissionregistration_v1alpha1_Validation(ref common.Refer
}
}
+func schema_k8sio_api_admissionregistration_v1alpha1_Variable(ref common.ReferenceCallback) common.OpenAPIDefinition {
+ return common.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Description: "Variable is the definition of a variable that is used for composition.",
+ Type: []string{"object"},
+ Properties: map[string]spec.Schema{
+ "name": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Name is the name of the variable. The name must be a valid CEL identifier and unique among all variables. The variable can be accessed in other expressions through `variables` For example, if name is \"foo\", the variable will be available as `variables.foo`",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ "expression": {
+ SchemaProps: spec.SchemaProps{
+ Description: "Expression is the expression that will be evaluated as the value of the variable. The CEL expression has access to the same identifiers as the CEL expressions in Validation.",
+ Default: "",
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ },
+ Required: []string{"name", "expression"},
+ },
+ },
+ }
+}
+
func schema_k8sio_api_admissionregistration_v1beta1_MatchCondition(ref common.ReferenceCallback) common.OpenAPIDefinition {
return common.OpenAPIDefinition{
Schema: spec.Schema{
diff --git a/staging/src/k8s.io/api/admissionregistration/v1alpha1/generated.pb.go b/staging/src/k8s.io/api/admissionregistration/v1alpha1/generated.pb.go
index 7465350263b..dcdf199ed12 100644
--- a/staging/src/k8s.io/api/admissionregistration/v1alpha1/generated.pb.go
+++ b/staging/src/k8s.io/api/admissionregistration/v1alpha1/generated.pb.go
@@ -493,6 +493,34 @@ func (m *Validation) XXX_DiscardUnknown() {
var xxx_messageInfo_Validation proto.InternalMessageInfo
+func (m *Variable) Reset() { *m = Variable{} }
+func (*Variable) ProtoMessage() {}
+func (*Variable) Descriptor() ([]byte, []int) {
+ return fileDescriptor_c3be8d256e3ae3cf, []int{16}
+}
+func (m *Variable) XXX_Unmarshal(b []byte) error {
+ return m.Unmarshal(b)
+}
+func (m *Variable) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ b = b[:cap(b)]
+ n, err := m.MarshalToSizedBuffer(b)
+ if err != nil {
+ return nil, err
+ }
+ return b[:n], nil
+}
+func (m *Variable) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_Variable.Merge(m, src)
+}
+func (m *Variable) XXX_Size() int {
+ return m.Size()
+}
+func (m *Variable) XXX_DiscardUnknown() {
+ xxx_messageInfo_Variable.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Variable proto.InternalMessageInfo
+
func init() {
proto.RegisterType((*AuditAnnotation)(nil), "k8s.io.api.admissionregistration.v1alpha1.AuditAnnotation")
proto.RegisterType((*ExpressionWarning)(nil), "k8s.io.api.admissionregistration.v1alpha1.ExpressionWarning")
@@ -510,6 +538,7 @@ func init() {
proto.RegisterType((*ValidatingAdmissionPolicySpec)(nil), "k8s.io.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicySpec")
proto.RegisterType((*ValidatingAdmissionPolicyStatus)(nil), "k8s.io.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyStatus")
proto.RegisterType((*Validation)(nil), "k8s.io.api.admissionregistration.v1alpha1.Validation")
+ proto.RegisterType((*Variable)(nil), "k8s.io.api.admissionregistration.v1alpha1.Variable")
}
func init() {
@@ -517,95 +546,99 @@ func init() {
}
var fileDescriptor_c3be8d256e3ae3cf = []byte{
- // 1407 bytes of a gzipped FileDescriptorProto
- 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xcb, 0x6f, 0x1b, 0x45,
- 0x18, 0xcf, 0xc6, 0x4e, 0x9a, 0x8c, 0xf3, 0xb0, 0x87, 0x56, 0x75, 0x23, 0x6a, 0x47, 0xab, 0x0a,
- 0x35, 0x12, 0xec, 0x92, 0xb4, 0x50, 0x40, 0x48, 0x28, 0xdb, 0x17, 0x7d, 0xa4, 0x89, 0xa6, 0x28,
- 0x91, 0x10, 0x95, 0x98, 0xec, 0x4e, 0xec, 0xa9, 0xbd, 0x0f, 0x76, 0xd6, 0xa1, 0x11, 0x48, 0x54,
- 0xe2, 0x02, 0x37, 0x0e, 0x5c, 0xf8, 0x5f, 0xb8, 0x70, 0xeb, 0xb1, 0xc7, 0x72, 0xc0, 0x22, 0xe6,
- 0xc2, 0x5f, 0x00, 0x52, 0x2e, 0xa0, 0x99, 0x9d, 0x7d, 0x3b, 0xc4, 0x2e, 0x81, 0x9b, 0xf7, 0x7b,
- 0xfc, 0x7e, 0xf3, 0x7d, 0xf3, 0x7d, 0x33, 0xdf, 0x18, 0xa0, 0xce, 0x3b, 0x4c, 0xa3, 0xae, 0xde,
- 0xe9, 0xed, 0x12, 0xdf, 0x21, 0x01, 0x61, 0xfa, 0x3e, 0x71, 0x2c, 0xd7, 0xd7, 0xa5, 0x02, 0x7b,
- 0x54, 0xc7, 0x96, 0x4d, 0x19, 0xa3, 0xae, 0xe3, 0x93, 0x16, 0x65, 0x81, 0x8f, 0x03, 0xea, 0x3a,
- 0xfa, 0xfe, 0x2a, 0xee, 0x7a, 0x6d, 0xbc, 0xaa, 0xb7, 0x88, 0x43, 0x7c, 0x1c, 0x10, 0x4b, 0xf3,
- 0x7c, 0x37, 0x70, 0xe1, 0x4a, 0xe8, 0xaa, 0x61, 0x8f, 0x6a, 0x43, 0x5d, 0xb5, 0xc8, 0x75, 0xe9,
- 0x8d, 0x16, 0x0d, 0xda, 0xbd, 0x5d, 0xcd, 0x74, 0x6d, 0xbd, 0xe5, 0xb6, 0x5c, 0x5d, 0x20, 0xec,
- 0xf6, 0xf6, 0xc4, 0x97, 0xf8, 0x10, 0xbf, 0x42, 0xe4, 0xa5, 0x2b, 0x23, 0x2c, 0x2a, 0xbf, 0x9c,
- 0xa5, 0xab, 0x89, 0x93, 0x8d, 0xcd, 0x36, 0x75, 0x88, 0x7f, 0xa0, 0x7b, 0x9d, 0x16, 0x17, 0x30,
- 0xdd, 0x26, 0x01, 0x1e, 0xe6, 0xa5, 0x1f, 0xe7, 0xe5, 0xf7, 0x9c, 0x80, 0xda, 0xa4, 0xe0, 0xf0,
- 0xf6, 0x49, 0x0e, 0xcc, 0x6c, 0x13, 0x1b, 0xe7, 0xfd, 0x54, 0x06, 0x16, 0xd7, 0x7b, 0x16, 0x0d,
- 0xd6, 0x1d, 0xc7, 0x0d, 0x44, 0x10, 0xf0, 0x22, 0x28, 0x75, 0xc8, 0x41, 0x5d, 0x59, 0x56, 0x2e,
- 0xcf, 0x1a, 0x95, 0x67, 0xfd, 0xe6, 0xc4, 0xa0, 0xdf, 0x2c, 0xdd, 0x23, 0x07, 0x88, 0xcb, 0xe1,
- 0x3a, 0x58, 0xdc, 0xc7, 0xdd, 0x1e, 0xb9, 0xf9, 0xc4, 0xf3, 0x89, 0x48, 0x41, 0x7d, 0x52, 0x98,
- 0x9e, 0x97, 0xa6, 0x8b, 0xdb, 0x59, 0x35, 0xca, 0xdb, 0xab, 0x5d, 0x50, 0x4b, 0xbe, 0x76, 0xb0,
- 0xef, 0x50, 0xa7, 0x05, 0x5f, 0x07, 0x33, 0x7b, 0x94, 0x74, 0x2d, 0x44, 0xf6, 0x24, 0x60, 0x55,
- 0x02, 0xce, 0xdc, 0x92, 0x72, 0x14, 0x5b, 0xc0, 0x15, 0x70, 0xe6, 0xf3, 0xd0, 0xb1, 0x5e, 0x12,
- 0xc6, 0x8b, 0xd2, 0xf8, 0x8c, 0xc4, 0x43, 0x91, 0x5e, 0xdd, 0x03, 0x0b, 0x1b, 0x38, 0x30, 0xdb,
- 0xd7, 0x5d, 0xc7, 0xa2, 0x22, 0xc2, 0x65, 0x50, 0x76, 0xb0, 0x4d, 0x64, 0x88, 0x73, 0xd2, 0xb3,
- 0xfc, 0x00, 0xdb, 0x04, 0x09, 0x0d, 0x5c, 0x03, 0x80, 0xe4, 0xe3, 0x83, 0xd2, 0x0e, 0xa4, 0x42,
- 0x4b, 0x59, 0xa9, 0x3f, 0x97, 0x25, 0x11, 0x22, 0xcc, 0xed, 0xf9, 0x26, 0x61, 0xf0, 0x09, 0xa8,
- 0x71, 0x38, 0xe6, 0x61, 0x93, 0x3c, 0x24, 0x5d, 0x62, 0x06, 0xae, 0x2f, 0x58, 0x2b, 0x6b, 0x57,
- 0xb4, 0xa4, 0x4e, 0xe3, 0x1d, 0xd3, 0xbc, 0x4e, 0x8b, 0x0b, 0x98, 0xc6, 0x0b, 0x43, 0xdb, 0x5f,
- 0xd5, 0xee, 0xe3, 0x5d, 0xd2, 0x8d, 0x5c, 0x8d, 0x73, 0x83, 0x7e, 0xb3, 0xf6, 0x20, 0x8f, 0x88,
- 0x8a, 0x24, 0xd0, 0x05, 0x0b, 0xee, 0xee, 0x63, 0x62, 0x06, 0x31, 0xed, 0xe4, 0xcb, 0xd3, 0xc2,
- 0x41, 0xbf, 0xb9, 0xb0, 0x99, 0x81, 0x43, 0x39, 0x78, 0xf8, 0x15, 0x98, 0xf7, 0x65, 0xdc, 0xa8,
- 0xd7, 0x25, 0xac, 0x5e, 0x5a, 0x2e, 0x5d, 0xae, 0xac, 0x19, 0xda, 0xc8, 0xed, 0xa8, 0xf1, 0xc0,
- 0x2c, 0xee, 0xbc, 0x43, 0x83, 0xf6, 0xa6, 0x47, 0x42, 0x3d, 0x33, 0xce, 0xc9, 0xc4, 0xcf, 0xa3,
- 0x34, 0x01, 0xca, 0xf2, 0xc1, 0xef, 0x15, 0x70, 0x96, 0x3c, 0x31, 0xbb, 0x3d, 0x8b, 0x64, 0xec,
- 0xea, 0xe5, 0x53, 0x5b, 0xc8, 0xab, 0x72, 0x21, 0x67, 0x6f, 0x0e, 0xe1, 0x41, 0x43, 0xd9, 0xe1,
- 0x0d, 0x50, 0xb1, 0x79, 0x51, 0x6c, 0xb9, 0x5d, 0x6a, 0x1e, 0xd4, 0xcf, 0x88, 0x52, 0x52, 0x07,
- 0xfd, 0x66, 0x65, 0x23, 0x11, 0x1f, 0xf5, 0x9b, 0x8b, 0xa9, 0xcf, 0x8f, 0x0e, 0x3c, 0x82, 0xd2,
- 0x6e, 0xea, 0x0b, 0x05, 0x9c, 0x3f, 0x66, 0x55, 0xf0, 0x5a, 0x92, 0x79, 0x51, 0x1a, 0x75, 0x65,
- 0xb9, 0x74, 0x79, 0xd6, 0xa8, 0xa5, 0x33, 0x26, 0x14, 0x28, 0x6b, 0x07, 0xbf, 0x56, 0x00, 0xf4,
- 0x0b, 0x78, 0xb2, 0x50, 0xae, 0x8d, 0x92, 0x2f, 0x6d, 0x48, 0x92, 0x96, 0x64, 0x92, 0x60, 0x51,
- 0x87, 0x86, 0xd0, 0xa9, 0x18, 0xcc, 0x6e, 0x61, 0x1f, 0xdb, 0xf7, 0xa8, 0x63, 0xf1, 0xbe, 0xc3,
- 0x1e, 0xdd, 0x26, 0xbe, 0xe8, 0x3b, 0x25, 0xdb, 0x77, 0xeb, 0x5b, 0x77, 0xa4, 0x06, 0xa5, 0xac,
- 0x78, 0x37, 0x77, 0xa8, 0x63, 0xc9, 0x2e, 0x8d, 0xbb, 0x99, 0xe3, 0x21, 0xa1, 0x51, 0x1f, 0x81,
- 0x19, 0x41, 0xc1, 0x0f, 0x8e, 0x93, 0x7b, 0x5f, 0x07, 0xb3, 0x71, 0x3f, 0x49, 0xd0, 0x9a, 0x34,
- 0x9b, 0x8d, 0x7b, 0x0f, 0x25, 0x36, 0xea, 0x0f, 0x0a, 0x98, 0xe3, 0x5b, 0x76, 0xbd, 0x4d, 0xcc,
- 0x0e, 0x3f, 0xca, 0xbe, 0x51, 0x00, 0x24, 0xf9, 0x03, 0x2e, 0xdc, 0x97, 0xca, 0xda, 0xfb, 0x63,
- 0x14, 0x62, 0xe1, 0x94, 0x4c, 0xb2, 0x5b, 0x50, 0x31, 0x34, 0x84, 0x53, 0xfd, 0x65, 0x12, 0x5c,
- 0xd8, 0xc6, 0x5d, 0x6a, 0xe1, 0x80, 0x3a, 0xad, 0xf5, 0x88, 0x2e, 0x2c, 0x2b, 0xf8, 0x29, 0x98,
- 0xe1, 0x1d, 0x6f, 0xe1, 0x00, 0xcb, 0x63, 0xe9, 0xcd, 0xd1, 0xce, 0x87, 0xf0, 0x30, 0xd8, 0x20,
- 0x01, 0x4e, 0xb6, 0x27, 0x91, 0xa1, 0x18, 0x15, 0x3e, 0x06, 0x65, 0xe6, 0x11, 0x53, 0x16, 0xd5,
- 0x87, 0x63, 0xc4, 0x7e, 0xec, 0xaa, 0x1f, 0x7a, 0xc4, 0x4c, 0x36, 0x8e, 0x7f, 0x21, 0xc1, 0x01,
- 0x7d, 0x30, 0xcd, 0x02, 0x1c, 0xf4, 0x98, 0xb8, 0x12, 0x2a, 0x6b, 0x77, 0x4f, 0x85, 0x4d, 0x20,
- 0x1a, 0x0b, 0x92, 0x6f, 0x3a, 0xfc, 0x46, 0x92, 0x49, 0xfd, 0x53, 0x01, 0xcb, 0xc7, 0xfa, 0x1a,
- 0xd4, 0xb1, 0x78, 0x3d, 0xfc, 0xf7, 0x69, 0xfe, 0x2c, 0x93, 0xe6, 0xcd, 0xd3, 0x08, 0x5c, 0x2e,
- 0xfe, 0xb8, 0x6c, 0xab, 0x7f, 0x28, 0xe0, 0xd2, 0x49, 0xce, 0xf7, 0x29, 0x0b, 0xe0, 0x27, 0x85,
- 0xe8, 0xb5, 0x11, 0x2f, 0x21, 0xca, 0xc2, 0xd8, 0xe3, 0x41, 0x20, 0x92, 0xa4, 0x22, 0xf7, 0xc0,
- 0x14, 0x0d, 0x88, 0xcd, 0x8f, 0x2d, 0xde, 0x5d, 0xf7, 0x4e, 0x31, 0x74, 0x63, 0x5e, 0xf2, 0x4e,
- 0xdd, 0xe1, 0x0c, 0x28, 0x24, 0x52, 0xbf, 0x2d, 0x9d, 0x1c, 0x38, 0xcf, 0x13, 0x3f, 0xcc, 0x3c,
- 0x21, 0x7c, 0x90, 0x1c, 0x38, 0xf1, 0x36, 0x6e, 0xc5, 0x1a, 0x94, 0xb2, 0x82, 0x8f, 0xc0, 0x8c,
- 0x27, 0x8f, 0xaa, 0x21, 0x37, 0xf6, 0x49, 0x11, 0x45, 0xa7, 0x9c, 0x31, 0xc7, 0xb3, 0x15, 0x7d,
- 0xa1, 0x18, 0x12, 0xf6, 0xc0, 0x82, 0x9d, 0x19, 0x51, 0x64, 0xab, 0xbc, 0x3b, 0x06, 0x49, 0x76,
- 0xc6, 0x09, 0x87, 0x83, 0xac, 0x0c, 0xe5, 0x48, 0xe0, 0x0e, 0xa8, 0xed, 0xcb, 0x8c, 0xb9, 0xce,
- 0xba, 0x19, 0xde, 0x33, 0x65, 0x71, 0x4d, 0xad, 0xf0, 0x91, 0x66, 0x3b, 0xaf, 0x3c, 0xea, 0x37,
- 0xab, 0x79, 0x21, 0x2a, 0x62, 0xa8, 0xbf, 0x2b, 0xe0, 0xe2, 0xb1, 0x7b, 0xf1, 0x3f, 0x54, 0x1f,
- 0xcd, 0x56, 0xdf, 0x8d, 0x53, 0xa9, 0xbe, 0xe1, 0x65, 0xf7, 0xe3, 0xd4, 0x3f, 0x84, 0x2a, 0xea,
- 0x0d, 0x83, 0x59, 0x2f, 0xba, 0x49, 0x65, 0xac, 0x57, 0xc7, 0x2d, 0x1e, 0xee, 0x6b, 0xcc, 0xf3,
- 0xab, 0x2e, 0xfe, 0x44, 0x09, 0x2a, 0xfc, 0x02, 0x54, 0x6d, 0x39, 0x4b, 0x73, 0x00, 0xea, 0x04,
- 0xd1, 0xbc, 0xf0, 0x2f, 0x2a, 0xe8, 0xec, 0xa0, 0xdf, 0xac, 0x6e, 0xe4, 0x60, 0x51, 0x81, 0x08,
- 0x76, 0x41, 0x25, 0xa9, 0x80, 0x68, 0xc0, 0x7c, 0xeb, 0x25, 0x52, 0xee, 0x3a, 0xc6, 0x2b, 0x32,
- 0xc7, 0x95, 0x44, 0xc6, 0x50, 0x1a, 0x1e, 0xde, 0x07, 0xf3, 0x7b, 0x98, 0x76, 0x7b, 0x3e, 0x91,
- 0xa3, 0x5b, 0x59, 0x34, 0xf0, 0x6b, 0x7c, 0xac, 0xba, 0x95, 0x56, 0x1c, 0xf5, 0x9b, 0xb5, 0x8c,
- 0x40, 0x8c, 0x6f, 0x59, 0x67, 0xf8, 0x54, 0x01, 0x55, 0x9c, 0x7d, 0x68, 0xb1, 0xfa, 0x94, 0x88,
- 0xe0, 0xbd, 0x31, 0x22, 0xc8, 0xbd, 0xd5, 0x8c, 0xba, 0x0c, 0xa3, 0x9a, 0x53, 0x30, 0x54, 0x60,
- 0x83, 0x5f, 0x82, 0x45, 0x3b, 0xf3, 0x0e, 0x62, 0xf5, 0x69, 0xb1, 0x80, 0xb1, 0xb7, 0x2e, 0x46,
- 0x48, 0xde, 0x7c, 0x59, 0x39, 0x43, 0x79, 0x2a, 0xf5, 0xa7, 0x49, 0xd0, 0x3c, 0xe1, 0x92, 0x85,
- 0x77, 0x01, 0x74, 0x77, 0x19, 0xf1, 0xf7, 0x89, 0x75, 0x3b, 0x7c, 0xa7, 0x46, 0x53, 0x60, 0x29,
- 0x19, 0x7c, 0x36, 0x0b, 0x16, 0x68, 0x88, 0x17, 0xb4, 0xc1, 0x5c, 0x90, 0x9a, 0xc9, 0xc6, 0x99,
- 0x6a, 0x65, 0xa8, 0xe9, 0x91, 0xce, 0xa8, 0x0e, 0xfa, 0xcd, 0xcc, 0x90, 0x87, 0x32, 0xf0, 0xd0,
- 0x04, 0xc0, 0x4c, 0xf2, 0x1a, 0x96, 0xa6, 0x3e, 0xda, 0x41, 0x93, 0x64, 0x33, 0xbe, 0x1c, 0x52,
- 0x89, 0x4c, 0xc1, 0xaa, 0x7f, 0x29, 0x00, 0x24, 0xf5, 0x0a, 0x2f, 0x81, 0xd4, 0x53, 0x54, 0xde,
- 0x2f, 0x65, 0x0e, 0x81, 0x52, 0x72, 0xfe, 0x52, 0xb6, 0x09, 0x63, 0xb8, 0x15, 0x0d, 0xb3, 0xf1,
- 0x4b, 0x79, 0x23, 0x14, 0xa3, 0x48, 0x0f, 0x77, 0xc0, 0xb4, 0x4f, 0x30, 0x73, 0x1d, 0xf9, 0xa6,
- 0xfe, 0x80, 0x0f, 0x3c, 0x48, 0x48, 0x8e, 0xfa, 0xcd, 0xd5, 0x51, 0xfe, 0xc9, 0xd0, 0xe4, 0x7c,
- 0x24, 0x9c, 0x90, 0x84, 0x83, 0xb7, 0x41, 0x4d, 0x72, 0xa4, 0x16, 0x1c, 0xf6, 0xd3, 0x05, 0xb9,
- 0x9a, 0xda, 0x46, 0xde, 0x00, 0x15, 0x7d, 0x8c, 0xcd, 0x67, 0x87, 0x8d, 0x89, 0xe7, 0x87, 0x8d,
- 0x89, 0x17, 0x87, 0x8d, 0x89, 0xa7, 0x83, 0x86, 0xf2, 0x6c, 0xd0, 0x50, 0x9e, 0x0f, 0x1a, 0xca,
- 0x8b, 0x41, 0x43, 0xf9, 0x75, 0xd0, 0x50, 0xbe, 0xfb, 0xad, 0x31, 0xf1, 0xf1, 0xca, 0xc8, 0xff,
- 0x1e, 0xfd, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x08, 0xaf, 0xaa, 0x52, 0x82, 0x12, 0x00, 0x00,
+ // 1457 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xcd, 0x6f, 0x1b, 0x45,
+ 0x1b, 0xcf, 0xc6, 0x6e, 0x12, 0x8f, 0xf3, 0x61, 0xcf, 0xdb, 0xaa, 0x6e, 0xf4, 0xd6, 0x8e, 0x56,
+ 0xd5, 0xab, 0x46, 0x7a, 0xd9, 0x25, 0x69, 0xa1, 0x80, 0x90, 0x50, 0xb6, 0x5f, 0xf4, 0x23, 0x4d,
+ 0x34, 0x45, 0x89, 0x84, 0xa8, 0xc4, 0x78, 0x77, 0x62, 0x4f, 0xed, 0xfd, 0x60, 0x67, 0x6d, 0x1a,
+ 0x81, 0x44, 0x25, 0x2e, 0x70, 0xe3, 0xc0, 0x85, 0x2b, 0xff, 0x09, 0xb7, 0x1e, 0x7b, 0x2c, 0x07,
+ 0x2c, 0x6a, 0x2e, 0xfc, 0x05, 0x20, 0xe5, 0x02, 0x9a, 0xd9, 0xd9, 0x4f, 0x3b, 0xc4, 0x2e, 0x81,
+ 0x9b, 0xf7, 0xf9, 0xf8, 0xfd, 0xe6, 0x79, 0xe6, 0x79, 0x66, 0x9e, 0x31, 0x40, 0x9d, 0xb7, 0x98,
+ 0x46, 0x5d, 0xbd, 0xd3, 0x6b, 0x12, 0xdf, 0x21, 0x01, 0x61, 0x7a, 0x9f, 0x38, 0x96, 0xeb, 0xeb,
+ 0x52, 0x81, 0x3d, 0xaa, 0x63, 0xcb, 0xa6, 0x8c, 0x51, 0xd7, 0xf1, 0x49, 0x8b, 0xb2, 0xc0, 0xc7,
+ 0x01, 0x75, 0x1d, 0xbd, 0xbf, 0x81, 0xbb, 0x5e, 0x1b, 0x6f, 0xe8, 0x2d, 0xe2, 0x10, 0x1f, 0x07,
+ 0xc4, 0xd2, 0x3c, 0xdf, 0x0d, 0x5c, 0xb8, 0x1e, 0xba, 0x6a, 0xd8, 0xa3, 0xda, 0x58, 0x57, 0x2d,
+ 0x72, 0x5d, 0x7d, 0xad, 0x45, 0x83, 0x76, 0xaf, 0xa9, 0x99, 0xae, 0xad, 0xb7, 0xdc, 0x96, 0xab,
+ 0x0b, 0x84, 0x66, 0xef, 0x40, 0x7c, 0x89, 0x0f, 0xf1, 0x2b, 0x44, 0x5e, 0xbd, 0x32, 0xc1, 0xa2,
+ 0xf2, 0xcb, 0x59, 0xbd, 0x9a, 0x38, 0xd9, 0xd8, 0x6c, 0x53, 0x87, 0xf8, 0x87, 0xba, 0xd7, 0x69,
+ 0x71, 0x01, 0xd3, 0x6d, 0x12, 0xe0, 0x71, 0x5e, 0xfa, 0x71, 0x5e, 0x7e, 0xcf, 0x09, 0xa8, 0x4d,
+ 0x46, 0x1c, 0xde, 0x3c, 0xc9, 0x81, 0x99, 0x6d, 0x62, 0xe3, 0xbc, 0x9f, 0xca, 0xc0, 0xca, 0x56,
+ 0xcf, 0xa2, 0xc1, 0x96, 0xe3, 0xb8, 0x81, 0x08, 0x02, 0x5e, 0x04, 0x85, 0x0e, 0x39, 0xac, 0x29,
+ 0x6b, 0xca, 0xe5, 0x92, 0x51, 0x7e, 0x36, 0x68, 0xcc, 0x0c, 0x07, 0x8d, 0xc2, 0x3d, 0x72, 0x88,
+ 0xb8, 0x1c, 0x6e, 0x81, 0x95, 0x3e, 0xee, 0xf6, 0xc8, 0xcd, 0x27, 0x9e, 0x4f, 0x44, 0x0a, 0x6a,
+ 0xb3, 0xc2, 0xf4, 0xbc, 0x34, 0x5d, 0xd9, 0xcb, 0xaa, 0x51, 0xde, 0x5e, 0xed, 0x82, 0x6a, 0xf2,
+ 0xb5, 0x8f, 0x7d, 0x87, 0x3a, 0x2d, 0xf8, 0x7f, 0xb0, 0x70, 0x40, 0x49, 0xd7, 0x42, 0xe4, 0x40,
+ 0x02, 0x56, 0x24, 0xe0, 0xc2, 0x2d, 0x29, 0x47, 0xb1, 0x05, 0x5c, 0x07, 0xf3, 0x9f, 0x86, 0x8e,
+ 0xb5, 0x82, 0x30, 0x5e, 0x91, 0xc6, 0xf3, 0x12, 0x0f, 0x45, 0x7a, 0xf5, 0x00, 0x2c, 0x6f, 0xe3,
+ 0xc0, 0x6c, 0x5f, 0x77, 0x1d, 0x8b, 0x8a, 0x08, 0xd7, 0x40, 0xd1, 0xc1, 0x36, 0x91, 0x21, 0x2e,
+ 0x4a, 0xcf, 0xe2, 0x03, 0x6c, 0x13, 0x24, 0x34, 0x70, 0x13, 0x00, 0x92, 0x8f, 0x0f, 0x4a, 0x3b,
+ 0x90, 0x0a, 0x2d, 0x65, 0xa5, 0xfe, 0x58, 0x94, 0x44, 0x88, 0x30, 0xb7, 0xe7, 0x9b, 0x84, 0xc1,
+ 0x27, 0xa0, 0xca, 0xe1, 0x98, 0x87, 0x4d, 0xf2, 0x90, 0x74, 0x89, 0x19, 0xb8, 0xbe, 0x60, 0x2d,
+ 0x6f, 0x5e, 0xd1, 0x92, 0x3a, 0x8d, 0x77, 0x4c, 0xf3, 0x3a, 0x2d, 0x2e, 0x60, 0x1a, 0x2f, 0x0c,
+ 0xad, 0xbf, 0xa1, 0xdd, 0xc7, 0x4d, 0xd2, 0x8d, 0x5c, 0x8d, 0x73, 0xc3, 0x41, 0xa3, 0xfa, 0x20,
+ 0x8f, 0x88, 0x46, 0x49, 0xa0, 0x0b, 0x96, 0xdd, 0xe6, 0x63, 0x62, 0x06, 0x31, 0xed, 0xec, 0xab,
+ 0xd3, 0xc2, 0xe1, 0xa0, 0xb1, 0xbc, 0x93, 0x81, 0x43, 0x39, 0x78, 0xf8, 0x05, 0x58, 0xf2, 0x65,
+ 0xdc, 0xa8, 0xd7, 0x25, 0xac, 0x56, 0x58, 0x2b, 0x5c, 0x2e, 0x6f, 0x1a, 0xda, 0xc4, 0xed, 0xa8,
+ 0xf1, 0xc0, 0x2c, 0xee, 0xbc, 0x4f, 0x83, 0xf6, 0x8e, 0x47, 0x42, 0x3d, 0x33, 0xce, 0xc9, 0xc4,
+ 0x2f, 0xa1, 0x34, 0x01, 0xca, 0xf2, 0xc1, 0x6f, 0x15, 0x70, 0x96, 0x3c, 0x31, 0xbb, 0x3d, 0x8b,
+ 0x64, 0xec, 0x6a, 0xc5, 0x53, 0x5b, 0xc8, 0x7f, 0xe5, 0x42, 0xce, 0xde, 0x1c, 0xc3, 0x83, 0xc6,
+ 0xb2, 0xc3, 0x1b, 0xa0, 0x6c, 0xf3, 0xa2, 0xd8, 0x75, 0xbb, 0xd4, 0x3c, 0xac, 0xcd, 0x8b, 0x52,
+ 0x52, 0x87, 0x83, 0x46, 0x79, 0x3b, 0x11, 0x1f, 0x0d, 0x1a, 0x2b, 0xa9, 0xcf, 0x0f, 0x0e, 0x3d,
+ 0x82, 0xd2, 0x6e, 0xea, 0x0b, 0x05, 0x9c, 0x3f, 0x66, 0x55, 0xf0, 0x5a, 0x92, 0x79, 0x51, 0x1a,
+ 0x35, 0x65, 0xad, 0x70, 0xb9, 0x64, 0x54, 0xd3, 0x19, 0x13, 0x0a, 0x94, 0xb5, 0x83, 0x5f, 0x2a,
+ 0x00, 0xfa, 0x23, 0x78, 0xb2, 0x50, 0xae, 0x4d, 0x92, 0x2f, 0x6d, 0x4c, 0x92, 0x56, 0x65, 0x92,
+ 0xe0, 0xa8, 0x0e, 0x8d, 0xa1, 0x53, 0x31, 0x28, 0xed, 0x62, 0x1f, 0xdb, 0xf7, 0xa8, 0x63, 0xf1,
+ 0xbe, 0xc3, 0x1e, 0xdd, 0x23, 0xbe, 0xe8, 0x3b, 0x25, 0xdb, 0x77, 0x5b, 0xbb, 0x77, 0xa4, 0x06,
+ 0xa5, 0xac, 0x78, 0x37, 0x77, 0xa8, 0x63, 0xc9, 0x2e, 0x8d, 0xbb, 0x99, 0xe3, 0x21, 0xa1, 0x51,
+ 0x1f, 0x81, 0x05, 0x41, 0xc1, 0x0f, 0x8e, 0x93, 0x7b, 0x5f, 0x07, 0xa5, 0xb8, 0x9f, 0x24, 0x68,
+ 0x55, 0x9a, 0x95, 0xe2, 0xde, 0x43, 0x89, 0x8d, 0xfa, 0x9d, 0x02, 0x16, 0xf9, 0x96, 0x5d, 0x6f,
+ 0x13, 0xb3, 0xc3, 0x8f, 0xb2, 0xaf, 0x14, 0x00, 0x49, 0xfe, 0x80, 0x0b, 0xf7, 0xa5, 0xbc, 0xf9,
+ 0xee, 0x14, 0x85, 0x38, 0x72, 0x4a, 0x26, 0xd9, 0x1d, 0x51, 0x31, 0x34, 0x86, 0x53, 0xfd, 0x69,
+ 0x16, 0x5c, 0xd8, 0xc3, 0x5d, 0x6a, 0xe1, 0x80, 0x3a, 0xad, 0xad, 0x88, 0x2e, 0x2c, 0x2b, 0xf8,
+ 0x31, 0x58, 0xe0, 0x1d, 0x6f, 0xe1, 0x00, 0xcb, 0x63, 0xe9, 0xf5, 0xc9, 0xce, 0x87, 0xf0, 0x30,
+ 0xd8, 0x26, 0x01, 0x4e, 0xb6, 0x27, 0x91, 0xa1, 0x18, 0x15, 0x3e, 0x06, 0x45, 0xe6, 0x11, 0x53,
+ 0x16, 0xd5, 0xfb, 0x53, 0xc4, 0x7e, 0xec, 0xaa, 0x1f, 0x7a, 0xc4, 0x4c, 0x36, 0x8e, 0x7f, 0x21,
+ 0xc1, 0x01, 0x7d, 0x30, 0xc7, 0x02, 0x1c, 0xf4, 0x98, 0xb8, 0x12, 0xca, 0x9b, 0x77, 0x4f, 0x85,
+ 0x4d, 0x20, 0x1a, 0xcb, 0x92, 0x6f, 0x2e, 0xfc, 0x46, 0x92, 0x49, 0xfd, 0x5d, 0x01, 0x6b, 0xc7,
+ 0xfa, 0x1a, 0xd4, 0xb1, 0x78, 0x3d, 0xfc, 0xf3, 0x69, 0xfe, 0x24, 0x93, 0xe6, 0x9d, 0xd3, 0x08,
+ 0x5c, 0x2e, 0xfe, 0xb8, 0x6c, 0xab, 0xbf, 0x29, 0xe0, 0xd2, 0x49, 0xce, 0xf7, 0x29, 0x0b, 0xe0,
+ 0x47, 0x23, 0xd1, 0x6b, 0x13, 0x5e, 0x42, 0x94, 0x85, 0xb1, 0xc7, 0x83, 0x40, 0x24, 0x49, 0x45,
+ 0xee, 0x81, 0x33, 0x34, 0x20, 0x36, 0x3f, 0xb6, 0x78, 0x77, 0xdd, 0x3b, 0xc5, 0xd0, 0x8d, 0x25,
+ 0xc9, 0x7b, 0xe6, 0x0e, 0x67, 0x40, 0x21, 0x91, 0xfa, 0x75, 0xe1, 0xe4, 0xc0, 0x79, 0x9e, 0xf8,
+ 0x61, 0xe6, 0x09, 0xe1, 0x83, 0xe4, 0xc0, 0x89, 0xb7, 0x71, 0x37, 0xd6, 0xa0, 0x94, 0x15, 0x7c,
+ 0x04, 0x16, 0x3c, 0x79, 0x54, 0x8d, 0xb9, 0xb1, 0x4f, 0x8a, 0x28, 0x3a, 0xe5, 0x8c, 0x45, 0x9e,
+ 0xad, 0xe8, 0x0b, 0xc5, 0x90, 0xb0, 0x07, 0x96, 0xed, 0xcc, 0x88, 0x22, 0x5b, 0xe5, 0xed, 0x29,
+ 0x48, 0xb2, 0x33, 0x4e, 0x38, 0x1c, 0x64, 0x65, 0x28, 0x47, 0x02, 0xf7, 0x41, 0xb5, 0x2f, 0x33,
+ 0xe6, 0x3a, 0x5b, 0x66, 0x78, 0xcf, 0x14, 0xc5, 0x35, 0xb5, 0xce, 0x47, 0x9a, 0xbd, 0xbc, 0xf2,
+ 0x68, 0xd0, 0xa8, 0xe4, 0x85, 0x68, 0x14, 0x43, 0xfd, 0x55, 0x01, 0x17, 0x8f, 0xdd, 0x8b, 0x7f,
+ 0xa1, 0xfa, 0x68, 0xb6, 0xfa, 0x6e, 0x9c, 0x4a, 0xf5, 0x8d, 0x2f, 0xbb, 0xef, 0xe7, 0xfe, 0x22,
+ 0x54, 0x51, 0x6f, 0x18, 0x94, 0xbc, 0xe8, 0x26, 0x95, 0xb1, 0x5e, 0x9d, 0xb6, 0x78, 0xb8, 0xaf,
+ 0xb1, 0xc4, 0xaf, 0xba, 0xf8, 0x13, 0x25, 0xa8, 0xf0, 0x33, 0x50, 0xb1, 0xe5, 0x2c, 0xcd, 0x01,
+ 0xa8, 0x13, 0x44, 0xf3, 0xc2, 0xdf, 0xa8, 0xa0, 0xb3, 0xc3, 0x41, 0xa3, 0xb2, 0x9d, 0x83, 0x45,
+ 0x23, 0x44, 0xb0, 0x0b, 0xca, 0x49, 0x05, 0x44, 0x03, 0xe6, 0x1b, 0xaf, 0x90, 0x72, 0xd7, 0x31,
+ 0xfe, 0x23, 0x73, 0x5c, 0x4e, 0x64, 0x0c, 0xa5, 0xe1, 0xe1, 0x7d, 0xb0, 0x74, 0x80, 0x69, 0xb7,
+ 0xe7, 0x13, 0x39, 0xba, 0x15, 0x45, 0x03, 0xff, 0x8f, 0x8f, 0x55, 0xb7, 0xd2, 0x8a, 0xa3, 0x41,
+ 0xa3, 0x9a, 0x11, 0x88, 0xf1, 0x2d, 0xeb, 0x0c, 0x9f, 0x2a, 0xa0, 0x82, 0xb3, 0x0f, 0x2d, 0x56,
+ 0x3b, 0x23, 0x22, 0x78, 0x67, 0x8a, 0x08, 0x72, 0x6f, 0x35, 0xa3, 0x26, 0xc3, 0xa8, 0xe4, 0x14,
+ 0x0c, 0x8d, 0xb0, 0xc1, 0xcf, 0xc1, 0x8a, 0x9d, 0x79, 0x07, 0xb1, 0xda, 0x9c, 0x58, 0xc0, 0xd4,
+ 0x5b, 0x17, 0x23, 0x24, 0x6f, 0xbe, 0xac, 0x9c, 0xa1, 0x3c, 0x15, 0xb4, 0x40, 0xa9, 0x8f, 0x7d,
+ 0x8a, 0x9b, 0x7c, 0x24, 0x9f, 0x17, 0xbc, 0x57, 0xa6, 0xda, 0xba, 0xd0, 0x37, 0x19, 0xc5, 0x22,
+ 0x09, 0x43, 0x09, 0xb0, 0xfa, 0xc3, 0x2c, 0x68, 0x9c, 0x70, 0x95, 0xc3, 0xbb, 0x00, 0xba, 0x4d,
+ 0x46, 0xfc, 0x3e, 0xb1, 0x6e, 0x87, 0xaf, 0xe1, 0x68, 0xd6, 0x2c, 0x24, 0xe3, 0xd5, 0xce, 0x88,
+ 0x05, 0x1a, 0xe3, 0x05, 0x6d, 0xb0, 0x18, 0xa4, 0x26, 0xbf, 0x69, 0x66, 0x67, 0x19, 0x58, 0x7a,
+ 0x70, 0x34, 0x2a, 0xc3, 0x41, 0x23, 0x33, 0x4a, 0xa2, 0x0c, 0x3c, 0x34, 0x01, 0x30, 0x93, 0xdd,
+ 0x0b, 0x1b, 0x40, 0x9f, 0xec, 0x38, 0x4b, 0xf6, 0x2c, 0xbe, 0x82, 0x52, 0xdb, 0x95, 0x82, 0x55,
+ 0xff, 0x50, 0x00, 0x48, 0xba, 0x02, 0x5e, 0x02, 0xa9, 0x07, 0xaf, 0xbc, 0xc5, 0x8a, 0x1c, 0x02,
+ 0xa5, 0xe4, 0xfc, 0x3d, 0x6e, 0x13, 0xc6, 0x70, 0x2b, 0x1a, 0x99, 0xe3, 0xf7, 0xf8, 0x76, 0x28,
+ 0x46, 0x91, 0x1e, 0xee, 0x83, 0x39, 0x9f, 0x60, 0xe6, 0x3a, 0xf2, 0xe5, 0xfe, 0x1e, 0x1f, 0xab,
+ 0x90, 0x90, 0x1c, 0x0d, 0x1a, 0x1b, 0x93, 0xfc, 0x5f, 0xa2, 0xc9, 0x29, 0x4c, 0x38, 0x21, 0x09,
+ 0x07, 0x6f, 0x83, 0xaa, 0xe4, 0x48, 0x2d, 0x38, 0xec, 0xda, 0x0b, 0x72, 0x35, 0xd5, 0xed, 0xbc,
+ 0x01, 0x1a, 0xf5, 0x51, 0xef, 0x82, 0x85, 0xa8, 0xba, 0x60, 0x0d, 0x14, 0x53, 0xd7, 0x77, 0x18,
+ 0xb8, 0x90, 0xe4, 0x12, 0x33, 0x3b, 0x3e, 0x31, 0xc6, 0xce, 0xb3, 0x97, 0xf5, 0x99, 0xe7, 0x2f,
+ 0xeb, 0x33, 0x2f, 0x5e, 0xd6, 0x67, 0x9e, 0x0e, 0xeb, 0xca, 0xb3, 0x61, 0x5d, 0x79, 0x3e, 0xac,
+ 0x2b, 0x2f, 0x86, 0x75, 0xe5, 0xe7, 0x61, 0x5d, 0xf9, 0xe6, 0x97, 0xfa, 0xcc, 0x87, 0xeb, 0x13,
+ 0xff, 0xdf, 0xf5, 0x67, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb2, 0xbb, 0x16, 0x34, 0x34, 0x13, 0x00,
+ 0x00,
}
func (m *AuditAnnotation) Marshal() (dAtA []byte, err error) {
@@ -1205,6 +1238,20 @@ func (m *ValidatingAdmissionPolicySpec) MarshalToSizedBuffer(dAtA []byte) (int,
_ = i
var l int
_ = l
+ if len(m.Variables) > 0 {
+ for iNdEx := len(m.Variables) - 1; iNdEx >= 0; iNdEx-- {
+ {
+ size, err := m.Variables[iNdEx].MarshalToSizedBuffer(dAtA[:i])
+ if err != nil {
+ return 0, err
+ }
+ i -= size
+ i = encodeVarintGenerated(dAtA, i, uint64(size))
+ }
+ i--
+ dAtA[i] = 0x3a
+ }
+ }
if len(m.MatchConditions) > 0 {
for iNdEx := len(m.MatchConditions) - 1; iNdEx >= 0; iNdEx-- {
{
@@ -1378,6 +1425,39 @@ func (m *Validation) MarshalToSizedBuffer(dAtA []byte) (int, error) {
return len(dAtA) - i, nil
}
+func (m *Variable) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalToSizedBuffer(dAtA[:size])
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *Variable) MarshalTo(dAtA []byte) (int, error) {
+ size := m.Size()
+ return m.MarshalToSizedBuffer(dAtA[:size])
+}
+
+func (m *Variable) MarshalToSizedBuffer(dAtA []byte) (int, error) {
+ i := len(dAtA)
+ _ = i
+ var l int
+ _ = l
+ i -= len(m.Expression)
+ copy(dAtA[i:], m.Expression)
+ i = encodeVarintGenerated(dAtA, i, uint64(len(m.Expression)))
+ i--
+ dAtA[i] = 0x12
+ i -= len(m.Name)
+ copy(dAtA[i:], m.Name)
+ i = encodeVarintGenerated(dAtA, i, uint64(len(m.Name)))
+ i--
+ dAtA[i] = 0xa
+ return len(dAtA) - i, nil
+}
+
func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int {
offset -= sovGenerated(v)
base := offset
@@ -1642,6 +1722,12 @@ func (m *ValidatingAdmissionPolicySpec) Size() (n int) {
n += 1 + l + sovGenerated(uint64(l))
}
}
+ if len(m.Variables) > 0 {
+ for _, e := range m.Variables {
+ l = e.Size()
+ n += 1 + l + sovGenerated(uint64(l))
+ }
+ }
return n
}
@@ -1684,6 +1770,19 @@ func (m *Validation) Size() (n int) {
return n
}
+func (m *Variable) Size() (n int) {
+ if m == nil {
+ return 0
+ }
+ var l int
+ _ = l
+ l = len(m.Name)
+ n += 1 + l + sovGenerated(uint64(l))
+ l = len(m.Expression)
+ n += 1 + l + sovGenerated(uint64(l))
+ return n
+}
+
func sovGenerated(x uint64) (n int) {
return (math_bits.Len64(x|1) + 6) / 7
}
@@ -1882,6 +1981,11 @@ func (this *ValidatingAdmissionPolicySpec) String() string {
repeatedStringForMatchConditions += strings.Replace(strings.Replace(f.String(), "MatchCondition", "MatchCondition", 1), `&`, ``, 1) + ","
}
repeatedStringForMatchConditions += "}"
+ repeatedStringForVariables := "[]Variable{"
+ for _, f := range this.Variables {
+ repeatedStringForVariables += strings.Replace(strings.Replace(f.String(), "Variable", "Variable", 1), `&`, ``, 1) + ","
+ }
+ repeatedStringForVariables += "}"
s := strings.Join([]string{`&ValidatingAdmissionPolicySpec{`,
`ParamKind:` + strings.Replace(this.ParamKind.String(), "ParamKind", "ParamKind", 1) + `,`,
`MatchConstraints:` + strings.Replace(this.MatchConstraints.String(), "MatchResources", "MatchResources", 1) + `,`,
@@ -1889,6 +1993,7 @@ func (this *ValidatingAdmissionPolicySpec) String() string {
`FailurePolicy:` + valueToStringGenerated(this.FailurePolicy) + `,`,
`AuditAnnotations:` + repeatedStringForAuditAnnotations + `,`,
`MatchConditions:` + repeatedStringForMatchConditions + `,`,
+ `Variables:` + repeatedStringForVariables + `,`,
`}`,
}, "")
return s
@@ -1923,6 +2028,17 @@ func (this *Validation) String() string {
}, "")
return s
}
+func (this *Variable) String() string {
+ if this == nil {
+ return "nil"
+ }
+ s := strings.Join([]string{`&Variable{`,
+ `Name:` + fmt.Sprintf("%v", this.Name) + `,`,
+ `Expression:` + fmt.Sprintf("%v", this.Expression) + `,`,
+ `}`,
+ }, "")
+ return s
+}
func valueToStringGenerated(v interface{}) string {
rv := reflect.ValueOf(v)
if rv.IsNil() {
@@ -3844,6 +3960,40 @@ func (m *ValidatingAdmissionPolicySpec) Unmarshal(dAtA []byte) error {
return err
}
iNdEx = postIndex
+ case 7:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Variables", wireType)
+ }
+ var msglen int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ msglen |= int(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ if msglen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + msglen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Variables = append(m.Variables, Variable{})
+ if err := m.Variables[len(m.Variables)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
+ return err
+ }
+ iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(dAtA[iNdEx:])
@@ -4183,6 +4333,120 @@ func (m *Validation) Unmarshal(dAtA []byte) error {
}
return nil
}
+func (m *Variable) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: Variable: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: Variable: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Name = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ case 2:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Expression", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= uint64(b&0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.Expression = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipGenerated(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if (skippy < 0) || (iNdEx+skippy) < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
func skipGenerated(dAtA []byte) (n int, err error) {
l := len(dAtA)
iNdEx := 0
diff --git a/staging/src/k8s.io/api/admissionregistration/v1alpha1/generated.proto b/staging/src/k8s.io/api/admissionregistration/v1alpha1/generated.proto
index c718c5464df..ff5d2014f35 100644
--- a/staging/src/k8s.io/api/admissionregistration/v1alpha1/generated.proto
+++ b/staging/src/k8s.io/api/admissionregistration/v1alpha1/generated.proto
@@ -430,6 +430,20 @@ message ValidatingAdmissionPolicySpec {
// +listMapKey=name
// +optional
repeated MatchCondition matchConditions = 6;
+
+ // Variables contain definitions of variables that can be used in composition of other expressions.
+ // Each variable is defined as a named CEL expression.
+ // The variables defined here will be available under `variables` in other expressions of the policy
+ // except MatchConditions because MatchConditions are evaluated before the rest of the policy.
+ //
+ // The expression of a variable can refer to other variables defined earlier in the list but not those after.
+ // Thus, Variables must be sorted by the order of first appearance and acyclic.
+ // +patchMergeKey=name
+ // +patchStrategy=merge
+ // +listType=map
+ // +listMapKey=name
+ // +optional
+ repeated Variable variables = 7;
}
// ValidatingAdmissionPolicyStatus represents the status of a ValidatingAdmissionPolicy.
@@ -460,6 +474,8 @@ message Validation {
// - 'oldObject' - The existing object. The value is null for CREATE requests.
// - 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)).
// - 'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind.
+ // - 'variables' - Map of composited variables, from its name to its lazily evaluated value.
+ // For example, a variable named 'foo' can be accessed as 'variables.foo'.
// - 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.
// See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz
// - 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the
@@ -525,3 +541,15 @@ message Validation {
optional string messageExpression = 4;
}
+// Variable is the definition of a variable that is used for composition.
+message Variable {
+ // Name is the name of the variable. The name must be a valid CEL identifier and unique among all variables.
+ // The variable can be accessed in other expressions through `variables`
+ // For example, if name is "foo", the variable will be available as `variables.foo`
+ optional string Name = 1;
+
+ // Expression is the expression that will be evaluated as the value of the variable.
+ // The CEL expression has access to the same identifiers as the CEL expressions in Validation.
+ optional string Expression = 2;
+}
+
diff --git a/staging/src/k8s.io/api/admissionregistration/v1alpha1/types.go b/staging/src/k8s.io/api/admissionregistration/v1alpha1/types.go
index 2bbb55a47da..bdb81416453 100644
--- a/staging/src/k8s.io/api/admissionregistration/v1alpha1/types.go
+++ b/staging/src/k8s.io/api/admissionregistration/v1alpha1/types.go
@@ -201,6 +201,20 @@ type ValidatingAdmissionPolicySpec struct {
// +listMapKey=name
// +optional
MatchConditions []MatchCondition `json:"matchConditions,omitempty" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,6,rep,name=matchConditions"`
+
+ // Variables contain definitions of variables that can be used in composition of other expressions.
+ // Each variable is defined as a named CEL expression.
+ // The variables defined here will be available under `variables` in other expressions of the policy
+ // except MatchConditions because MatchConditions are evaluated before the rest of the policy.
+ //
+ // The expression of a variable can refer to other variables defined earlier in the list but not those after.
+ // Thus, Variables must be sorted by the order of first appearance and acyclic.
+ // +patchMergeKey=name
+ // +patchStrategy=merge
+ // +listType=map
+ // +listMapKey=name
+ // +optional
+ Variables []Variable `json:"variables" patchStrategy:"merge" patchMergeKey:"name" protobuf:"bytes,7,rep,name=variables"`
}
type MatchCondition v1.MatchCondition
@@ -228,6 +242,8 @@ type Validation struct {
// - 'oldObject' - The existing object. The value is null for CREATE requests.
// - 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)).
// - 'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind.
+ // - 'variables' - Map of composited variables, from its name to its lazily evaluated value.
+ // For example, a variable named 'foo' can be accessed as 'variables.foo'.
// - 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.
// See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz
// - 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the
@@ -290,6 +306,18 @@ type Validation struct {
MessageExpression string `json:"messageExpression,omitempty" protobuf:"bytes,4,opt,name=messageExpression"`
}
+// Variable is the definition of a variable that is used for composition.
+type Variable struct {
+ // Name is the name of the variable. The name must be a valid CEL identifier and unique among all variables.
+ // The variable can be accessed in other expressions through `variables`
+ // For example, if name is "foo", the variable will be available as `variables.foo`
+ Name string `json:"name" protobuf:"bytes,1,opt,name=Name"`
+
+ // Expression is the expression that will be evaluated as the value of the variable.
+ // The CEL expression has access to the same identifiers as the CEL expressions in Validation.
+ Expression string `json:"expression" protobuf:"bytes,2,opt,name=Expression"`
+}
+
// AuditAnnotation describes how to produce an audit annotation for an API request.
type AuditAnnotation struct {
// key specifies the audit annotation key. The audit annotation keys of
diff --git a/staging/src/k8s.io/api/admissionregistration/v1alpha1/types_swagger_doc_generated.go b/staging/src/k8s.io/api/admissionregistration/v1alpha1/types_swagger_doc_generated.go
index b3cac1821ba..c6b121e87ee 100644
--- a/staging/src/k8s.io/api/admissionregistration/v1alpha1/types_swagger_doc_generated.go
+++ b/staging/src/k8s.io/api/admissionregistration/v1alpha1/types_swagger_doc_generated.go
@@ -159,6 +159,7 @@ var map_ValidatingAdmissionPolicySpec = map[string]string{
"failurePolicy": "failurePolicy defines how to handle failures for the admission policy. Failures can occur from CEL expression parse errors, type check errors, runtime errors and invalid or mis-configured policy definitions or bindings.\n\nA policy is invalid if spec.paramKind refers to a non-existent Kind. A binding is invalid if spec.paramRef.name refers to a non-existent resource.\n\nfailurePolicy does not define how validations that evaluate to false are handled.\n\nWhen failurePolicy is set to Fail, ValidatingAdmissionPolicyBinding validationActions define how failures are enforced.\n\nAllowed values are Ignore or Fail. Defaults to Fail.",
"auditAnnotations": "auditAnnotations contains CEL expressions which are used to produce audit annotations for the audit event of the API request. validations and auditAnnotations may not both be empty; a least one of validations or auditAnnotations is required.",
"matchConditions": "MatchConditions is a list of conditions that must be met for a request to be validated. Match conditions filter requests that have already been matched by the rules, namespaceSelector, and objectSelector. An empty list of matchConditions matches all requests. There are a maximum of 64 match conditions allowed.\n\nIf a parameter object is provided, it can be accessed via the `params` handle in the same manner as validation expressions.\n\nThe exact matching logic is (in order):\n 1. If ANY matchCondition evaluates to FALSE, the policy is skipped.\n 2. If ALL matchConditions evaluate to TRUE, the policy is evaluated.\n 3. If any matchCondition evaluates to an error (but none are FALSE):\n - If failurePolicy=Fail, reject the request\n - If failurePolicy=Ignore, the policy is skipped",
+ "variables": "Variables contain definitions of variables that can be used in composition of other expressions. Each variable is defined as a named CEL expression. The variables defined here will be available under `variables` in other expressions of the policy except MatchConditions because MatchConditions are evaluated before the rest of the policy.\n\nThe expression of a variable can refer to other variables defined earlier in the list but not those after. Thus, Variables must be sorted by the order of first appearance and acyclic.",
}
func (ValidatingAdmissionPolicySpec) SwaggerDoc() map[string]string {
@@ -178,7 +179,7 @@ func (ValidatingAdmissionPolicyStatus) SwaggerDoc() map[string]string {
var map_Validation = map[string]string{
"": "Validation specifies the CEL expression which is used to apply the validation.",
- "expression": "Expression represents the expression which will be evaluated by CEL. ref: https://github.com/google/cel-spec CEL expressions have access to the contents of the API request/response, organized into CEL variables as well as some other useful variables:\n\n- 'object' - The object from the incoming request. The value is null for DELETE requests. - 'oldObject' - The existing object. The value is null for CREATE requests. - 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)). - 'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind. - 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.\n See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n- 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the\n request resource.\n\nThe `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` are always accessible from the root of the object. No other metadata properties are accessible.\n\nOnly property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible. Accessible property names are escaped according to the following rules when accessed in the expression: - '__' escapes to '__underscores__' - '.' escapes to '__dot__' - '-' escapes to '__dash__' - '/' escapes to '__slash__' - Property names that exactly match a CEL RESERVED keyword escape to '__{keyword}__'. The keywords are:\n\t \"true\", \"false\", \"null\", \"in\", \"as\", \"break\", \"const\", \"continue\", \"else\", \"for\", \"function\", \"if\",\n\t \"import\", \"let\", \"loop\", \"package\", \"namespace\", \"return\".\nExamples:\n - Expression accessing a property named \"namespace\": {\"Expression\": \"object.__namespace__ > 0\"}\n - Expression accessing a property named \"x-prop\": {\"Expression\": \"object.x__dash__prop > 0\"}\n - Expression accessing a property named \"redact__d\": {\"Expression\": \"object.redact__underscores__d > 0\"}\n\nEquality on arrays with list type of 'set' or 'map' ignores element order, i.e. [1, 2] == [2, 1]. Concatenation on arrays with x-kubernetes-list-type use the semantics of the list type:\n - 'set': `X + Y` performs a union where the array positions of all elements in `X` are preserved and\n non-intersecting elements in `Y` are appended, retaining their partial order.\n - 'map': `X + Y` performs a merge where the array positions of all keys in `X` are preserved but the values\n are overwritten by values in `Y` when the key sets of `X` and `Y` intersect. Elements in `Y` with\n non-intersecting keys are appended, retaining their partial order.\nRequired.",
+ "expression": "Expression represents the expression which will be evaluated by CEL. ref: https://github.com/google/cel-spec CEL expressions have access to the contents of the API request/response, organized into CEL variables as well as some other useful variables:\n\n- 'object' - The object from the incoming request. The value is null for DELETE requests. - 'oldObject' - The existing object. The value is null for CREATE requests. - 'request' - Attributes of the API request([ref](/pkg/apis/admission/types.go#AdmissionRequest)). - 'params' - Parameter resource referred to by the policy binding being evaluated. Only populated if the policy has a ParamKind. - 'variables' - Map of composited variables, from its name to its lazily evaluated value.\n For example, a variable named 'foo' can be accessed as 'variables.foo'.\n- 'authorizer' - A CEL Authorizer. May be used to perform authorization checks for the principal (user or service account) of the request.\n See https://pkg.go.dev/k8s.io/apiserver/pkg/cel/library#Authz\n- 'authorizer.requestResource' - A CEL ResourceCheck constructed from the 'authorizer' and configured with the\n request resource.\n\nThe `apiVersion`, `kind`, `metadata.name` and `metadata.generateName` are always accessible from the root of the object. No other metadata properties are accessible.\n\nOnly property names of the form `[a-zA-Z_.-/][a-zA-Z0-9_.-/]*` are accessible. Accessible property names are escaped according to the following rules when accessed in the expression: - '__' escapes to '__underscores__' - '.' escapes to '__dot__' - '-' escapes to '__dash__' - '/' escapes to '__slash__' - Property names that exactly match a CEL RESERVED keyword escape to '__{keyword}__'. The keywords are:\n\t \"true\", \"false\", \"null\", \"in\", \"as\", \"break\", \"const\", \"continue\", \"else\", \"for\", \"function\", \"if\",\n\t \"import\", \"let\", \"loop\", \"package\", \"namespace\", \"return\".\nExamples:\n - Expression accessing a property named \"namespace\": {\"Expression\": \"object.__namespace__ > 0\"}\n - Expression accessing a property named \"x-prop\": {\"Expression\": \"object.x__dash__prop > 0\"}\n - Expression accessing a property named \"redact__d\": {\"Expression\": \"object.redact__underscores__d > 0\"}\n\nEquality on arrays with list type of 'set' or 'map' ignores element order, i.e. [1, 2] == [2, 1]. Concatenation on arrays with x-kubernetes-list-type use the semantics of the list type:\n - 'set': `X + Y` performs a union where the array positions of all elements in `X` are preserved and\n non-intersecting elements in `Y` are appended, retaining their partial order.\n - 'map': `X + Y` performs a merge where the array positions of all keys in `X` are preserved but the values\n are overwritten by values in `Y` when the key sets of `X` and `Y` intersect. Elements in `Y` with\n non-intersecting keys are appended, retaining their partial order.\nRequired.",
"message": "Message represents the message displayed when validation fails. The message is required if the Expression contains line breaks. The message must not contain line breaks. If unset, the message is \"failed rule: {Rule}\". e.g. \"must be a URL with the host matching spec.host\" If the Expression contains line breaks. Message is required. The message must not contain line breaks. If unset, the message is \"failed Expression: {Expression}\".",
"reason": "Reason represents a machine-readable description of why this validation failed. If this is the first validation in the list to fail, this reason, as well as the corresponding HTTP response code, are used in the HTTP response to the client. The currently supported reasons are: \"Unauthorized\", \"Forbidden\", \"Invalid\", \"RequestEntityTooLarge\". If not set, StatusReasonInvalid is used in the response to the client.",
"messageExpression": "messageExpression declares a CEL expression that evaluates to the validation failure message that is returned when this rule fails. Since messageExpression is used as a failure message, it must evaluate to a string. If both message and messageExpression are present on a validation, then messageExpression will be used if validation fails. If messageExpression results in a runtime error, the runtime error is logged, and the validation failure message is produced as if the messageExpression field were unset. If messageExpression evaluates to an empty string, a string with only spaces, or a string that contains line breaks, then the validation failure message will also be produced as if the messageExpression field were unset, and the fact that messageExpression produced an empty string/string with only spaces/string with line breaks will be logged. messageExpression has access to all the same variables as the `expression` except for 'authorizer' and 'authorizer.requestResource'. Example: \"object.x must be less than max (\"+string(params.max)+\")\"",
@@ -188,4 +189,14 @@ func (Validation) SwaggerDoc() map[string]string {
return map_Validation
}
+var map_Variable = map[string]string{
+ "": "Variable is the definition of a variable that is used for composition.",
+ "name": "Name is the name of the variable. The name must be a valid CEL identifier and unique among all variables. The variable can be accessed in other expressions through `variables` For example, if name is \"foo\", the variable will be available as `variables.foo`",
+ "expression": "Expression is the expression that will be evaluated as the value of the variable. The CEL expression has access to the same identifiers as the CEL expressions in Validation.",
+}
+
+func (Variable) SwaggerDoc() map[string]string {
+ return map_Variable
+}
+
// AUTO-GENERATED FUNCTIONS END HERE
diff --git a/staging/src/k8s.io/api/admissionregistration/v1alpha1/zz_generated.deepcopy.go b/staging/src/k8s.io/api/admissionregistration/v1alpha1/zz_generated.deepcopy.go
index 8e4abfd0877..1633c8e51b2 100644
--- a/staging/src/k8s.io/api/admissionregistration/v1alpha1/zz_generated.deepcopy.go
+++ b/staging/src/k8s.io/api/admissionregistration/v1alpha1/zz_generated.deepcopy.go
@@ -381,6 +381,11 @@ func (in *ValidatingAdmissionPolicySpec) DeepCopyInto(out *ValidatingAdmissionPo
*out = make([]MatchCondition, len(*in))
copy(*out, *in)
}
+ if in.Variables != nil {
+ in, out := &in.Variables, &out.Variables
+ *out = make([]Variable, len(*in))
+ copy(*out, *in)
+ }
return
}
@@ -442,3 +447,19 @@ func (in *Validation) DeepCopy() *Validation {
in.DeepCopyInto(out)
return out
}
+
+// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
+func (in *Variable) DeepCopyInto(out *Variable) {
+ *out = *in
+ return
+}
+
+// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Variable.
+func (in *Variable) DeepCopy() *Variable {
+ if in == nil {
+ return nil
+ }
+ out := new(Variable)
+ in.DeepCopyInto(out)
+ return out
+}
diff --git a/staging/src/k8s.io/api/testdata/HEAD/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.json b/staging/src/k8s.io/api/testdata/HEAD/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.json
index e7139fc9544..d8160896548 100644
--- a/staging/src/k8s.io/api/testdata/HEAD/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.json
+++ b/staging/src/k8s.io/api/testdata/HEAD/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.json
@@ -139,6 +139,12 @@
"name": "nameValue",
"expression": "expressionValue"
}
+ ],
+ "variables": [
+ {
+ "name": "nameValue",
+ "expression": "expressionValue"
+ }
]
},
"status": {
diff --git a/staging/src/k8s.io/api/testdata/HEAD/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.pb b/staging/src/k8s.io/api/testdata/HEAD/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.pb
index 2fd9006f302..cd92e73bd39 100644
Binary files a/staging/src/k8s.io/api/testdata/HEAD/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.pb and b/staging/src/k8s.io/api/testdata/HEAD/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.pb differ
diff --git a/staging/src/k8s.io/api/testdata/HEAD/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.yaml b/staging/src/k8s.io/api/testdata/HEAD/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.yaml
index 62ef12d9010..8ca6b38bc5f 100644
--- a/staging/src/k8s.io/api/testdata/HEAD/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.yaml
+++ b/staging/src/k8s.io/api/testdata/HEAD/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.yaml
@@ -90,6 +90,9 @@ spec:
message: messageValue
messageExpression: messageExpressionValue
reason: reasonValue
+ variables:
+ - expression: expressionValue
+ name: nameValue
status:
conditions:
- lastTransitionTime: "2004-01-01T01:01:01Z"
diff --git a/staging/src/k8s.io/api/testdata/v1.26.0/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.after_roundtrip.json b/staging/src/k8s.io/api/testdata/v1.26.0/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.after_roundtrip.json
index ecbc63e1906..bb5881da205 100644
--- a/staging/src/k8s.io/api/testdata/v1.26.0/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.after_roundtrip.json
+++ b/staging/src/k8s.io/api/testdata/v1.26.0/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.after_roundtrip.json
@@ -126,7 +126,8 @@
"reason": "reasonValue"
}
],
- "failurePolicy": "failurePolicyValue"
+ "failurePolicy": "failurePolicyValue",
+ "variables": null
},
"status": {}
}
\ No newline at end of file
diff --git a/staging/src/k8s.io/api/testdata/v1.26.0/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.after_roundtrip.yaml b/staging/src/k8s.io/api/testdata/v1.26.0/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.after_roundtrip.yaml
index 2e046b95b42..b31c9ebd8b2 100644
--- a/staging/src/k8s.io/api/testdata/v1.26.0/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.after_roundtrip.yaml
+++ b/staging/src/k8s.io/api/testdata/v1.26.0/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.after_roundtrip.yaml
@@ -83,4 +83,5 @@ spec:
- expression: expressionValue
message: messageValue
reason: reasonValue
+ variables: null
status: {}
diff --git a/staging/src/k8s.io/api/testdata/v1.27.0/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.after_roundtrip.json b/staging/src/k8s.io/api/testdata/v1.27.0/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.after_roundtrip.json
new file mode 100644
index 00000000000..d61b666f317
--- /dev/null
+++ b/staging/src/k8s.io/api/testdata/v1.27.0/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.after_roundtrip.json
@@ -0,0 +1,166 @@
+{
+ "kind": "ValidatingAdmissionPolicy",
+ "apiVersion": "admissionregistration.k8s.io/v1alpha1",
+ "metadata": {
+ "name": "nameValue",
+ "generateName": "generateNameValue",
+ "namespace": "namespaceValue",
+ "selfLink": "selfLinkValue",
+ "uid": "uidValue",
+ "resourceVersion": "resourceVersionValue",
+ "generation": 7,
+ "creationTimestamp": "2008-01-01T01:01:01Z",
+ "deletionTimestamp": "2009-01-01T01:01:01Z",
+ "deletionGracePeriodSeconds": 10,
+ "labels": {
+ "labelsKey": "labelsValue"
+ },
+ "annotations": {
+ "annotationsKey": "annotationsValue"
+ },
+ "ownerReferences": [
+ {
+ "apiVersion": "apiVersionValue",
+ "kind": "kindValue",
+ "name": "nameValue",
+ "uid": "uidValue",
+ "controller": true,
+ "blockOwnerDeletion": true
+ }
+ ],
+ "finalizers": [
+ "finalizersValue"
+ ],
+ "managedFields": [
+ {
+ "manager": "managerValue",
+ "operation": "operationValue",
+ "apiVersion": "apiVersionValue",
+ "time": "2004-01-01T01:01:01Z",
+ "fieldsType": "fieldsTypeValue",
+ "fieldsV1": {},
+ "subresource": "subresourceValue"
+ }
+ ]
+ },
+ "spec": {
+ "paramKind": {
+ "apiVersion": "apiVersionValue",
+ "kind": "kindValue"
+ },
+ "matchConstraints": {
+ "namespaceSelector": {
+ "matchLabels": {
+ "matchLabelsKey": "matchLabelsValue"
+ },
+ "matchExpressions": [
+ {
+ "key": "keyValue",
+ "operator": "operatorValue",
+ "values": [
+ "valuesValue"
+ ]
+ }
+ ]
+ },
+ "objectSelector": {
+ "matchLabels": {
+ "matchLabelsKey": "matchLabelsValue"
+ },
+ "matchExpressions": [
+ {
+ "key": "keyValue",
+ "operator": "operatorValue",
+ "values": [
+ "valuesValue"
+ ]
+ }
+ ]
+ },
+ "resourceRules": [
+ {
+ "resourceNames": [
+ "resourceNamesValue"
+ ],
+ "operations": [
+ "operationsValue"
+ ],
+ "apiGroups": [
+ "apiGroupsValue"
+ ],
+ "apiVersions": [
+ "apiVersionsValue"
+ ],
+ "resources": [
+ "resourcesValue"
+ ],
+ "scope": "scopeValue"
+ }
+ ],
+ "excludeResourceRules": [
+ {
+ "resourceNames": [
+ "resourceNamesValue"
+ ],
+ "operations": [
+ "operationsValue"
+ ],
+ "apiGroups": [
+ "apiGroupsValue"
+ ],
+ "apiVersions": [
+ "apiVersionsValue"
+ ],
+ "resources": [
+ "resourcesValue"
+ ],
+ "scope": "scopeValue"
+ }
+ ],
+ "matchPolicy": "matchPolicyValue"
+ },
+ "validations": [
+ {
+ "expression": "expressionValue",
+ "message": "messageValue",
+ "reason": "reasonValue",
+ "messageExpression": "messageExpressionValue"
+ }
+ ],
+ "failurePolicy": "failurePolicyValue",
+ "auditAnnotations": [
+ {
+ "key": "keyValue",
+ "valueExpression": "valueExpressionValue"
+ }
+ ],
+ "matchConditions": [
+ {
+ "name": "nameValue",
+ "expression": "expressionValue"
+ }
+ ],
+ "variables": null
+ },
+ "status": {
+ "observedGeneration": 1,
+ "typeChecking": {
+ "expressionWarnings": [
+ {
+ "fieldRef": "fieldRefValue",
+ "warning": "warningValue"
+ }
+ ]
+ },
+ "conditions": [
+ {
+ "type": "typeValue",
+ "status": "statusValue",
+ "observedGeneration": 3,
+ "lastTransitionTime": "2004-01-01T01:01:01Z",
+ "reason": "reasonValue",
+ "message": "messageValue"
+ }
+ ]
+ }
+}
\ No newline at end of file
diff --git a/staging/src/k8s.io/api/testdata/v1.27.0/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.after_roundtrip.yaml b/staging/src/k8s.io/api/testdata/v1.27.0/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.after_roundtrip.yaml
new file mode 100644
index 00000000000..1cc7e0a8f32
--- /dev/null
+++ b/staging/src/k8s.io/api/testdata/v1.27.0/admissionregistration.k8s.io.v1alpha1.ValidatingAdmissionPolicy.after_roundtrip.yaml
@@ -0,0 +1,106 @@
+apiVersion: admissionregistration.k8s.io/v1alpha1
+kind: ValidatingAdmissionPolicy
+metadata:
+ annotations:
+ annotationsKey: annotationsValue
+ creationTimestamp: "2008-01-01T01:01:01Z"
+ deletionGracePeriodSeconds: 10
+ deletionTimestamp: "2009-01-01T01:01:01Z"
+ finalizers:
+ - finalizersValue
+ generateName: generateNameValue
+ generation: 7
+ labels:
+ labelsKey: labelsValue
+ managedFields:
+ - apiVersion: apiVersionValue
+ fieldsType: fieldsTypeValue
+ fieldsV1: {}
+ manager: managerValue
+ operation: operationValue
+ subresource: subresourceValue
+ time: "2004-01-01T01:01:01Z"
+ name: nameValue
+ namespace: namespaceValue
+ ownerReferences:
+ - apiVersion: apiVersionValue
+ blockOwnerDeletion: true
+ controller: true
+ kind: kindValue
+ name: nameValue
+ uid: uidValue
+ resourceVersion: resourceVersionValue
+ selfLink: selfLinkValue
+ uid: uidValue
+spec:
+ auditAnnotations:
+ - key: keyValue
+ valueExpression: valueExpressionValue
+ failurePolicy: failurePolicyValue
+ matchConditions:
+ - expression: expressionValue
+ name: nameValue
+ matchConstraints:
+ excludeResourceRules:
+ - apiGroups:
+ - apiGroupsValue
+ apiVersions:
+ - apiVersionsValue
+ operations:
+ - operationsValue
+ resourceNames:
+ - resourceNamesValue
+ resources:
+ - resourcesValue
+ scope: scopeValue
+ matchPolicy: matchPolicyValue
+ namespaceSelector:
+ matchExpressions:
+ - key: keyValue
+ operator: operatorValue
+ values:
+ - valuesValue
+ matchLabels:
+ matchLabelsKey: matchLabelsValue
+ objectSelector:
+ matchExpressions:
+ - key: keyValue
+ operator: operatorValue
+ values:
+ - valuesValue
+ matchLabels:
+ matchLabelsKey: matchLabelsValue
+ resourceRules:
+ - apiGroups:
+ - apiGroupsValue
+ apiVersions:
+ - apiVersionsValue
+ operations:
+ - operationsValue
+ resourceNames:
+ - resourceNamesValue
+ resources:
+ - resourcesValue
+ scope: scopeValue
+ paramKind:
+ apiVersion: apiVersionValue
+ kind: kindValue
+ validations:
+ - expression: expressionValue
+ message: messageValue
+ messageExpression: messageExpressionValue
+ reason: reasonValue
+ variables: null
+status:
+ conditions:
+ - lastTransitionTime: "2004-01-01T01:01:01Z"
+ message: messageValue
+ observedGeneration: 3
+ reason: reasonValue
+ status: statusValue
+ type: typeValue
+ observedGeneration: 1
+ typeChecking:
+ expressionWarnings:
+ - fieldRef: fieldRefValue
+ warning: warningValue
diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/compile.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/compile.go
index 745e46aa9c5..8128b95041f 100644
--- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/compile.go
+++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/compile.go
@@ -35,6 +35,7 @@ const (
RequestVarName = "request"
AuthorizerVarName = "authorizer"
RequestResourceAuthorizerVarName = "authorizer.requestResource"
+ VariableVarName = "variables"
)
// BuildRequestType generates a DeclType for AdmissionRequest. This may be replaced with a utility that
@@ -132,7 +133,7 @@ func (c compiler) CompileCELExpression(expressionAccessor ExpressionAccessor, op
found := false
returnTypes := expressionAccessor.ReturnTypes()
for _, returnType := range returnTypes {
- if ast.OutputType() == returnType {
+ if ast.OutputType() == returnType || cel.AnyType == returnType {
found = true
break
}
diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/composition.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/composition.go
new file mode 100644
index 00000000000..afdf4d36752
--- /dev/null
+++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/composition.go
@@ -0,0 +1,197 @@
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cel
+
+import (
+ "context"
+ "math"
+
+ "github.com/google/cel-go/cel"
+ "github.com/google/cel-go/common/types"
+ "github.com/google/cel-go/common/types/ref"
+
+ v1 "k8s.io/api/admission/v1"
+ "k8s.io/apimachinery/pkg/util/version"
+ "k8s.io/apiserver/pkg/admission"
+ apiservercel "k8s.io/apiserver/pkg/cel"
+ "k8s.io/apiserver/pkg/cel/environment"
+ "k8s.io/apiserver/pkg/cel/lazy"
+)
+
+const VariablesTypeName = "kubernetes.variables"
+
+type CompositedCompiler struct {
+ Compiler
+ FilterCompiler
+
+ CompositionEnv *CompositionEnv
+}
+
+type CompositedFilter struct {
+ Filter
+
+ compositionEnv *CompositionEnv
+}
+
+func NewCompositedCompiler(envSet *environment.EnvSet) (*CompositedCompiler, error) {
+ compositionContext, err := NewCompositionEnv(VariablesTypeName, envSet)
+ if err != nil {
+ return nil, err
+ }
+ compiler := NewCompiler(compositionContext.EnvSet)
+ filterCompiler := NewFilterCompiler(compositionContext.EnvSet)
+ return &CompositedCompiler{
+ Compiler: compiler,
+ FilterCompiler: filterCompiler,
+ CompositionEnv: compositionContext,
+ }, nil
+}
+
+func (c *CompositedCompiler) CompileAndStoreVariables(variables []NamedExpressionAccessor, options OptionalVariableDeclarations, mode environment.Type) {
+ for _, v := range variables {
+ _ = c.CompileAndStoreVariable(v, options, mode)
+ }
+}
+
+func (c *CompositedCompiler) CompileAndStoreVariable(variable NamedExpressionAccessor, options OptionalVariableDeclarations, mode environment.Type) CompilationResult {
+ c.CompositionEnv.AddField(variable.GetName())
+ result := c.Compiler.CompileCELExpression(variable, options, mode)
+ c.CompositionEnv.CompiledVariables[variable.GetName()] = result
+ return result
+}
+
+func (c *CompositedCompiler) Compile(expressions []ExpressionAccessor, optionalDecls OptionalVariableDeclarations, envType environment.Type) Filter {
+ filter := c.FilterCompiler.Compile(expressions, optionalDecls, envType)
+ return &CompositedFilter{
+ Filter: filter,
+ compositionEnv: c.CompositionEnv,
+ }
+}
+
+type CompositionEnv struct {
+ *environment.EnvSet
+
+ MapType *apiservercel.DeclType
+ CompiledVariables map[string]CompilationResult
+}
+
+func (c *CompositionEnv) AddField(name string) {
+ c.MapType.Fields[name] = apiservercel.NewDeclField(name, apiservercel.DynType, true, nil, nil)
+}
+
+func NewCompositionEnv(typeName string, baseEnvSet *environment.EnvSet) (*CompositionEnv, error) {
+ declType := apiservercel.NewObjectType(typeName, map[string]*apiservercel.DeclField{})
+ envSet, err := baseEnvSet.Extend(environment.VersionedOptions{
+ // set to 1.0 because composition is one of the fundamental components
+ IntroducedVersion: version.MajorMinor(1, 0),
+ EnvOptions: []cel.EnvOption{
+ cel.Variable("variables", declType.CelType()),
+ },
+ DeclTypes: []*apiservercel.DeclType{
+ declType,
+ },
+ })
+ if err != nil {
+ return nil, err
+ }
+ return &CompositionEnv{
+ MapType: declType,
+ EnvSet: envSet,
+ CompiledVariables: map[string]CompilationResult{},
+ }, nil
+}
+
+func (c *CompositionEnv) CreateContext(parent context.Context) CompositionContext {
+ return &compositionContext{
+ Context: parent,
+ compositionEnv: c,
+ }
+}
+
+type CompositionContext interface {
+ context.Context
+ Variables(activation any) ref.Val
+ GetAndResetCost() int64
+}
+
+type compositionContext struct {
+ context.Context
+
+ compositionEnv *CompositionEnv
+ accumulatedCost int64
+}
+
+func (c *compositionContext) Variables(activation any) ref.Val {
+ lazyMap := lazy.NewMapValue(c.compositionEnv.MapType)
+ for name, result := range c.compositionEnv.CompiledVariables {
+ accessor := &variableAccessor{
+ name: name,
+ result: result,
+ activation: activation,
+ context: c,
+ }
+ lazyMap.Append(name, accessor.Callback)
+ }
+ return lazyMap
+}
+
+func (f *CompositedFilter) ForInput(ctx context.Context, versionedAttr *admission.VersionedAttributes, request *v1.AdmissionRequest, optionalVars OptionalVariableBindings, runtimeCELCostBudget int64) ([]EvaluationResult, int64, error) {
+ ctx = f.compositionEnv.CreateContext(ctx)
+ return f.Filter.ForInput(ctx, versionedAttr, request, optionalVars, runtimeCELCostBudget)
+}
+
+func (c *compositionContext) reportCost(cost int64) {
+ c.accumulatedCost += cost
+}
+
+func (c *compositionContext) GetAndResetCost() int64 {
+ cost := c.accumulatedCost
+ c.accumulatedCost = 0
+ return cost
+}
+
+type variableAccessor struct {
+ name string
+ result CompilationResult
+ activation any
+ context *compositionContext
+}
+
+func (a *variableAccessor) Callback(_ *lazy.MapValue) ref.Val {
+ if a.result.Error != nil {
+ return types.NewErr("composited variable %q fails to compile: %v", a.name, a.result.Error)
+ }
+
+ v, details, err := a.result.Program.Eval(a.activation)
+ if details == nil {
+ return types.NewErr("unable to get evaluation details of variable %q", a.name)
+ }
+ costPtr := details.ActualCost()
+ if costPtr == nil {
+ return types.NewErr("unable to calculate cost of variable %q", a.name)
+ }
+ cost := int64(*costPtr)
+ if *costPtr > math.MaxInt64 {
+ cost = math.MaxInt64
+ }
+ a.context.reportCost(cost)
+
+ if err != nil {
+ return types.NewErr("composited variable %q fails to evaluate: %v", a.name, err)
+ }
+ return v
+}
diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/composition_test.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/composition_test.go
new file mode 100644
index 00000000000..08ec6c5225c
--- /dev/null
+++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/composition_test.go
@@ -0,0 +1,172 @@
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cel
+
+import (
+ "context"
+ "strings"
+ "testing"
+
+ "github.com/google/cel-go/cel"
+
+ "k8s.io/apiserver/pkg/admission"
+ celconfig "k8s.io/apiserver/pkg/apis/cel"
+ "k8s.io/apiserver/pkg/cel/environment"
+)
+
+type testVariable struct {
+ name string
+ expression string
+}
+
+func (t *testVariable) GetExpression() string {
+ return t.expression
+}
+
+func (t *testVariable) ReturnTypes() []*cel.Type {
+ return []*cel.Type{cel.AnyType}
+}
+
+func (t *testVariable) GetName() string {
+ return t.name
+}
+
+func TestCompositedPolicies(t *testing.T) {
+ cases := []struct {
+ name string
+ variables []NamedExpressionAccessor
+ expression string
+ attributes admission.Attributes
+ expectedResult any
+ expectErr bool
+ expectedErrorMessage string
+ runtimeCostBudget int64
+ }{
+ {
+ name: "simple",
+ variables: []NamedExpressionAccessor{
+ &testVariable{
+ name: "name",
+ expression: "object.metadata.name",
+ },
+ },
+ attributes: endpointCreateAttributes(),
+ expression: "variables.name == 'endpoints1'",
+ expectedResult: true,
+ },
+ {
+ name: "delayed compile error",
+ variables: []NamedExpressionAccessor{
+ &testVariable{
+ name: "name",
+ expression: "1 == '1'", // won't compile
+ },
+ },
+ attributes: endpointCreateAttributes(),
+ expression: "variables.name == 'endpoints1'",
+ expectErr: true,
+ expectedErrorMessage: `composited variable "name" fails to compile:`,
+ },
+ {
+ name: "delayed eval error",
+ variables: []NamedExpressionAccessor{
+ &testVariable{
+ name: "name",
+ expression: "object.spec.subsets[114514].addresses.size()", // array index out of bound
+ },
+ },
+ attributes: endpointCreateAttributes(),
+ expression: "variables.name == 'endpoints1'",
+ expectErr: true,
+ expectedErrorMessage: `composited variable "name" fails to evaluate:`,
+ },
+ {
+ name: "out of budget during lazy evaluation",
+ variables: []NamedExpressionAccessor{
+ &testVariable{
+ name: "name",
+ expression: "object.metadata.name", // cost = 3
+ },
+ },
+ attributes: endpointCreateAttributes(),
+ expression: "variables.name == 'endpoints1'", // cost = 3
+ expectedResult: true,
+ runtimeCostBudget: 4, // enough for main variable but not for entire expression
+ expectErr: true,
+ expectedErrorMessage: "running out of cost budget",
+ },
+ {
+ name: "lazy evaluation, budget counts only once",
+ variables: []NamedExpressionAccessor{
+ &testVariable{
+ name: "name",
+ expression: "object.metadata.name", // cost = 3
+ },
+ },
+ attributes: endpointCreateAttributes(),
+ expression: "variables.name == 'endpoints1' && variables.name == 'endpoints1' ", // cost = 7
+ expectedResult: true,
+ runtimeCostBudget: 10, // enough for one lazy evaluation but not two, should pass
+ },
+ }
+ for _, tc := range cases {
+ t.Run(tc.name, func(t *testing.T) {
+ compiler, err := NewCompositedCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()))
+ if err != nil {
+ t.Fatal(err)
+ }
+ compiler.CompileAndStoreVariables(tc.variables, OptionalVariableDeclarations{HasParams: false, HasAuthorizer: false}, environment.NewExpressions)
+ validations := []ExpressionAccessor{&condition{Expression: tc.expression}}
+ f := compiler.Compile(validations, OptionalVariableDeclarations{HasParams: false, HasAuthorizer: false}, environment.NewExpressions)
+ versionedAttr, err := admission.NewVersionedAttributes(tc.attributes, tc.attributes.GetKind(), newObjectInterfacesForTest())
+ if err != nil {
+ t.Fatal(err)
+ }
+ optionalVars := OptionalVariableBindings{}
+ costBudget := tc.runtimeCostBudget
+ if costBudget == 0 {
+ costBudget = celconfig.RuntimeCELCostBudget
+ }
+ result, _, err := f.ForInput(context.Background(), versionedAttr, CreateAdmissionRequest(versionedAttr.Attributes), optionalVars, costBudget)
+ if !tc.expectErr && err != nil {
+ t.Fatalf("failed evaluation: %v", err)
+ }
+ if !tc.expectErr && len(result) == 0 {
+ t.Fatal("unexpected empty result")
+ }
+ if err == nil {
+ err = result[0].Error
+ }
+ if tc.expectErr {
+ if err == nil {
+ t.Fatal("unexpected no error")
+ }
+ if !strings.Contains(err.Error(), tc.expectedErrorMessage) {
+ t.Errorf("expected error to contain %q but got %s", tc.expectedErrorMessage, err.Error())
+ }
+ return
+ }
+ if err != nil {
+ t.Fatalf("failed validation: %v", result[0].Error)
+ }
+ if tc.expectedResult != result[0].EvalResult.Value() {
+ t.Errorf("wrong result: expected %v but got %v", tc.expectedResult, result)
+ }
+
+ })
+ }
+}
diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/filter.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/filter.go
index 9aa10bd6b26..bb586cbf973 100644
--- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/filter.go
+++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/filter.go
@@ -46,7 +46,7 @@ func NewFilterCompiler(env *environment.EnvSet) FilterCompiler {
}
type evaluationActivation struct {
- object, oldObject, params, request, authorizer, requestResourceAuthorizer interface{}
+ object, oldObject, params, request, authorizer, requestResourceAuthorizer, variables interface{}
}
// ResolveName returns a value from the activation by qualified name, or false if the name
@@ -65,6 +65,8 @@ func (a *evaluationActivation) ResolveName(name string) (interface{}, bool) {
return a.authorizer, a.authorizer != nil
case RequestResourceAuthorizerVarName:
return a.requestResourceAuthorizer, a.requestResourceAuthorizer != nil
+ case VariableVarName: // variables always present
+ return a.variables, true
default:
return nil, false
}
@@ -163,6 +165,14 @@ func (f *filter) ForInput(ctx context.Context, versionedAttr *admission.Versione
requestResourceAuthorizer: requestResourceAuthorizerVal,
}
+ // composition is an optional feature that only applies for ValidatingAdmissionPolicy.
+ // check if the context allows composition
+ var compositionCtx CompositionContext
+ var ok bool
+ if compositionCtx, ok = ctx.(CompositionContext); ok {
+ va.variables = compositionCtx.Variables(va)
+ }
+
remainingBudget := runtimeCELCostBudget
for i, compilationResult := range f.compilationResults {
var evaluation = &evaluations[i]
@@ -186,6 +196,17 @@ func (f *filter) ForInput(ctx context.Context, versionedAttr *admission.Versione
}
t1 := time.Now()
evalResult, evalDetails, err := compilationResult.Program.ContextEval(ctx, va)
+ // budget may be spent due to lazy evaluation of composited variables
+ if compositionCtx != nil {
+ compositionCost := compositionCtx.GetAndResetCost()
+ if compositionCost > remainingBudget {
+ return nil, -1, &cel.Error{
+ Type: cel.ErrorTypeInvalid,
+ Detail: fmt.Sprintf("validation failed due to running out of cost budget, no further validation rules will be run"),
+ }
+ }
+ remainingBudget -= compositionCost
+ }
elapsed := time.Since(t1)
evaluation.Elapsed = elapsed
if evalDetails == nil {
diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/interface.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/interface.go
index 50782d6f044..5bc708cf529 100644
--- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/interface.go
+++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/cel/interface.go
@@ -35,6 +35,13 @@ type ExpressionAccessor interface {
ReturnTypes() []*cel.Type
}
+// NamedExpressionAccessor extends NamedExpressionAccessor with a name.
+type NamedExpressionAccessor interface {
+ ExpressionAccessor
+
+ GetName() string // follows the naming convention of ExpressionAccessor
+}
+
// EvaluationResult contains the minimal required fields and metadata of a cel evaluation
type EvaluationResult struct {
EvalResult ref.Val
diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/controller.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/controller.go
index 506167f9752..4df61ed1ca3 100644
--- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/controller.go
+++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/controller.go
@@ -36,12 +36,10 @@ import (
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/admission"
celmetrics "k8s.io/apiserver/pkg/admission/cel"
- "k8s.io/apiserver/pkg/admission/plugin/cel"
"k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/internal/generic"
"k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/matching"
celconfig "k8s.io/apiserver/pkg/apis/cel"
"k8s.io/apiserver/pkg/authorization/authorizer"
- "k8s.io/apiserver/pkg/cel/environment"
"k8s.io/apiserver/pkg/warning"
"k8s.io/client-go/dynamic"
"k8s.io/client-go/informers"
@@ -135,7 +133,7 @@ func NewAdmissionController(
restMapper,
client,
dynamicClient,
- cel.NewFilterCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion())),
+ nil,
NewMatcher(matching.NewMatcher(informerFactory.Core().V1().Namespaces().Lister(), client)),
generic.NewInformer[*v1alpha1.ValidatingAdmissionPolicy](
informerFactory.Admissionregistration().V1alpha1().ValidatingAdmissionPolicies().Informer()),
diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/controller_reconcile.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/controller_reconcile.go
index 77d9670fad0..ea987ee2652 100644
--- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/controller_reconcile.go
+++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/controller_reconcile.go
@@ -29,6 +29,7 @@ import (
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
+ utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/sets"
celmetrics "k8s.io/apiserver/pkg/admission/cel"
"k8s.io/apiserver/pkg/admission/plugin/cel"
@@ -53,6 +54,7 @@ type policyController struct {
// Provided to the policy's Compile function as an injected dependency to
// assist with compiling its expressions to CEL
+ // pass nil to create filter compiler in demand
filterCompiler cel.FilterCompiler
matcher Matcher
@@ -463,18 +465,29 @@ func (c *policyController) latestPolicyData() []policyData {
failurePolicy := convertv1alpha1FailurePolicyTypeTov1FailurePolicyType(definitionInfo.lastReconciledValue.Spec.FailurePolicy)
var matcher matchconditions.Matcher = nil
matchConditions := definitionInfo.lastReconciledValue.Spec.MatchConditions
+
+ filterCompiler := c.filterCompiler
+ if filterCompiler == nil {
+ compositedCompiler, err := cel.NewCompositedCompiler(environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()))
+ if err == nil {
+ filterCompiler = compositedCompiler
+ compositedCompiler.CompileAndStoreVariables(convertV1alpha1Variables(definitionInfo.lastReconciledValue.Spec.Variables), optionalVars, environment.StoredExpressions)
+ } else {
+ utilruntime.HandleError(err)
+ }
+ }
if len(matchConditions) > 0 {
matchExpressionAccessors := make([]cel.ExpressionAccessor, len(matchConditions))
for i := range matchConditions {
matchExpressionAccessors[i] = (*matchconditions.MatchCondition)(&matchConditions[i])
}
- matcher = matchconditions.NewMatcher(c.filterCompiler.Compile(matchExpressionAccessors, optionalVars, environment.StoredExpressions), failurePolicy, "validatingadmissionpolicy", definitionInfo.lastReconciledValue.Name)
+ matcher = matchconditions.NewMatcher(filterCompiler.Compile(matchExpressionAccessors, optionalVars, environment.StoredExpressions), failurePolicy, "validatingadmissionpolicy", definitionInfo.lastReconciledValue.Name)
}
bindingInfo.validator = c.newValidator(
- c.filterCompiler.Compile(convertv1alpha1Validations(definitionInfo.lastReconciledValue.Spec.Validations), optionalVars, environment.StoredExpressions),
+ filterCompiler.Compile(convertv1alpha1Validations(definitionInfo.lastReconciledValue.Spec.Validations), optionalVars, environment.StoredExpressions),
matcher,
- c.filterCompiler.Compile(convertv1alpha1AuditAnnotations(definitionInfo.lastReconciledValue.Spec.AuditAnnotations), optionalVars, environment.StoredExpressions),
- c.filterCompiler.Compile(convertV1Alpha1MessageExpressions(definitionInfo.lastReconciledValue.Spec.Validations), expressionOptionalVars, environment.StoredExpressions),
+ filterCompiler.Compile(convertv1alpha1AuditAnnotations(definitionInfo.lastReconciledValue.Spec.AuditAnnotations), optionalVars, environment.StoredExpressions),
+ filterCompiler.Compile(convertV1Alpha1MessageExpressions(definitionInfo.lastReconciledValue.Spec.Validations), expressionOptionalVars, environment.StoredExpressions),
failurePolicy,
)
}
@@ -551,6 +564,14 @@ func convertv1alpha1AuditAnnotations(inputValidations []v1alpha1.AuditAnnotation
return celExpressionAccessor
}
+func convertV1alpha1Variables(variables []v1alpha1.Variable) []cel.NamedExpressionAccessor {
+ namedExpressions := make([]cel.NamedExpressionAccessor, len(variables))
+ for i, variable := range variables {
+ namedExpressions[i] = &Variable{Name: variable.Name, Expression: variable.Expression}
+ }
+ return namedExpressions
+}
+
func getNamespaceName(namespace, name string) namespacedName {
return namespacedName{
namespace: namespace,
diff --git a/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/interface.go b/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/interface.go
index 135148b9e1e..750295d3602 100644
--- a/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/interface.go
+++ b/staging/src/k8s.io/apiserver/pkg/admission/plugin/validatingadmissionpolicy/interface.go
@@ -61,6 +61,24 @@ func (v *AuditAnnotationCondition) ReturnTypes() []*celgo.Type {
return []*celgo.Type{celgo.StringType, celgo.NullType}
}
+// Variable is a named expression for composition.
+type Variable struct {
+ Name string
+ Expression string
+}
+
+func (v *Variable) GetExpression() string {
+ return v.Expression
+}
+
+func (v *Variable) ReturnTypes() []*celgo.Type {
+ return []*celgo.Type{celgo.AnyType, celgo.DynType}
+}
+
+func (v *Variable) GetName() string {
+ return v.Name
+}
+
// Matcher is used for matching ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding to attributes
type Matcher interface {
admission.InitializationValidator
diff --git a/staging/src/k8s.io/apiserver/pkg/cel/lazy/lazy.go b/staging/src/k8s.io/apiserver/pkg/cel/lazy/lazy.go
new file mode 100644
index 00000000000..1742deb0a2f
--- /dev/null
+++ b/staging/src/k8s.io/apiserver/pkg/cel/lazy/lazy.go
@@ -0,0 +1,191 @@
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package lazy
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/google/cel-go/common/types"
+ "github.com/google/cel-go/common/types/ref"
+ "github.com/google/cel-go/common/types/traits"
+
+ "k8s.io/apiserver/pkg/cel"
+)
+
+type GetFieldFunc func(*MapValue) ref.Val
+
+var _ ref.Val = (*MapValue)(nil)
+var _ traits.Mapper = (*MapValue)(nil)
+
+// MapValue is a map that lazily evaluate its value when a field is first accessed.
+// The map value is not designed to be thread-safe.
+type MapValue struct {
+ typeValue *types.TypeValue
+
+ // values are previously evaluated values obtained from callbacks
+ values map[string]ref.Val
+ // callbacks are a map of field name to the function that returns the field Val
+ callbacks map[string]GetFieldFunc
+ // knownValues are registered names, used for iteration
+ knownValues []string
+}
+
+func NewMapValue(objectType ref.Type) *MapValue {
+ return &MapValue{
+ typeValue: types.NewTypeValue(objectType.TypeName(), traits.IndexerType|traits.FieldTesterType|traits.IterableType),
+ values: map[string]ref.Val{},
+ callbacks: map[string]GetFieldFunc{},
+ }
+}
+
+// Append adds the given field with its name and callback.
+func (m *MapValue) Append(name string, callback GetFieldFunc) {
+ m.knownValues = append(m.knownValues, name)
+ m.callbacks[name] = callback
+}
+
+// Contains checks if the key is known to the map
+func (m *MapValue) Contains(key ref.Val) ref.Val {
+ v, found := m.Find(key)
+ if v != nil && types.IsUnknownOrError(v) {
+ return v
+ }
+ return types.Bool(found)
+}
+
+// Iterator returns an iterator to traverse the map.
+func (m *MapValue) Iterator() traits.Iterator {
+ return &iterator{parent: m, index: 0}
+}
+
+// Size returns the number of currently known fields
+func (m *MapValue) Size() ref.Val {
+ return types.Int(len(m.callbacks))
+}
+
+// ConvertToNative returns an error because it is disallowed
+func (m *MapValue) ConvertToNative(typeDesc reflect.Type) (any, error) {
+ return nil, fmt.Errorf("disallowed conversion from %q to %q", m.typeValue.TypeName(), typeDesc.Name())
+}
+
+// ConvertToType converts the map to the given type.
+// Only its own type and "Type" type are allowed.
+func (m *MapValue) ConvertToType(typeVal ref.Type) ref.Val {
+ switch typeVal {
+ case m.typeValue:
+ return m
+ case types.TypeType:
+ return m.typeValue
+ }
+ return types.NewErr("disallowed conversion from %q to %q", m.typeValue.TypeName(), typeVal.TypeName())
+}
+
+// Equal returns true if the other object is the same pointer-wise.
+func (m *MapValue) Equal(other ref.Val) ref.Val {
+ otherMap, ok := other.(*MapValue)
+ if !ok {
+ return types.MaybeNoSuchOverloadErr(other)
+ }
+ return types.Bool(m == otherMap)
+}
+
+// Type returns its registered type.
+func (m *MapValue) Type() ref.Type {
+ return m.typeValue
+}
+
+// Value is not allowed.
+func (m *MapValue) Value() any {
+ return types.NoSuchOverloadErr()
+}
+
+// resolveField resolves the field. Calls the callback if the value is not yet stored.
+func (m *MapValue) resolveField(name string) ref.Val {
+ v, seen := m.values[name]
+ if seen {
+ return v
+ }
+ f := m.callbacks[name]
+ v = f(m)
+ m.values[name] = v
+ return v
+}
+
+func (m *MapValue) Find(key ref.Val) (ref.Val, bool) {
+ n, ok := key.(types.String)
+ if !ok {
+ return types.MaybeNoSuchOverloadErr(n), true
+ }
+ name, ok := cel.Unescape(n.Value().(string))
+ if !ok {
+ return nil, false
+ }
+ if _, exists := m.callbacks[name]; !exists {
+ return nil, false
+ }
+ return m.resolveField(name), true
+}
+
+func (m *MapValue) Get(key ref.Val) ref.Val {
+ v, found := m.Find(key)
+ if found {
+ return v
+ }
+ return types.ValOrErr(key, "no such key: %v", key)
+}
+
+type iterator struct {
+ parent *MapValue
+ index int
+}
+
+func (i *iterator) ConvertToNative(typeDesc reflect.Type) (any, error) {
+ return nil, fmt.Errorf("disallowed conversion to %q", typeDesc.Name())
+}
+
+func (i *iterator) ConvertToType(typeValue ref.Type) ref.Val {
+ return types.NewErr("disallowed conversion o %q", typeValue.TypeName())
+}
+
+func (i *iterator) Equal(other ref.Val) ref.Val {
+ otherIterator, ok := other.(*iterator)
+ if !ok {
+ return types.MaybeNoSuchOverloadErr(other)
+ }
+ return types.Bool(otherIterator == i)
+}
+
+func (i *iterator) Type() ref.Type {
+ return types.IteratorType
+}
+
+func (i *iterator) Value() any {
+ return nil
+}
+
+func (i *iterator) HasNext() ref.Val {
+ return types.Bool(i.index < len(i.parent.knownValues))
+}
+
+func (i *iterator) Next() ref.Val {
+ ret := i.parent.Get(types.String(i.parent.knownValues[i.index]))
+ i.index++
+ return ret
+}
+
+var _ traits.Iterator = (*iterator)(nil)
diff --git a/staging/src/k8s.io/apiserver/pkg/cel/lazy/lazy_test.go b/staging/src/k8s.io/apiserver/pkg/cel/lazy/lazy_test.go
new file mode 100644
index 00000000000..54f288d8a51
--- /dev/null
+++ b/staging/src/k8s.io/apiserver/pkg/cel/lazy/lazy_test.go
@@ -0,0 +1,150 @@
+/*
+Copyright 2023 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package lazy
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/google/cel-go/cel"
+ "github.com/google/cel-go/common/types"
+ "github.com/google/cel-go/common/types/ref"
+ "github.com/google/cel-go/interpreter"
+
+ "k8s.io/apimachinery/pkg/util/version"
+ apiservercel "k8s.io/apiserver/pkg/cel"
+ "k8s.io/apiserver/pkg/cel/environment"
+)
+
+func TestLazyMapType(t *testing.T) {
+ env, variablesType, err := buildTestEnv()
+ if err != nil {
+ t.Fatal(err)
+ }
+ variablesMap := NewMapValue(variablesType)
+ activation := &testActivation{variables: variablesMap}
+
+ // add foo as a string
+ variablesType.Fields["foo"] = apiservercel.NewDeclField("foo", apiservercel.StringType, true, nil, nil)
+ variablesMap.Append("foo", func(_ *MapValue) ref.Val {
+ return types.String("foo-string")
+ })
+
+ exp := "variables.foo == 'foo-string'"
+ v, err := compileAndRun(env, activation, exp)
+ if err != nil {
+ t.Fatalf("%q: %v", exp, err)
+ }
+ if !v.Value().(bool) {
+ t.Errorf("expected equal but got non-equal")
+ }
+
+ evalCounter := 0
+ // add dict as a map constructed from an expression
+ variablesType.Fields["dict"] = apiservercel.NewDeclField("dict", apiservercel.DynType, true, nil, nil)
+ variablesMap.Append("dict", func(_ *MapValue) ref.Val {
+ evalCounter++
+ v, err := compileAndRun(env, activation, `{"a": "a"}`)
+ if err != nil {
+ return types.NewErr(err.Error())
+ }
+ return v
+ })
+
+ // iterate the map with .all
+ exp = `variables.all(n, n != "")`
+ v, err = compileAndRun(env, activation, exp)
+ if err != nil {
+ t.Fatalf("%q: %v", exp, err)
+ }
+ if v.Value().(bool) != true {
+ t.Errorf("%q: wrong result: %v", exp, v.Value())
+ }
+
+ // add unused as a string
+ variablesType.Fields["unused"] = apiservercel.NewDeclField("unused", apiservercel.StringType, true, nil, nil)
+ variablesMap.Append("unused", func(_ *MapValue) ref.Val {
+ t.Fatalf("unused variable must not be evaluated")
+ return nil
+ })
+
+ exp = "variables.dict.a + ' ' + variables.dict.a + ' ' + variables.foo"
+ v, err = compileAndRun(env, activation, exp)
+ if err != nil {
+ t.Fatalf("%q: %v", exp, err)
+ }
+ if v.Value().(string) != "a a foo-string" {
+ t.Errorf("%q: wrong result: %v", exp, v.Value())
+ }
+ if evalCounter != 1 {
+ t.Errorf("expected eval %d times but got %d", 1, evalCounter)
+ }
+}
+
+type testActivation struct {
+ variables *MapValue
+}
+
+func compileAndRun(env *cel.Env, activation *testActivation, exp string) (ref.Val, error) {
+ ast, issues := env.Compile(exp)
+ if issues != nil {
+ return nil, fmt.Errorf("fail to compile: %v", issues)
+ }
+ prog, err := env.Program(ast)
+ if err != nil {
+ return nil, fmt.Errorf("cannot create program: %w", err)
+ }
+ v, _, err := prog.Eval(activation)
+ if err != nil {
+ return nil, fmt.Errorf("cannot eval program: %w", err)
+ }
+ return v, nil
+}
+
+func buildTestEnv() (*cel.Env, *apiservercel.DeclType, error) {
+ variablesType := apiservercel.NewMapType(apiservercel.StringType, apiservercel.AnyType, 0)
+ variablesType.Fields = make(map[string]*apiservercel.DeclField)
+ envSet, err := environment.MustBaseEnvSet(environment.DefaultCompatibilityVersion()).Extend(
+ environment.VersionedOptions{
+ IntroducedVersion: version.MajorMinor(1, 28),
+ EnvOptions: []cel.EnvOption{
+ cel.Variable("variables", variablesType.CelType()),
+ },
+ DeclTypes: []*apiservercel.DeclType{
+ variablesType,
+ },
+ })
+ if err != nil {
+ return nil, nil, err
+ }
+ // TODO: change to NewExpressions after 1.28
+ env, err := envSet.Env(environment.StoredExpressions)
+ return env, variablesType, err
+}
+
+func (a *testActivation) ResolveName(name string) (any, bool) {
+ switch name {
+ case "variables":
+ return a.variables, true
+ default:
+ return nil, false
+ }
+}
+
+func (a *testActivation) Parent() interpreter.Activation {
+ return nil
+}
diff --git a/staging/src/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/validatingadmissionpolicyspec.go b/staging/src/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/validatingadmissionpolicyspec.go
index f674b5b1ec2..7ee320e4288 100644
--- a/staging/src/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/validatingadmissionpolicyspec.go
+++ b/staging/src/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/validatingadmissionpolicyspec.go
@@ -31,6 +31,7 @@ type ValidatingAdmissionPolicySpecApplyConfiguration struct {
FailurePolicy *admissionregistrationv1alpha1.FailurePolicyType `json:"failurePolicy,omitempty"`
AuditAnnotations []AuditAnnotationApplyConfiguration `json:"auditAnnotations,omitempty"`
MatchConditions []MatchConditionApplyConfiguration `json:"matchConditions,omitempty"`
+ Variables []VariableApplyConfiguration `json:"variables,omitempty"`
}
// ValidatingAdmissionPolicySpecApplyConfiguration constructs an declarative configuration of the ValidatingAdmissionPolicySpec type for use with
@@ -101,3 +102,16 @@ func (b *ValidatingAdmissionPolicySpecApplyConfiguration) WithMatchConditions(va
}
return b
}
+
+// WithVariables adds the given value to the Variables field in the declarative configuration
+// and returns the receiver, so that objects can be build by chaining "With" function invocations.
+// If called multiple times, values provided by each call will be appended to the Variables field.
+func (b *ValidatingAdmissionPolicySpecApplyConfiguration) WithVariables(values ...*VariableApplyConfiguration) *ValidatingAdmissionPolicySpecApplyConfiguration {
+ for i := range values {
+ if values[i] == nil {
+ panic("nil value passed to WithVariables")
+ }
+ b.Variables = append(b.Variables, *values[i])
+ }
+ return b
+}
diff --git a/staging/src/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/variable.go b/staging/src/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/variable.go
new file mode 100644
index 00000000000..2c70a8cfb5a
--- /dev/null
+++ b/staging/src/k8s.io/client-go/applyconfigurations/admissionregistration/v1alpha1/variable.go
@@ -0,0 +1,48 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by applyconfiguration-gen. DO NOT EDIT.
+
+package v1alpha1
+
+// VariableApplyConfiguration represents an declarative configuration of the Variable type for use
+// with apply.
+type VariableApplyConfiguration struct {
+ Name *string `json:"name,omitempty"`
+ Expression *string `json:"expression,omitempty"`
+}
+
+// VariableApplyConfiguration constructs an declarative configuration of the Variable type for use with
+// apply.
+func Variable() *VariableApplyConfiguration {
+ return &VariableApplyConfiguration{}
+}
+
+// WithName sets the Name field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the Name field is set to the value of the last call.
+func (b *VariableApplyConfiguration) WithName(value string) *VariableApplyConfiguration {
+ b.Name = &value
+ return b
+}
+
+// WithExpression sets the Expression field in the declarative configuration to the given value
+// and returns the receiver, so that objects can be built by chaining "With" function invocations.
+// If called multiple times, the Expression field is set to the value of the last call.
+func (b *VariableApplyConfiguration) WithExpression(value string) *VariableApplyConfiguration {
+ b.Expression = &value
+ return b
+}
diff --git a/staging/src/k8s.io/client-go/applyconfigurations/internal/internal.go b/staging/src/k8s.io/client-go/applyconfigurations/internal/internal.go
index 2129d0d5860..a23e357e5d4 100644
--- a/staging/src/k8s.io/client-go/applyconfigurations/internal/internal.go
+++ b/staging/src/k8s.io/client-go/applyconfigurations/internal/internal.go
@@ -464,6 +464,14 @@ var schemaYAML = typed.YAMLObject(`types:
elementType:
namedType: io.k8s.api.admissionregistration.v1alpha1.Validation
elementRelationship: atomic
+ - name: variables
+ type:
+ list:
+ elementType:
+ namedType: io.k8s.api.admissionregistration.v1alpha1.Variable
+ elementRelationship: associative
+ keys:
+ - name
- name: io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyStatus
map:
fields:
@@ -497,6 +505,17 @@ var schemaYAML = typed.YAMLObject(`types:
- name: reason
type:
scalar: string
+- name: io.k8s.api.admissionregistration.v1alpha1.Variable
+ map:
+ fields:
+ - name: expression
+ type:
+ scalar: string
+ default: ""
+ - name: name
+ type:
+ scalar: string
+ default: ""
- name: io.k8s.api.admissionregistration.v1beta1.MatchCondition
map:
fields:
diff --git a/staging/src/k8s.io/client-go/applyconfigurations/utils.go b/staging/src/k8s.io/client-go/applyconfigurations/utils.go
index 6c35d6c27cb..d8e4c8f0511 100644
--- a/staging/src/k8s.io/client-go/applyconfigurations/utils.go
+++ b/staging/src/k8s.io/client-go/applyconfigurations/utils.go
@@ -171,6 +171,8 @@ func ForKind(kind schema.GroupVersionKind) interface{} {
return &admissionregistrationv1alpha1.ValidatingAdmissionPolicyStatusApplyConfiguration{}
case v1alpha1.SchemeGroupVersion.WithKind("Validation"):
return &admissionregistrationv1alpha1.ValidationApplyConfiguration{}
+ case v1alpha1.SchemeGroupVersion.WithKind("Variable"):
+ return &admissionregistrationv1alpha1.VariableApplyConfiguration{}
// Group=admissionregistration.k8s.io, Version=v1beta1
case v1beta1.SchemeGroupVersion.WithKind("MatchCondition"):
diff --git a/test/e2e/apimachinery/validatingadmissionpolicy.go b/test/e2e/apimachinery/validatingadmissionpolicy.go
index aff69b944f6..46bbe9ff103 100644
--- a/test/e2e/apimachinery/validatingadmissionpolicy.go
+++ b/test/e2e/apimachinery/validatingadmissionpolicy.go
@@ -188,6 +188,66 @@ var _ = SIGDescribe("ValidatingAdmissionPolicy [Privileged:ClusterAdmin][Alpha][
gomega.Expect(warning.Warning).To(gomega.ContainSubstring("found no matching overload for '_+_' applied to '(string, int)'"))
})
})
+
+ ginkgo.It("should allow expressions to refer variables.", func(ctx context.Context) {
+ ginkgo.By("creating a policy with variables", func() {
+ policy := newValidatingAdmissionPolicyBuilder(f.UniqueName+".policy.example.com").
+ MatchUniqueNamespace(f.UniqueName).
+ StartResourceRule().
+ MatchResource([]string{"apps"}, []string{"v1"}, []string{"deployments"}).
+ EndResourceRule().
+ WithVariable(admissionregistrationv1alpha1.Variable{
+ Name: "replicas",
+ Expression: "object.spec.replicas",
+ }).
+ WithVariable(admissionregistrationv1alpha1.Variable{
+ Name: "replicasReminder", // a bit artificial but good for testing purpose
+ Expression: "variables.replicas % 2",
+ }).
+ WithValidation(admissionregistrationv1alpha1.Validation{
+ Expression: "variables.replicas > 1 && variables.replicasReminder == 1",
+ }).
+ Build()
+ policy, err := client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Create(ctx, policy, metav1.CreateOptions{})
+ framework.ExpectNoError(err, "create policy")
+ ginkgo.DeferCleanup(func(ctx context.Context, name string) error {
+ return client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicies().Delete(ctx, name, metav1.DeleteOptions{})
+ }, policy.Name)
+ binding := createBinding(f.UniqueName+".binding.example.com", f.UniqueName, policy.Name)
+ binding, err = client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicyBindings().Create(ctx, binding, metav1.CreateOptions{})
+ framework.ExpectNoError(err, "create policy binding")
+ ginkgo.DeferCleanup(func(ctx context.Context, name string) error {
+ return client.AdmissionregistrationV1alpha1().ValidatingAdmissionPolicyBindings().Delete(ctx, name, metav1.DeleteOptions{})
+ }, binding.Name)
+ })
+ ginkgo.By("waiting until the marker is denied", func() {
+ deployment := basicDeployment("marker-deployment", 1)
+ err := wait.PollUntilContextCancel(ctx, 100*time.Millisecond, true, func(ctx context.Context) (done bool, err error) {
+ _, err = client.AppsV1().Deployments(f.Namespace.Name).Create(ctx, deployment, metav1.CreateOptions{})
+ defer client.AppsV1().Deployments(f.Namespace.Name).Delete(ctx, deployment.Name, metav1.DeleteOptions{})
+ if err != nil {
+ if apierrors.IsInvalid(err) {
+ return true, nil
+ }
+ return false, err
+ }
+ return false, nil
+ })
+ framework.ExpectNoError(err, "wait for marker")
+ })
+ ginkgo.By("testing a replicated Deployment to be allowed", func() {
+ deployment := basicDeployment("replicated", 3)
+ deployment, err := client.AppsV1().Deployments(f.Namespace.Name).Create(ctx, deployment, metav1.CreateOptions{})
+ defer client.AppsV1().Deployments(f.Namespace.Name).Delete(ctx, deployment.Name, metav1.DeleteOptions{})
+ framework.ExpectNoError(err, "create replicated Deployment")
+ })
+ ginkgo.By("testing a non-replicated ReplicaSet not to be denied", func() {
+ replicaSet := basicReplicaSet("non-replicated", 1)
+ replicaSet, err := client.AppsV1().ReplicaSets(f.Namespace.Name).Create(ctx, replicaSet, metav1.CreateOptions{})
+ defer client.AppsV1().ReplicaSets(f.Namespace.Name).Delete(ctx, replicaSet.Name, metav1.DeleteOptions{})
+ framework.ExpectNoError(err, "create non-replicated ReplicaSet")
+ })
+ })
})
func createBinding(bindingName string, uniqueLabel string, policyName string) *admissionregistrationv1alpha1.ValidatingAdmissionPolicyBinding {
@@ -332,6 +392,11 @@ func (b *validatingAdmissionPolicyBuilder) WithValidation(validation admissionre
return b
}
+func (b *validatingAdmissionPolicyBuilder) WithVariable(variable admissionregistrationv1alpha1.Variable) *validatingAdmissionPolicyBuilder {
+ b.policy.Spec.Variables = append(b.policy.Spec.Variables, variable)
+ return b
+}
+
func (b *validatingAdmissionPolicyBuilder) Build() *admissionregistrationv1alpha1.ValidatingAdmissionPolicy {
return b.policy
}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index fdd5396a4b0..eab556ef7fb 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1488,6 +1488,7 @@ k8s.io/apiserver/pkg/authorization/union
k8s.io/apiserver/pkg/cel
k8s.io/apiserver/pkg/cel/common
k8s.io/apiserver/pkg/cel/environment
+k8s.io/apiserver/pkg/cel/lazy
k8s.io/apiserver/pkg/cel/library
k8s.io/apiserver/pkg/cel/metrics
k8s.io/apiserver/pkg/cel/openapi