ValidatingAdmissionPolicy: Variable Composition (#118642)
* [API REVIEW] Variable Composition * lazy map. * variable composition implementation. * check variables during VAP validation. * generated: ./hack/update-vendor.sh * generated: UPDATE_COMPATIBILITY_FIXTURE_DATA (cd staging/src/k8s.io/api/ && env UPDATE_COMPATIBILITY_FIXTURE_DATA=true go test) * cost calucation. * tests for cost calculations. * e2e test for variables. * fix doc for Validation.Expression. * generated: ./hack/update-codegen.sh * fix missing utilruntime import. * generated: ./hack/update-openapi-spec.sh
This commit is contained in:
33
api/openapi-spec/swagger.json
generated
33
api/openapi-spec/swagger.json
generated
@@ -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": {
|
||||
|
@@ -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": {
|
||||
|
@@ -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.
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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)
|
||||
}
|
||||
|
@@ -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: <input>: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: <input>: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: <input>: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 {
|
||||
|
@@ -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
|
||||
|
59
pkg/generated/openapi/zz_generated.openapi.go
generated
59
pkg/generated/openapi/zz_generated.openapi.go
generated
@@ -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{
|
||||
|
@@ -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
|
||||
|
@@ -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;
|
||||
}
|
||||
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
|
@@ -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
|
||||
}
|
||||
|
@@ -139,6 +139,12 @@
|
||||
"name": "nameValue",
|
||||
"expression": "expressionValue"
|
||||
}
|
||||
],
|
||||
"variables": [
|
||||
{
|
||||
"name": "nameValue",
|
||||
"expression": "expressionValue"
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": {
|
||||
|
Binary file not shown.
@@ -90,6 +90,9 @@ spec:
|
||||
message: messageValue
|
||||
messageExpression: messageExpressionValue
|
||||
reason: reasonValue
|
||||
variables:
|
||||
- expression: expressionValue
|
||||
name: nameValue
|
||||
status:
|
||||
conditions:
|
||||
- lastTransitionTime: "2004-01-01T01:01:01Z"
|
||||
|
@@ -126,7 +126,8 @@
|
||||
"reason": "reasonValue"
|
||||
}
|
||||
],
|
||||
"failurePolicy": "failurePolicyValue"
|
||||
"failurePolicy": "failurePolicyValue",
|
||||
"variables": null
|
||||
},
|
||||
"status": {}
|
||||
}
|
@@ -83,4 +83,5 @@ spec:
|
||||
- expression: expressionValue
|
||||
message: messageValue
|
||||
reason: reasonValue
|
||||
variables: null
|
||||
status: {}
|
||||
|
@@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@@ -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
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
@@ -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)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
@@ -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 {
|
||||
|
@@ -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
|
||||
|
@@ -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()),
|
||||
|
@@ -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,
|
||||
|
@@ -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
|
||||
|
191
staging/src/k8s.io/apiserver/pkg/cel/lazy/lazy.go
Normal file
191
staging/src/k8s.io/apiserver/pkg/cel/lazy/lazy.go
Normal file
@@ -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)
|
150
staging/src/k8s.io/apiserver/pkg/cel/lazy/lazy_test.go
Normal file
150
staging/src/k8s.io/apiserver/pkg/cel/lazy/lazy_test.go
Normal file
@@ -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
|
||||
}
|
@@ -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
|
||||
}
|
||||
|
@@ -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
|
||||
}
|
@@ -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:
|
||||
|
@@ -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"):
|
||||
|
@@ -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
|
||||
}
|
||||
|
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
@@ -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
|
||||
|
Reference in New Issue
Block a user