Merge pull request #119215 from alexzielenski/apiserver/policy/namespaceParamRef-alpha
KEP-3488: Per namespace policy params
This commit is contained in:
18
api/openapi-spec/swagger.json
generated
18
api/openapi-spec/swagger.json
generated
@@ -547,15 +547,23 @@
|
||||
"x-kubernetes-map-type": "atomic"
|
||||
},
|
||||
"io.k8s.api.admissionregistration.v1alpha1.ParamRef": {
|
||||
"description": "ParamRef references a parameter resource",
|
||||
"description": "ParamRef describes how to locate the params to be used as input to expressions of rules applied by a policy binding.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "Name of the resource being referenced.",
|
||||
"description": "`name` is the name of the resource being referenced.\n\n`name` and `selector` are mutually exclusive properties. If one is set, the other must be unset.",
|
||||
"type": "string"
|
||||
},
|
||||
"namespace": {
|
||||
"description": "Namespace of the referenced resource. Should be empty for the cluster-scoped resources",
|
||||
"description": "namespace is the namespace of the referenced resource. Allows limiting the search for params to a specific namespace. Applies to both `name` and `selector` fields.\n\nA per-namespace parameter may be used by specifying a namespace-scoped `paramKind` in the policy and leaving this field empty.\n\n- If `paramKind` is cluster-scoped, this field MUST be unset. Setting this field results in a configuration error.\n\n- If `paramKind` is namespace-scoped, the namespace of the object being evaluated for admission will be used when this field is left unset. Take care that if this is left empty the binding must not match any cluster-scoped resources, which will result in an error.",
|
||||
"type": "string"
|
||||
},
|
||||
"parameterNotFoundAction": {
|
||||
"description": "`parameterNotFoundAction` controls the behavior of the binding when the resource exists, and name or selector is valid, but there are no parameters matched by the binding. If the value is set to `Allow`, then no matched parameters will be treated as successful validation by the binding. If set to `Deny`, then no matched parameters will be subject to the `failurePolicy` of the policy.\n\nAllowed values are `Allow` or `Deny` Default to `Deny`",
|
||||
"type": "string"
|
||||
},
|
||||
"selector": {
|
||||
"$ref": "#/definitions/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector",
|
||||
"description": "selector can be used to match multiple param objects based on their labels. Supply selector: {} to match all resources of the ParamKind.\n\nIf multiple params are found, they are all evaluated with the policy expressions and the results are ANDed together.\n\nOne of `name` or `selector` must be set, but `name` and `selector` are mutually exclusive properties. If one is set, the other must be unset."
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
@@ -609,7 +617,7 @@
|
||||
]
|
||||
},
|
||||
"io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyBinding": {
|
||||
"description": "ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources. ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.",
|
||||
"description": "ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources. ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.\n\nFor a given admission request, each binding will cause its policy to be evaluated N times, where N is 1 for policies/bindings that don't use params, otherwise N is the number of parameters selected by the binding.\n\nThe CEL expressions of a policy must have a computed CEL cost below the maximum CEL budget. Each evaluation of the policy is given an independent CEL cost budget. Adding/removing policies, bindings, or params can not affect whether a given (policy, binding, param) combination is within its own CEL budget.",
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
@@ -678,7 +686,7 @@
|
||||
},
|
||||
"paramRef": {
|
||||
"$ref": "#/definitions/io.k8s.api.admissionregistration.v1alpha1.ParamRef",
|
||||
"description": "ParamRef specifies the parameter resource used to configure the admission control policy. It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy. If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied."
|
||||
"description": "paramRef specifies the parameter resource used to configure the admission control policy. It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy. If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied. If the policy does not specify a ParamKind then this field is ignored, and the rules are evaluated without a param."
|
||||
},
|
||||
"policyName": {
|
||||
"description": "PolicyName references a ValidatingAdmissionPolicy name which the ValidatingAdmissionPolicyBinding binds to. If the referenced resource does not exist, this binding is considered invalid and will be ignored Required.",
|
||||
|
@@ -185,15 +185,27 @@
|
||||
"x-kubernetes-map-type": "atomic"
|
||||
},
|
||||
"io.k8s.api.admissionregistration.v1alpha1.ParamRef": {
|
||||
"description": "ParamRef references a parameter resource",
|
||||
"description": "ParamRef describes how to locate the params to be used as input to expressions of rules applied by a policy binding.",
|
||||
"properties": {
|
||||
"name": {
|
||||
"description": "Name of the resource being referenced.",
|
||||
"description": "`name` is the name of the resource being referenced.\n\n`name` and `selector` are mutually exclusive properties. If one is set, the other must be unset.",
|
||||
"type": "string"
|
||||
},
|
||||
"namespace": {
|
||||
"description": "Namespace of the referenced resource. Should be empty for the cluster-scoped resources",
|
||||
"description": "namespace is the namespace of the referenced resource. Allows limiting the search for params to a specific namespace. Applies to both `name` and `selector` fields.\n\nA per-namespace parameter may be used by specifying a namespace-scoped `paramKind` in the policy and leaving this field empty.\n\n- If `paramKind` is cluster-scoped, this field MUST be unset. Setting this field results in a configuration error.\n\n- If `paramKind` is namespace-scoped, the namespace of the object being evaluated for admission will be used when this field is left unset. Take care that if this is left empty the binding must not match any cluster-scoped resources, which will result in an error.",
|
||||
"type": "string"
|
||||
},
|
||||
"parameterNotFoundAction": {
|
||||
"description": "`parameterNotFoundAction` controls the behavior of the binding when the resource exists, and name or selector is valid, but there are no parameters matched by the binding. If the value is set to `Allow`, then no matched parameters will be treated as successful validation by the binding. If set to `Deny`, then no matched parameters will be subject to the `failurePolicy` of the policy.\n\nAllowed values are `Allow` or `Deny` Default to `Deny`",
|
||||
"type": "string"
|
||||
},
|
||||
"selector": {
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/components/schemas/io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector"
|
||||
}
|
||||
],
|
||||
"description": "selector can be used to match multiple param objects based on their labels. Supply selector: {} to match all resources of the ParamKind.\n\nIf multiple params are found, they are all evaluated with the policy expressions and the results are ANDed together.\n\nOne of `name` or `selector` must be set, but `name` and `selector` are mutually exclusive properties. If one is set, the other must be unset."
|
||||
}
|
||||
},
|
||||
"type": "object",
|
||||
@@ -267,7 +279,7 @@
|
||||
]
|
||||
},
|
||||
"io.k8s.api.admissionregistration.v1alpha1.ValidatingAdmissionPolicyBinding": {
|
||||
"description": "ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources. ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.",
|
||||
"description": "ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources. ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.\n\nFor a given admission request, each binding will cause its policy to be evaluated N times, where N is 1 for policies/bindings that don't use params, otherwise N is the number of parameters selected by the binding.\n\nThe CEL expressions of a policy must have a computed CEL cost below the maximum CEL budget. Each evaluation of the policy is given an independent CEL cost budget. Adding/removing policies, bindings, or params can not affect whether a given (policy, binding, param) combination is within its own CEL budget.",
|
||||
"properties": {
|
||||
"apiVersion": {
|
||||
"description": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources",
|
||||
@@ -364,7 +376,7 @@
|
||||
"$ref": "#/components/schemas/io.k8s.api.admissionregistration.v1alpha1.ParamRef"
|
||||
}
|
||||
],
|
||||
"description": "ParamRef specifies the parameter resource used to configure the admission control policy. It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy. If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied."
|
||||
"description": "paramRef specifies the parameter resource used to configure the admission control policy. It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy. If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied. If the policy does not specify a ParamKind then this field is ignored, and the rules are evaluated without a param."
|
||||
},
|
||||
"policyName": {
|
||||
"description": "PolicyName references a ValidatingAdmissionPolicy name which the ValidatingAdmissionPolicyBinding binds to. If the referenced resource does not exist, this binding is considered invalid and will be ignored Required.",
|
||||
|
@@ -98,5 +98,14 @@ var Funcs = func(codecs runtimeserializer.CodecFactory) []interface{} {
|
||||
obj.MatchPolicy = &m
|
||||
}
|
||||
},
|
||||
func(obj *admissionregistration.ParamRef, c fuzz.Continue) {
|
||||
c.FuzzNoCustom(obj) // fuzz self without calling this function again
|
||||
|
||||
// Populate required field
|
||||
if obj.ParameterNotFoundAction == nil {
|
||||
v := admissionregistration.DenyAction
|
||||
obj.ParameterNotFoundAction = &v
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@@ -76,6 +76,18 @@ const (
|
||||
AllScopes ScopeType = "*"
|
||||
)
|
||||
|
||||
// ParameterNotFoundActionType specifies a failure policy that defines how a binding
|
||||
// is evaluated when the param referred by its perNamespaceParamRef is not found.
|
||||
type ParameterNotFoundActionType string
|
||||
|
||||
const (
|
||||
// Allow means all requests will be admitted if no param resources
|
||||
// could be found.
|
||||
AllowAction ParameterNotFoundActionType = "Allow"
|
||||
// Deny means all requests will be denied if no param resources are found.
|
||||
DenyAction ParameterNotFoundActionType = "Deny"
|
||||
)
|
||||
|
||||
// FailurePolicyType specifies the type of failure policy
|
||||
type FailurePolicyType string
|
||||
|
||||
@@ -401,6 +413,15 @@ type AuditAnnotation struct {
|
||||
|
||||
// ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources.
|
||||
// ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.
|
||||
//
|
||||
// For a given admission request, each binding will cause its policy to be
|
||||
// evaluated N times, where N is 1 for policies/bindings that don't use
|
||||
// params, otherwise N is the number of parameters selected by the binding.
|
||||
//
|
||||
// The CEL expressions of a policy must have a computed CEL cost below the maximum
|
||||
// CEL budget. Each evaluation of the policy is given an independent CEL cost budget.
|
||||
// Adding/removing policies, bindings, or params can not affect whether a
|
||||
// given (policy, binding, param) combination is within its own CEL budget.
|
||||
type ValidatingAdmissionPolicyBinding struct {
|
||||
metav1.TypeMeta
|
||||
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
|
||||
@@ -430,9 +451,10 @@ type ValidatingAdmissionPolicyBindingSpec struct {
|
||||
// Required.
|
||||
PolicyName string
|
||||
|
||||
// ParamRef specifies the parameter resource used to configure the admission control policy.
|
||||
// paramRef specifies the parameter resource used to configure the admission control policy.
|
||||
// It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy.
|
||||
// If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied.
|
||||
// If the policy does not specify a ParamKind then this field is ignored, and the rules are evaluated without a param.
|
||||
// +optional
|
||||
ParamRef *ParamRef
|
||||
|
||||
@@ -486,14 +508,63 @@ type ValidatingAdmissionPolicyBindingSpec struct {
|
||||
ValidationActions []ValidationAction
|
||||
}
|
||||
|
||||
// ParamRef references a parameter resource
|
||||
// ParamRef describes how to locate the params to be used as input to
|
||||
// expressions of rules applied by a policy binding.
|
||||
// +structType=atomic
|
||||
type ParamRef struct {
|
||||
// Name of the resource being referenced.
|
||||
// name is the name of the resource being referenced.
|
||||
//
|
||||
// One of `name` or `selector` must be set, but `name` and `selector` are
|
||||
// mutually exclusive properties. If one is set, the other must be unset.
|
||||
//
|
||||
// A single parameter used for all admission requests can be configured
|
||||
// by setting the `name` field, leaving `selector` blank, and setting namespace
|
||||
// if `paramKind` is namespace-scoped.
|
||||
//
|
||||
// +optional
|
||||
Name string
|
||||
// Namespace of the referenced resource.
|
||||
// Should be empty for the cluster-scoped resources
|
||||
|
||||
// namespace is the namespace of the referenced resource. Allows limiting
|
||||
// the search for params to a specific namespace. Applies to both `name` and
|
||||
// `selector` fields.
|
||||
//
|
||||
// A per-namespace parameter may be used by specifying a namespace-scoped
|
||||
// `paramKind` in the policy and leaving this field empty.
|
||||
//
|
||||
// - If `paramKind` is cluster-scoped, this field MUST be unset. Setting this
|
||||
// field results in a configuration error.
|
||||
//
|
||||
// - If `paramKind` is namespace-scoped, the namespace of the object being
|
||||
// evaluated for admission will be used when this field is left unset. Take
|
||||
// care that if this is left empty the binding must not match any cluster-scoped
|
||||
// resources, which will result in an error.
|
||||
//
|
||||
// +optional
|
||||
Namespace string
|
||||
|
||||
// selector can be used to match multiple param objects based on their labels.
|
||||
// Supply selector: {} to match all resources of the ParamKind.
|
||||
//
|
||||
// If multiple params are found, they are all evaluated with the policy expressions
|
||||
// and the results are ANDed together.
|
||||
//
|
||||
// One of `name` or `selector` must be set, but `name` and `selector` are
|
||||
// mutually exclusive properties. If one is set, the other must be unset.
|
||||
//
|
||||
// +optional
|
||||
Selector *metav1.LabelSelector
|
||||
|
||||
// parameterNotFoundAction controls the behavior of the binding when the resource
|
||||
// exists, and name or selector is valid, but there are no parameters
|
||||
// matched by the binding. If the value is set to `Allow`, then no
|
||||
// matched parameters will be treated as successful validation by the binding.
|
||||
// If set to `Deny`, then no matched parameters will be subject to the
|
||||
// `failurePolicy` of the policy.
|
||||
//
|
||||
// Allowed values are `Allow` or `Deny`
|
||||
//
|
||||
// Required
|
||||
ParameterNotFoundAction *ParameterNotFoundActionType
|
||||
}
|
||||
|
||||
// MatchResources decides whether to run the admission control policy on an object based
|
||||
|
@@ -49,3 +49,11 @@ func SetDefaults_MatchResources(obj *admissionregistrationv1alpha1.MatchResource
|
||||
obj.ObjectSelector = &selector
|
||||
}
|
||||
}
|
||||
|
||||
// SetDefaults_ParamRef sets defaults for ParamRef
|
||||
func SetDefaults_ParamRef(obj *admissionregistrationv1alpha1.ParamRef) {
|
||||
if obj.ParameterNotFoundAction == nil {
|
||||
v := admissionregistrationv1alpha1.DenyAction
|
||||
obj.ParameterNotFoundAction = &v
|
||||
}
|
||||
}
|
||||
|
@@ -116,3 +116,58 @@ func TestDefaultAdmissionPolicy(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultAdmissionPolicyBinding(t *testing.T) {
|
||||
denyAction := v1alpha1.DenyAction
|
||||
equivalent := v1alpha1.Equivalent
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
original runtime.Object
|
||||
expected runtime.Object
|
||||
}{
|
||||
{
|
||||
name: "ValidatingAdmissionPolicyBinding.ParamRef.ParameterNotFoundAction",
|
||||
original: &v1alpha1.ValidatingAdmissionPolicyBinding{
|
||||
Spec: v1alpha1.ValidatingAdmissionPolicyBindingSpec{
|
||||
ParamRef: &v1alpha1.ParamRef{},
|
||||
},
|
||||
},
|
||||
expected: &v1alpha1.ValidatingAdmissionPolicyBinding{
|
||||
Spec: v1alpha1.ValidatingAdmissionPolicyBindingSpec{
|
||||
ParamRef: &v1alpha1.ParamRef{
|
||||
ParameterNotFoundAction: &denyAction,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ValidatingAdmissionPolicyBinding.MatchResources",
|
||||
original: &v1alpha1.ValidatingAdmissionPolicyBinding{
|
||||
Spec: v1alpha1.ValidatingAdmissionPolicyBindingSpec{
|
||||
MatchResources: &v1alpha1.MatchResources{},
|
||||
},
|
||||
},
|
||||
expected: &v1alpha1.ValidatingAdmissionPolicyBinding{
|
||||
Spec: v1alpha1.ValidatingAdmissionPolicyBindingSpec{
|
||||
MatchResources: &v1alpha1.MatchResources{
|
||||
NamespaceSelector: &metav1.LabelSelector{},
|
||||
ObjectSelector: &metav1.LabelSelector{},
|
||||
MatchPolicy: &equivalent,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
original := test.original
|
||||
expected := test.expected
|
||||
legacyscheme.Scheme.Default(original)
|
||||
if !apiequality.Semantic.DeepEqual(original, expected) {
|
||||
t.Error(cmp.Diff(expected, original))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@@ -397,6 +397,8 @@ func Convert_admissionregistration_ParamKind_To_v1alpha1_ParamKind(in *admission
|
||||
func autoConvert_v1alpha1_ParamRef_To_admissionregistration_ParamRef(in *v1alpha1.ParamRef, out *admissionregistration.ParamRef, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
out.Namespace = in.Namespace
|
||||
out.Selector = (*v1.LabelSelector)(unsafe.Pointer(in.Selector))
|
||||
out.ParameterNotFoundAction = (*admissionregistration.ParameterNotFoundActionType)(unsafe.Pointer(in.ParameterNotFoundAction))
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -408,6 +410,8 @@ func Convert_v1alpha1_ParamRef_To_admissionregistration_ParamRef(in *v1alpha1.Pa
|
||||
func autoConvert_admissionregistration_ParamRef_To_v1alpha1_ParamRef(in *admissionregistration.ParamRef, out *v1alpha1.ParamRef, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
out.Namespace = in.Namespace
|
||||
out.Selector = (*v1.LabelSelector)(unsafe.Pointer(in.Selector))
|
||||
out.ParameterNotFoundAction = (*v1alpha1.ParameterNotFoundActionType)(unsafe.Pointer(in.ParameterNotFoundAction))
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@@ -62,6 +62,9 @@ func SetObjectDefaults_ValidatingAdmissionPolicy(in *v1alpha1.ValidatingAdmissio
|
||||
}
|
||||
|
||||
func SetObjectDefaults_ValidatingAdmissionPolicyBinding(in *v1alpha1.ValidatingAdmissionPolicyBinding) {
|
||||
if in.Spec.ParamRef != nil {
|
||||
SetDefaults_ParamRef(in.Spec.ParamRef)
|
||||
}
|
||||
if in.Spec.MatchResources != nil {
|
||||
SetDefaults_MatchResources(in.Spec.MatchResources)
|
||||
for i := range in.Spec.MatchResources.ResourceRules {
|
||||
|
@@ -1151,9 +1151,38 @@ func validateParamRef(pr *admissionregistration.ParamRef, fldPath *field.Path) f
|
||||
if pr == nil {
|
||||
return allErrors
|
||||
}
|
||||
for _, msg := range path.ValidatePathSegmentName(pr.Name, false) {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("name"), pr.Name, msg))
|
||||
|
||||
if len(pr.Name) > 0 {
|
||||
for _, msg := range path.ValidatePathSegmentName(pr.Name, false) {
|
||||
allErrors = append(allErrors, field.Invalid(fldPath.Child("name"), pr.Name, msg))
|
||||
}
|
||||
|
||||
if pr.Selector != nil {
|
||||
allErrors = append(allErrors, field.Forbidden(fldPath.Child("name"), `name and selector are mutually exclusive`))
|
||||
}
|
||||
}
|
||||
|
||||
if pr.Selector != nil {
|
||||
labelSelectorValidationOpts := metav1validation.LabelSelectorValidationOptions{}
|
||||
allErrors = append(allErrors, metav1validation.ValidateLabelSelector(pr.Selector, labelSelectorValidationOpts, fldPath.Child("selector"))...)
|
||||
|
||||
if len(pr.Name) > 0 {
|
||||
allErrors = append(allErrors, field.Forbidden(fldPath.Child("selector"), `name and selector are mutually exclusive`))
|
||||
}
|
||||
}
|
||||
|
||||
if len(pr.Name) == 0 && pr.Selector == nil {
|
||||
allErrors = append(allErrors, field.Required(fldPath, `one of name or selector must be specified`))
|
||||
}
|
||||
|
||||
if pr.ParameterNotFoundAction == nil || len(*pr.ParameterNotFoundAction) == 0 {
|
||||
allErrors = append(allErrors, field.Required(fldPath.Child("parameterNotFoundAction"), ""))
|
||||
} else {
|
||||
if *pr.ParameterNotFoundAction != admissionregistration.DenyAction && *pr.ParameterNotFoundAction != admissionregistration.AllowAction {
|
||||
allErrors = append(allErrors, field.NotSupported(fldPath.Child("parameterNotFoundAction"), pr.ParameterNotFoundAction, []string{string(admissionregistration.DenyAction), string(admissionregistration.AllowAction)}))
|
||||
}
|
||||
}
|
||||
|
||||
return allErrors
|
||||
}
|
||||
|
||||
|
@@ -32,6 +32,8 @@ import (
|
||||
"k8s.io/kubernetes/pkg/apis/admissionregistration"
|
||||
)
|
||||
|
||||
func ptr[T any](v T) *T { return &v }
|
||||
|
||||
func strPtr(s string) *string { return &s }
|
||||
|
||||
func int32Ptr(i int32) *int32 { return &i }
|
||||
@@ -3517,7 +3519,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
},
|
||||
MatchResources: &admissionregistration.MatchResources{
|
||||
MatchPolicy: func() *admissionregistration.MatchPolicyType {
|
||||
@@ -3537,7 +3540,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
},
|
||||
MatchResources: &admissionregistration.MatchResources{
|
||||
ResourceRules: []admissionregistration.NamedRuleWithOperations{{
|
||||
@@ -3591,7 +3595,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
}, MatchResources: &admissionregistration.MatchResources{
|
||||
ResourceRules: []admissionregistration.NamedRuleWithOperations{{
|
||||
RuleWithOperations: admissionregistration.RuleWithOperations{
|
||||
@@ -3616,7 +3621,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
}, MatchResources: &admissionregistration.MatchResources{
|
||||
ResourceRules: []admissionregistration.NamedRuleWithOperations{{
|
||||
RuleWithOperations: admissionregistration.RuleWithOperations{
|
||||
@@ -3641,7 +3647,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
MatchResources: &admissionregistration.MatchResources{
|
||||
@@ -3668,7 +3675,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
MatchResources: &admissionregistration.MatchResources{
|
||||
@@ -3704,7 +3712,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
MatchResources: &admissionregistration.MatchResources{
|
||||
@@ -3731,7 +3740,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
MatchResources: &admissionregistration.MatchResources{
|
||||
@@ -3758,7 +3768,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
MatchResources: &admissionregistration.MatchResources{
|
||||
@@ -3794,7 +3805,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
MatchResources: &admissionregistration.MatchResources{
|
||||
@@ -3821,7 +3833,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
MatchResources: &admissionregistration.MatchResources{
|
||||
@@ -3848,7 +3861,8 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny, admissionregistration.Deny},
|
||||
},
|
||||
@@ -3863,12 +3877,80 @@ func TestValidateValidatingAdmissionPolicyBinding(t *testing.T) {
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.ValidationAction("illegal")},
|
||||
},
|
||||
},
|
||||
expectedError: `Unsupported value: "illegal": supported values: "Audit", "Deny", "Warn"`,
|
||||
}, {
|
||||
name: "paramRef selector must not be set when name is set",
|
||||
config: &admissionregistration.ValidatingAdmissionPolicyBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "config",
|
||||
},
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label": "value",
|
||||
},
|
||||
},
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: `spec.paramRef.name: Forbidden: name and selector are mutually exclusive, spec.paramRef.selector: Forbidden: name and selector are mutually exclusive`,
|
||||
}, {
|
||||
name: "paramRef parameterNotFoundAction must be set",
|
||||
config: &admissionregistration.ValidatingAdmissionPolicyBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "config",
|
||||
},
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: "spec.paramRef.parameterNotFoundAction: Required value",
|
||||
}, {
|
||||
name: "paramRef parameterNotFoundAction must be an valid value",
|
||||
config: &admissionregistration.ValidatingAdmissionPolicyBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "config",
|
||||
},
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
ParameterNotFoundAction: ptr(admissionregistration.ParameterNotFoundActionType("invalid")),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: `spec.paramRef.parameterNotFoundAction: Unsupported value: "invalid": supported values: "Deny", "Allow"`,
|
||||
}, {
|
||||
name: "paramRef one of name or selector",
|
||||
config: &admissionregistration.ValidatingAdmissionPolicyBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "config",
|
||||
},
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: `one of name or selector must be specified`,
|
||||
}}
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
@@ -3903,7 +3985,8 @@ func TestValidateValidatingAdmissionPolicyBindingUpdate(t *testing.T) {
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
MatchResources: &admissionregistration.MatchResources{
|
||||
@@ -3937,7 +4020,8 @@ func TestValidateValidatingAdmissionPolicyBindingUpdate(t *testing.T) {
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
MatchResources: &admissionregistration.MatchResources{
|
||||
@@ -3973,7 +4057,8 @@ func TestValidateValidatingAdmissionPolicyBindingUpdate(t *testing.T) {
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "xyzlimit-scale.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
Name: "xyzlimit-scale-setting.example.com",
|
||||
ParameterNotFoundAction: ptr(admissionregistration.DenyAction),
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
MatchResources: &admissionregistration.MatchResources{
|
||||
|
@@ -295,6 +295,16 @@ func (in *ParamKind) DeepCopy() *ParamKind {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ParamRef) DeepCopyInto(out *ParamRef) {
|
||||
*out = *in
|
||||
if in.Selector != nil {
|
||||
in, out := &in.Selector, &out.Selector
|
||||
*out = new(v1.LabelSelector)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ParameterNotFoundAction != nil {
|
||||
in, out := &in.ParameterNotFoundAction, &out.ParameterNotFoundAction
|
||||
*out = new(ParameterNotFoundActionType)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -502,7 +512,7 @@ func (in *ValidatingAdmissionPolicyBindingSpec) DeepCopyInto(out *ValidatingAdmi
|
||||
if in.ParamRef != nil {
|
||||
in, out := &in.ParamRef, &out.ParamRef
|
||||
*out = new(ParamRef)
|
||||
**out = **in
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.MatchResources != nil {
|
||||
in, out := &in.MatchResources, &out.MatchResources
|
||||
|
26
pkg/generated/openapi/zz_generated.openapi.go
generated
26
pkg/generated/openapi/zz_generated.openapi.go
generated
@@ -2269,23 +2269,37 @@ func schema_k8sio_api_admissionregistration_v1alpha1_ParamRef(ref common.Referen
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "ParamRef references a parameter resource",
|
||||
Description: "ParamRef describes how to locate the params to be used as input to expressions of rules applied by a policy binding.",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"name": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Name of the resource being referenced.",
|
||||
Description: "`name` is the name of the resource being referenced.\n\n`name` and `selector` are mutually exclusive properties. If one is set, the other must be unset.",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"namespace": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "Namespace of the referenced resource. Should be empty for the cluster-scoped resources",
|
||||
Description: "namespace is the namespace of the referenced resource. Allows limiting the search for params to a specific namespace. Applies to both `name` and `selector` fields.\n\nA per-namespace parameter may be used by specifying a namespace-scoped `paramKind` in the policy and leaving this field empty.\n\n- If `paramKind` is cluster-scoped, this field MUST be unset. Setting this field results in a configuration error.\n\n- If `paramKind` is namespace-scoped, the namespace of the object being evaluated for admission will be used when this field is left unset. Take care that if this is left empty the binding must not match any cluster-scoped resources, which will result in an error.",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
},
|
||||
},
|
||||
"selector": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "selector can be used to match multiple param objects based on their labels. Supply selector: {} to match all resources of the ParamKind.\n\nIf multiple params are found, they are all evaluated with the policy expressions and the results are ANDed together.\n\nOne of `name` or `selector` must be set, but `name` and `selector` are mutually exclusive properties. If one is set, the other must be unset.",
|
||||
Ref: ref("k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"),
|
||||
},
|
||||
},
|
||||
"parameterNotFoundAction": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "`parameterNotFoundAction` controls the behavior of the binding when the resource exists, and name or selector is valid, but there are no parameters matched by the binding. If the value is set to `Allow`, then no matched parameters will be treated as successful validation by the binding. If set to `Deny`, then no matched parameters will be subject to the `failurePolicy` of the policy.\n\nAllowed values are `Allow` or `Deny` Default to `Deny`\n\nPossible enum values:\n - `\"Allow\"` Ignore means that an error finding params for a binding is ignored\n - `\"Deny\"` Fail means that an error finding params for a binding is ignored",
|
||||
Type: []string{"string"},
|
||||
Format: "",
|
||||
Enum: []interface{}{"Allow", "Deny"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
VendorExtensible: spec.VendorExtensible{
|
||||
@@ -2294,6 +2308,8 @@ func schema_k8sio_api_admissionregistration_v1alpha1_ParamRef(ref common.Referen
|
||||
},
|
||||
},
|
||||
},
|
||||
Dependencies: []string{
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1.LabelSelector"},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2385,7 +2401,7 @@ func schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicyBi
|
||||
return common.OpenAPIDefinition{
|
||||
Schema: spec.Schema{
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources. ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.",
|
||||
Description: "ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources. ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.\n\nFor a given admission request, each binding will cause its policy to be evaluated N times, where N is 1 for policies/bindings that don't use params, otherwise N is the number of parameters selected by the binding.\n\nThe CEL expressions of a policy must have a computed CEL cost below the maximum CEL budget. Each evaluation of the policy is given an independent CEL cost budget. Adding/removing policies, bindings, or params can not affect whether a given (policy, binding, param) combination is within its own CEL budget.",
|
||||
Type: []string{"object"},
|
||||
Properties: map[string]spec.Schema{
|
||||
"kind": {
|
||||
@@ -2490,7 +2506,7 @@ func schema_k8sio_api_admissionregistration_v1alpha1_ValidatingAdmissionPolicyBi
|
||||
},
|
||||
"paramRef": {
|
||||
SchemaProps: spec.SchemaProps{
|
||||
Description: "ParamRef specifies the parameter resource used to configure the admission control policy. It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy. If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied.",
|
||||
Description: "paramRef specifies the parameter resource used to configure the admission control policy. It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy. If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied. If the policy does not specify a ParamKind then this field is ignored, and the rules are evaluated without a param.",
|
||||
Ref: ref("k8s.io/api/admissionregistration/v1alpha1.ParamRef"),
|
||||
},
|
||||
},
|
||||
|
@@ -1673,13 +1673,18 @@ func printValidatingAdmissionPolicyBinding(obj *admissionregistration.Validating
|
||||
Object: runtime.RawExtension{Object: obj},
|
||||
}
|
||||
paramName := "<unset>"
|
||||
if obj.Spec.ParamRef != nil {
|
||||
if obj.Spec.ParamRef.Namespace != "" {
|
||||
paramName = obj.Spec.ParamRef.Namespace + "/" + obj.Spec.ParamRef.Name
|
||||
} else {
|
||||
paramName = obj.Spec.ParamRef.Name
|
||||
if pr := obj.Spec.ParamRef; pr != nil {
|
||||
if len(pr.Name) > 0 {
|
||||
if pr.Namespace != "" {
|
||||
paramName = pr.Namespace + "/" + pr.Name
|
||||
} else {
|
||||
// Can't tell from here if param is cluster-scoped, so all
|
||||
// params without names get * namespace
|
||||
paramName = "*/" + pr.Name
|
||||
}
|
||||
} else if pr.Selector != nil {
|
||||
paramName = pr.Selector.String()
|
||||
}
|
||||
|
||||
}
|
||||
row.Cells = append(row.Cells, obj.Name, obj.Spec.PolicyName, paramName, translateTimestampSince(obj.CreationTimestamp))
|
||||
return []metav1.TableRow{row}, nil
|
||||
|
@@ -85,15 +85,21 @@ func (v *validatingAdmissionPolicyBindingStrategy) authorize(ctx context.Context
|
||||
}
|
||||
}
|
||||
|
||||
paramRef := binding.Spec.ParamRef
|
||||
var attrs authorizer.AttributesRecord
|
||||
|
||||
// require that the user can read (verb "get") the referred resource.
|
||||
attrs := authorizer.AttributesRecord{
|
||||
paramRef := binding.Spec.ParamRef
|
||||
verb := "get"
|
||||
|
||||
if len(paramRef.Name) == 0 {
|
||||
verb = "list"
|
||||
}
|
||||
|
||||
attrs = authorizer.AttributesRecord{
|
||||
User: user,
|
||||
Verb: "get",
|
||||
Verb: verb,
|
||||
ResourceRequest: true,
|
||||
Name: paramRef.Name,
|
||||
Namespace: paramRef.Namespace,
|
||||
Namespace: paramRef.Namespace, // if empty, no namespace indicates get across all namespaces
|
||||
APIGroup: apiGroup,
|
||||
APIVersion: apiVersion,
|
||||
Resource: resource,
|
||||
@@ -104,7 +110,8 @@ func (v *validatingAdmissionPolicyBindingStrategy) authorize(ctx context.Context
|
||||
return err
|
||||
}
|
||||
if d != authorizer.DecisionAllow {
|
||||
return fmt.Errorf(`user %v does not have "get" permission on the object referenced by paramRef`, user)
|
||||
return fmt.Errorf(`user %v does not have "%v" permission on the object referenced by paramRef`, verb, user)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@@ -103,19 +103,46 @@ func TestAuthorization(t *testing.T) {
|
||||
strategy := NewStrategy(tc.auth, tc.policyGetter, tc.resourceResolver)
|
||||
t.Run("create", func(t *testing.T) {
|
||||
ctx := request.WithUser(context.Background(), tc.userInfo)
|
||||
errs := strategy.Validate(ctx, validPolicyBinding())
|
||||
if len(errs) > 0 != tc.expectErr {
|
||||
t.Errorf("expected error: %v but got error: %v", tc.expectErr, errs)
|
||||
for _, obj := range validPolicyBindings() {
|
||||
errs := strategy.Validate(ctx, obj)
|
||||
if len(errs) > 0 != tc.expectErr {
|
||||
t.Errorf("expected error: %v but got error: %v", tc.expectErr, errs)
|
||||
}
|
||||
}
|
||||
})
|
||||
t.Run("update", func(t *testing.T) {
|
||||
ctx := request.WithUser(context.Background(), tc.userInfo)
|
||||
obj := validPolicyBinding()
|
||||
objWithChangedParamRef := obj.DeepCopy()
|
||||
objWithChangedParamRef.Spec.ParamRef.Name = "changed"
|
||||
errs := strategy.ValidateUpdate(ctx, obj, objWithChangedParamRef)
|
||||
if len(errs) > 0 != tc.expectErr {
|
||||
t.Errorf("expected error: %v but got error: %v", tc.expectErr, errs)
|
||||
for _, obj := range validPolicyBindings() {
|
||||
objWithChangedParamRef := obj.DeepCopy()
|
||||
if pr := objWithChangedParamRef.Spec.ParamRef; pr != nil {
|
||||
if len(pr.Name) > 0 {
|
||||
pr.Name = "changed"
|
||||
}
|
||||
|
||||
if pr.Selector != nil {
|
||||
pr.Selector = &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"changed": "value",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
if len(pr.Namespace) > 0 {
|
||||
pr.Namespace = "othernamespace"
|
||||
}
|
||||
|
||||
if pr.ParameterNotFoundAction == nil || *pr.ParameterNotFoundAction == admissionregistration.AllowAction {
|
||||
v := admissionregistration.DenyAction
|
||||
pr.ParameterNotFoundAction = &v
|
||||
} else {
|
||||
v := admissionregistration.AllowAction
|
||||
pr.ParameterNotFoundAction = &v
|
||||
}
|
||||
}
|
||||
errs := strategy.ValidateUpdate(ctx, obj, objWithChangedParamRef)
|
||||
if len(errs) > 0 != tc.expectErr {
|
||||
t.Errorf("expected error: %v but got error: %v", tc.expectErr, errs)
|
||||
}
|
||||
}
|
||||
})
|
||||
})
|
||||
|
@@ -37,121 +37,175 @@ import (
|
||||
)
|
||||
|
||||
func TestCreate(t *testing.T) {
|
||||
storage, server := newInsecureStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
configuration := validPolicyBinding()
|
||||
test.TestCreate(
|
||||
// valid
|
||||
configuration,
|
||||
// invalid
|
||||
newPolicyBinding(""),
|
||||
)
|
||||
for _, configuration := range validPolicyBindings() {
|
||||
t.Run(configuration.Name, func(t *testing.T) {
|
||||
storage, server := newInsecureStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
|
||||
test.TestCreate(
|
||||
// valid
|
||||
configuration,
|
||||
// invalid
|
||||
newPolicyBinding(""),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUpdate(t *testing.T) {
|
||||
storage, server := newInsecureStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
|
||||
test.TestUpdate(
|
||||
// valid
|
||||
validPolicyBinding(),
|
||||
// updateFunc
|
||||
func(obj runtime.Object) runtime.Object {
|
||||
object := obj.(*admissionregistration.ValidatingAdmissionPolicyBinding)
|
||||
object.Labels = map[string]string{"c": "d"}
|
||||
return object
|
||||
},
|
||||
// invalid updateFunc
|
||||
func(obj runtime.Object) runtime.Object {
|
||||
object := obj.(*admissionregistration.ValidatingAdmissionPolicyBinding)
|
||||
object.Name = ""
|
||||
return object
|
||||
},
|
||||
)
|
||||
for _, b := range validPolicyBindings() {
|
||||
storage, server := newInsecureStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
t.Run(b.Name, func(t *testing.T) {
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
test.TestUpdate(
|
||||
// valid
|
||||
b,
|
||||
// updateFunc
|
||||
func(obj runtime.Object) runtime.Object {
|
||||
object := obj.(*admissionregistration.ValidatingAdmissionPolicyBinding)
|
||||
object.Labels = map[string]string{"c": "d"}
|
||||
return object
|
||||
},
|
||||
// invalid updateFunc
|
||||
func(obj runtime.Object) runtime.Object {
|
||||
object := obj.(*admissionregistration.ValidatingAdmissionPolicyBinding)
|
||||
object.Name = ""
|
||||
return object
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGet(t *testing.T) {
|
||||
storage, server := newInsecureStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
test.TestGet(validPolicyBinding())
|
||||
for _, b := range validPolicyBindings() {
|
||||
t.Run(b.Name, func(t *testing.T) {
|
||||
storage, server := newInsecureStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
test.TestGet(b)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestList(t *testing.T) {
|
||||
storage, server := newInsecureStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
test.TestList(validPolicyBinding())
|
||||
for _, b := range validPolicyBindings() {
|
||||
t.Run(b.Name, func(t *testing.T) {
|
||||
storage, server := newInsecureStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
test.TestList(b)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDelete(t *testing.T) {
|
||||
storage, server := newInsecureStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
test.TestDelete(validPolicyBinding())
|
||||
for _, b := range validPolicyBindings() {
|
||||
t.Run(b.Name, func(t *testing.T) {
|
||||
storage, server := newInsecureStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
test.TestDelete(b)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestWatch(t *testing.T) {
|
||||
storage, server := newInsecureStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
test.TestWatch(
|
||||
validPolicyBinding(),
|
||||
[]labels.Set{},
|
||||
[]labels.Set{
|
||||
{"hoo": "bar"},
|
||||
},
|
||||
[]fields.Set{
|
||||
{"metadata.name": "foo"},
|
||||
},
|
||||
[]fields.Set{
|
||||
{"metadata.name": "nomatch"},
|
||||
},
|
||||
)
|
||||
for _, b := range validPolicyBindings() {
|
||||
t.Run(b.Name, func(t *testing.T) {
|
||||
storage, server := newInsecureStorage(t)
|
||||
defer server.Terminate(t)
|
||||
defer storage.Store.DestroyFunc()
|
||||
test := genericregistrytest.New(t, storage.Store).ClusterScope()
|
||||
test.TestWatch(
|
||||
b,
|
||||
[]labels.Set{},
|
||||
[]labels.Set{
|
||||
{"hoo": "bar"},
|
||||
},
|
||||
[]fields.Set{
|
||||
{"metadata.name": b.Name},
|
||||
},
|
||||
[]fields.Set{
|
||||
{"metadata.name": "nomatch"},
|
||||
},
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func validPolicyBinding() *admissionregistration.ValidatingAdmissionPolicyBinding {
|
||||
return &admissionregistration.ValidatingAdmissionPolicyBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "replicalimit-policy.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "param-test",
|
||||
func validPolicyBindings() []*admissionregistration.ValidatingAdmissionPolicyBinding {
|
||||
denyAction := admissionregistration.DenyAction
|
||||
return []*admissionregistration.ValidatingAdmissionPolicyBinding{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
MatchResources: &admissionregistration.MatchResources{
|
||||
MatchPolicy: func() *admissionregistration.MatchPolicyType {
|
||||
r := admissionregistration.MatchPolicyType("Exact")
|
||||
return &r
|
||||
}(),
|
||||
ObjectSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"a": "b"},
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "replicalimit-policy.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "replica-limit-test.example.com",
|
||||
ParameterNotFoundAction: &denyAction,
|
||||
},
|
||||
NamespaceSelector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"a": "b"},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-clusterwide",
|
||||
},
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "replicalimit-policy.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "replica-limit-test.example.com",
|
||||
Namespace: "default",
|
||||
ParameterNotFoundAction: &denyAction,
|
||||
},
|
||||
ResourceRules: []admissionregistration.NamedRuleWithOperations{
|
||||
{
|
||||
RuleWithOperations: admissionregistration.RuleWithOperations{
|
||||
Operations: []admissionregistration.OperationType{"CREATE"},
|
||||
Rule: admissionregistration.Rule{
|
||||
APIGroups: []string{"a"},
|
||||
APIVersions: []string{"a"},
|
||||
Resources: []string{"a"},
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-selector",
|
||||
},
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "replicalimit-policy.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label": "value",
|
||||
},
|
||||
},
|
||||
ParameterNotFoundAction: &denyAction,
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-selector-clusterwide",
|
||||
},
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "replicalimit-policy.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Namespace: "mynamespace",
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label": "value",
|
||||
},
|
||||
},
|
||||
ParameterNotFoundAction: &denyAction,
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -166,7 +220,8 @@ func newPolicyBinding(name string) *admissionregistration.ValidatingAdmissionPol
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "replicalimit-policy.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "param-test",
|
||||
Name: "param-test",
|
||||
Namespace: "default",
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
MatchResources: &admissionregistration.MatchResources{},
|
||||
|
@@ -35,32 +35,87 @@ func TestPolicyBindingStrategy(t *testing.T) {
|
||||
t.Errorf("PolicyBinding should not allow create on update")
|
||||
}
|
||||
|
||||
configuration := validPolicyBinding()
|
||||
strategy.PrepareForCreate(ctx, configuration)
|
||||
errs := strategy.Validate(ctx, configuration)
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("Unexpected error validating %v", errs)
|
||||
}
|
||||
invalidConfiguration := &admissionregistration.ValidatingAdmissionPolicyBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: ""},
|
||||
}
|
||||
strategy.PrepareForUpdate(ctx, invalidConfiguration, configuration)
|
||||
errs = strategy.ValidateUpdate(ctx, invalidConfiguration, configuration)
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("Expected a validation error")
|
||||
for _, configuration := range validPolicyBindings() {
|
||||
strategy.PrepareForCreate(ctx, configuration)
|
||||
errs := strategy.Validate(ctx, configuration)
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("Unexpected error validating %v", errs)
|
||||
}
|
||||
invalidConfiguration := &admissionregistration.ValidatingAdmissionPolicyBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: ""},
|
||||
}
|
||||
strategy.PrepareForUpdate(ctx, invalidConfiguration, configuration)
|
||||
errs = strategy.ValidateUpdate(ctx, invalidConfiguration, configuration)
|
||||
if len(errs) == 0 {
|
||||
t.Errorf("Expected a validation error")
|
||||
}
|
||||
}
|
||||
}
|
||||
func validPolicyBinding() *admissionregistration.ValidatingAdmissionPolicyBinding {
|
||||
return &admissionregistration.ValidatingAdmissionPolicyBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "replicalimit-policy.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "replica-limit-test.example.com",
|
||||
|
||||
func validPolicyBindings() []*admissionregistration.ValidatingAdmissionPolicyBinding {
|
||||
denyAction := admissionregistration.DenyAction
|
||||
return []*admissionregistration.ValidatingAdmissionPolicyBinding{
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "replicalimit-policy.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "replica-limit-test.example.com",
|
||||
ParameterNotFoundAction: &denyAction,
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-clusterwide",
|
||||
},
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "replicalimit-policy.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Name: "replica-limit-test.example.com",
|
||||
Namespace: "default",
|
||||
ParameterNotFoundAction: &denyAction,
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-selector",
|
||||
},
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "replicalimit-policy.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label": "value",
|
||||
},
|
||||
},
|
||||
ParameterNotFoundAction: &denyAction,
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "foo-selector-clusterwide",
|
||||
},
|
||||
Spec: admissionregistration.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: "replicalimit-policy.example.com",
|
||||
ParamRef: &admissionregistration.ParamRef{
|
||||
Namespace: "mynamespace",
|
||||
Selector: &metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{
|
||||
"label": "value",
|
||||
},
|
||||
},
|
||||
ParameterNotFoundAction: &denyAction,
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
},
|
||||
ValidationActions: []admissionregistration.ValidationAction{admissionregistration.Deny},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@@ -546,99 +546,102 @@ func init() {
|
||||
}
|
||||
|
||||
var fileDescriptor_c3be8d256e3ae3cf = []byte{
|
||||
// 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,
|
||||
// 1509 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x58, 0xcb, 0x6f, 0x1b, 0x45,
|
||||
0x18, 0xcf, 0xc6, 0x6e, 0x12, 0x8f, 0xf3, 0xf2, 0xd0, 0x2a, 0x6e, 0xa0, 0xde, 0x68, 0x55, 0xa1,
|
||||
0x46, 0x82, 0x35, 0x49, 0x0b, 0x85, 0x0a, 0x09, 0x65, 0xfb, 0xa2, 0x8f, 0x3c, 0x34, 0x45, 0x89,
|
||||
0x84, 0x40, 0x62, 0xb2, 0x3b, 0x71, 0xa6, 0xf6, 0x3e, 0xd8, 0x59, 0x9b, 0x46, 0x20, 0x51, 0x89,
|
||||
0x0b, 0xdc, 0x38, 0x70, 0xe1, 0xca, 0x9f, 0xc0, 0x7f, 0xc0, 0xad, 0xc7, 0x1e, 0xcb, 0x01, 0x8b,
|
||||
0x9a, 0x0b, 0x7f, 0x01, 0x48, 0xb9, 0x80, 0x66, 0x76, 0xf6, 0x69, 0x9b, 0xd8, 0x25, 0x70, 0xf3,
|
||||
0x7c, 0x8f, 0xdf, 0xf7, 0x98, 0xef, 0xfb, 0xf6, 0x1b, 0x03, 0xd4, 0x7c, 0x9b, 0xe9, 0xd4, 0xad,
|
||||
0x37, 0xdb, 0xfb, 0xc4, 0x77, 0x48, 0x40, 0x58, 0xbd, 0x43, 0x1c, 0xcb, 0xf5, 0xeb, 0x92, 0x81,
|
||||
0x3d, 0x5a, 0xc7, 0x96, 0x4d, 0x19, 0xa3, 0xae, 0xe3, 0x93, 0x06, 0x65, 0x81, 0x8f, 0x03, 0xea,
|
||||
0x3a, 0xf5, 0xce, 0x1a, 0x6e, 0x79, 0x87, 0x78, 0xad, 0xde, 0x20, 0x0e, 0xf1, 0x71, 0x40, 0x2c,
|
||||
0xdd, 0xf3, 0xdd, 0xc0, 0x85, 0xab, 0xa1, 0xaa, 0x8e, 0x3d, 0xaa, 0x0f, 0x54, 0xd5, 0x23, 0xd5,
|
||||
0xe5, 0xd7, 0x1b, 0x34, 0x38, 0x6c, 0xef, 0xeb, 0xa6, 0x6b, 0xd7, 0x1b, 0x6e, 0xc3, 0xad, 0x0b,
|
||||
0x84, 0xfd, 0xf6, 0x81, 0x38, 0x89, 0x83, 0xf8, 0x15, 0x22, 0x2f, 0x5f, 0x1e, 0xc1, 0xa9, 0xbc,
|
||||
0x3b, 0xcb, 0x57, 0x12, 0x25, 0x1b, 0x9b, 0x87, 0xd4, 0x21, 0xfe, 0x51, 0xdd, 0x6b, 0x36, 0x38,
|
||||
0x81, 0xd5, 0x6d, 0x12, 0xe0, 0x41, 0x5a, 0xf5, 0x61, 0x5a, 0x7e, 0xdb, 0x09, 0xa8, 0x4d, 0xfa,
|
||||
0x14, 0xde, 0x3a, 0x49, 0x81, 0x99, 0x87, 0xc4, 0xc6, 0x79, 0x3d, 0x8d, 0x81, 0x85, 0x8d, 0xb6,
|
||||
0x45, 0x83, 0x0d, 0xc7, 0x71, 0x03, 0x11, 0x04, 0xbc, 0x00, 0x0a, 0x4d, 0x72, 0x54, 0x55, 0x56,
|
||||
0x94, 0x4b, 0x25, 0xa3, 0xfc, 0xa4, 0xab, 0x4e, 0xf4, 0xba, 0x6a, 0xe1, 0x1e, 0x39, 0x42, 0x9c,
|
||||
0x0e, 0x37, 0xc0, 0x42, 0x07, 0xb7, 0xda, 0xe4, 0xe6, 0x23, 0xcf, 0x27, 0x22, 0x05, 0xd5, 0x49,
|
||||
0x21, 0xba, 0x24, 0x45, 0x17, 0x76, 0xb3, 0x6c, 0x94, 0x97, 0xd7, 0x5a, 0xa0, 0x92, 0x9c, 0xf6,
|
||||
0xb0, 0xef, 0x50, 0xa7, 0x01, 0x5f, 0x03, 0x33, 0x07, 0x94, 0xb4, 0x2c, 0x44, 0x0e, 0x24, 0xe0,
|
||||
0xa2, 0x04, 0x9c, 0xb9, 0x25, 0xe9, 0x28, 0x96, 0x80, 0xab, 0x60, 0xfa, 0xb3, 0x50, 0xb1, 0x5a,
|
||||
0x10, 0xc2, 0x0b, 0x52, 0x78, 0x5a, 0xe2, 0xa1, 0x88, 0xaf, 0x1d, 0x80, 0xf9, 0x4d, 0x1c, 0x98,
|
||||
0x87, 0xd7, 0x5d, 0xc7, 0xa2, 0x22, 0xc2, 0x15, 0x50, 0x74, 0xb0, 0x4d, 0x64, 0x88, 0xb3, 0x52,
|
||||
0xb3, 0xb8, 0x85, 0x6d, 0x82, 0x04, 0x07, 0xae, 0x03, 0x40, 0xf2, 0xf1, 0x41, 0x29, 0x07, 0x52,
|
||||
0xa1, 0xa5, 0xa4, 0xb4, 0x9f, 0x8b, 0xd2, 0x10, 0x22, 0xcc, 0x6d, 0xfb, 0x26, 0x61, 0xf0, 0x11,
|
||||
0xa8, 0x70, 0x38, 0xe6, 0x61, 0x93, 0x3c, 0x20, 0x2d, 0x62, 0x06, 0xae, 0x2f, 0xac, 0x96, 0xd7,
|
||||
0x2f, 0xeb, 0x49, 0x9d, 0xc6, 0x37, 0xa6, 0x7b, 0xcd, 0x06, 0x27, 0x30, 0x9d, 0x17, 0x86, 0xde,
|
||||
0x59, 0xd3, 0xef, 0xe3, 0x7d, 0xd2, 0x8a, 0x54, 0x8d, 0x73, 0xbd, 0xae, 0x5a, 0xd9, 0xca, 0x23,
|
||||
0xa2, 0x7e, 0x23, 0xd0, 0x05, 0xf3, 0xee, 0xfe, 0x43, 0x62, 0x06, 0xb1, 0xd9, 0xc9, 0x17, 0x37,
|
||||
0x0b, 0x7b, 0x5d, 0x75, 0x7e, 0x3b, 0x03, 0x87, 0x72, 0xf0, 0xf0, 0x4b, 0x30, 0xe7, 0xcb, 0xb8,
|
||||
0x51, 0xbb, 0x45, 0x58, 0xb5, 0xb0, 0x52, 0xb8, 0x54, 0x5e, 0x37, 0xf4, 0x91, 0xdb, 0x51, 0xe7,
|
||||
0x81, 0x59, 0x5c, 0x79, 0x8f, 0x06, 0x87, 0xdb, 0x1e, 0x09, 0xf9, 0xcc, 0x38, 0x27, 0x13, 0x3f,
|
||||
0x87, 0xd2, 0x06, 0x50, 0xd6, 0x1e, 0xfc, 0x4e, 0x01, 0x67, 0xc9, 0x23, 0xb3, 0xd5, 0xb6, 0x48,
|
||||
0x46, 0xae, 0x5a, 0x3c, 0x35, 0x47, 0x5e, 0x91, 0x8e, 0x9c, 0xbd, 0x39, 0xc0, 0x0e, 0x1a, 0x68,
|
||||
0x1d, 0xde, 0x00, 0x65, 0x9b, 0x17, 0xc5, 0x8e, 0xdb, 0xa2, 0xe6, 0x51, 0x75, 0x5a, 0x94, 0x92,
|
||||
0xd6, 0xeb, 0xaa, 0xe5, 0xcd, 0x84, 0x7c, 0xdc, 0x55, 0x17, 0x52, 0xc7, 0x0f, 0x8e, 0x3c, 0x82,
|
||||
0xd2, 0x6a, 0xda, 0x33, 0x05, 0x2c, 0x0d, 0xf1, 0x0a, 0x5e, 0x4d, 0x32, 0x2f, 0x4a, 0xa3, 0xaa,
|
||||
0xac, 0x14, 0x2e, 0x95, 0x8c, 0x4a, 0x3a, 0x63, 0x82, 0x81, 0xb2, 0x72, 0xf0, 0x2b, 0x05, 0x40,
|
||||
0xbf, 0x0f, 0x4f, 0x16, 0xca, 0xd5, 0x51, 0xf2, 0xa5, 0x0f, 0x48, 0xd2, 0xb2, 0x4c, 0x12, 0xec,
|
||||
0xe7, 0xa1, 0x01, 0xe6, 0x34, 0x0c, 0x4a, 0x3b, 0xd8, 0xc7, 0xf6, 0x3d, 0xea, 0x58, 0xbc, 0xef,
|
||||
0xb0, 0x47, 0x77, 0x89, 0x2f, 0xfa, 0x4e, 0xc9, 0xf6, 0xdd, 0xc6, 0xce, 0x1d, 0xc9, 0x41, 0x29,
|
||||
0x29, 0xde, 0xcd, 0x4d, 0xea, 0x58, 0xb2, 0x4b, 0xe3, 0x6e, 0xe6, 0x78, 0x48, 0x70, 0xb4, 0x1f,
|
||||
0x27, 0xc1, 0x8c, 0xb0, 0xc1, 0x27, 0xc7, 0xc9, 0xcd, 0x5f, 0x07, 0xa5, 0xb8, 0xa1, 0x24, 0x6a,
|
||||
0x45, 0x8a, 0x95, 0xe2, 0xe6, 0x43, 0x89, 0x0c, 0xfc, 0x18, 0xcc, 0xb0, 0xa8, 0xcd, 0x0a, 0x2f,
|
||||
0xde, 0x66, 0xb3, 0x7c, 0xd6, 0xc5, 0x0d, 0x16, 0x43, 0xc2, 0x00, 0x2c, 0x79, 0xdc, 0x7b, 0x12,
|
||||
0x10, 0x7f, 0xcb, 0x0d, 0x6e, 0xb9, 0x6d, 0xc7, 0xda, 0x30, 0x79, 0xf6, 0xaa, 0x45, 0xe1, 0xdd,
|
||||
0xb5, 0x5e, 0x57, 0x5d, 0xda, 0x19, 0x2c, 0x72, 0xdc, 0x55, 0x5f, 0x1e, 0xc2, 0x12, 0x65, 0x36,
|
||||
0x0c, 0x5a, 0xfb, 0x5e, 0x01, 0xb3, 0x5c, 0xe2, 0xfa, 0x21, 0x31, 0x9b, 0x7c, 0x40, 0x7f, 0xad,
|
||||
0x00, 0x48, 0xf2, 0x63, 0x3b, 0xac, 0xb6, 0xf2, 0xfa, 0xbb, 0x63, 0xb4, 0x57, 0xdf, 0xec, 0x4f,
|
||||
0x6a, 0xa6, 0x8f, 0xc5, 0xd0, 0x00, 0x9b, 0xda, 0x2f, 0x93, 0xe0, 0xfc, 0x2e, 0x6e, 0x51, 0x0b,
|
||||
0x07, 0xd4, 0x69, 0x6c, 0x44, 0xe6, 0xc2, 0x66, 0x81, 0x9f, 0x80, 0x19, 0x9e, 0x60, 0x0b, 0x07,
|
||||
0x58, 0x0e, 0xdb, 0x37, 0x46, 0xbb, 0x8e, 0x70, 0xc4, 0x6d, 0x92, 0x00, 0x27, 0x45, 0x97, 0xd0,
|
||||
0x50, 0x8c, 0x0a, 0x1f, 0x82, 0x22, 0xf3, 0x88, 0x29, 0x5b, 0xe5, 0xfd, 0x31, 0x62, 0x1f, 0xea,
|
||||
0xf5, 0x03, 0x8f, 0x98, 0x49, 0x35, 0xf2, 0x13, 0x12, 0x36, 0xa0, 0x0f, 0xa6, 0x58, 0x80, 0x83,
|
||||
0x36, 0x93, 0xa5, 0x75, 0xf7, 0x54, 0xac, 0x09, 0x44, 0x63, 0x5e, 0xda, 0x9b, 0x0a, 0xcf, 0x48,
|
||||
0x5a, 0xd2, 0xfe, 0x54, 0xc0, 0xca, 0x50, 0x5d, 0x83, 0x3a, 0x16, 0xaf, 0x87, 0xff, 0x3e, 0xcd,
|
||||
0x9f, 0x66, 0xd2, 0xbc, 0x7d, 0x1a, 0x81, 0x4b, 0xe7, 0x87, 0x65, 0x5b, 0xfb, 0x43, 0x01, 0x17,
|
||||
0x4f, 0x52, 0xbe, 0x4f, 0x59, 0x00, 0x3f, 0xea, 0x8b, 0x5e, 0x1f, 0xb1, 0xe7, 0x29, 0x0b, 0x63,
|
||||
0x8f, 0xd7, 0x9b, 0x88, 0x92, 0x8a, 0xdc, 0x03, 0x67, 0x68, 0x40, 0x6c, 0x3e, 0x8c, 0x79, 0x77,
|
||||
0xdd, 0x3b, 0xc5, 0xd0, 0x8d, 0x39, 0x69, 0xf7, 0xcc, 0x1d, 0x6e, 0x01, 0x85, 0x86, 0xb4, 0x6f,
|
||||
0x0a, 0x27, 0x07, 0xce, 0xf3, 0xc4, 0x47, 0xb4, 0x27, 0x88, 0x5b, 0xc9, 0x14, 0x8d, 0xaf, 0x71,
|
||||
0x27, 0xe6, 0xa0, 0x94, 0x14, 0x1f, 0x90, 0x9e, 0x9c, 0xbf, 0x03, 0xf6, 0x90, 0x93, 0x22, 0x8a,
|
||||
0x46, 0x77, 0x38, 0x20, 0xa3, 0x13, 0x8a, 0x21, 0x61, 0x1b, 0xcc, 0xdb, 0x99, 0xc5, 0x4b, 0xb6,
|
||||
0xca, 0x3b, 0x63, 0x18, 0xc9, 0x6e, 0x6e, 0xe1, 0xca, 0x93, 0xa5, 0xa1, 0x9c, 0x11, 0xb8, 0x07,
|
||||
0x2a, 0x1d, 0x99, 0x31, 0xd7, 0x09, 0xa7, 0x66, 0xb8, 0x6d, 0x94, 0x8c, 0x55, 0xbe, 0xa8, 0xed,
|
||||
0xe6, 0x99, 0xc7, 0x5d, 0x75, 0x31, 0x4f, 0x44, 0xfd, 0x18, 0xda, 0xef, 0x0a, 0xb8, 0x30, 0xf4,
|
||||
0x2e, 0xfe, 0x87, 0xea, 0xa3, 0xd9, 0xea, 0xbb, 0x71, 0x2a, 0xd5, 0x37, 0xb8, 0xec, 0x7e, 0x98,
|
||||
0xfa, 0x87, 0x50, 0x45, 0xbd, 0x61, 0x50, 0xf2, 0xa2, 0xfd, 0x40, 0xc6, 0x7a, 0x65, 0xdc, 0xe2,
|
||||
0xe1, 0xba, 0xc6, 0x1c, 0xff, 0x7e, 0xc7, 0x47, 0x94, 0xa0, 0xc2, 0xcf, 0xc1, 0xa2, 0x2d, 0x5f,
|
||||
0x08, 0x1c, 0x80, 0x3a, 0x41, 0xb4, 0x05, 0xfd, 0x8b, 0x0a, 0x3a, 0xdb, 0xeb, 0xaa, 0x8b, 0x9b,
|
||||
0x39, 0x58, 0xd4, 0x67, 0x08, 0xb6, 0x40, 0x39, 0xa9, 0x80, 0x68, 0x6d, 0x7e, 0xf3, 0x05, 0x52,
|
||||
0xee, 0x3a, 0xc6, 0x4b, 0x32, 0xc7, 0xe5, 0x84, 0xc6, 0x50, 0x1a, 0x1e, 0xde, 0x07, 0x73, 0x07,
|
||||
0x98, 0xb6, 0xda, 0x3e, 0x91, 0x0b, 0x69, 0xb8, 0x41, 0xbc, 0xca, 0x97, 0xc5, 0x5b, 0x69, 0xc6,
|
||||
0x71, 0x57, 0xad, 0x64, 0x08, 0x62, 0x5b, 0xc8, 0x2a, 0xc3, 0xc7, 0x0a, 0x58, 0xc4, 0xd9, 0xe7,
|
||||
0x23, 0xab, 0x9e, 0x11, 0x11, 0x5c, 0x1b, 0x23, 0x82, 0xdc, 0x0b, 0xd4, 0xa8, 0xca, 0x30, 0x16,
|
||||
0x73, 0x0c, 0x86, 0xfa, 0xac, 0xc1, 0x2f, 0xc0, 0x82, 0x9d, 0x79, 0xdd, 0xb1, 0xea, 0x94, 0x70,
|
||||
0x60, 0xec, 0xab, 0x8b, 0x11, 0x92, 0x97, 0x6c, 0x96, 0xce, 0x50, 0xde, 0x14, 0xb4, 0x40, 0xa9,
|
||||
0x83, 0x7d, 0x8a, 0xf7, 0xf9, 0x43, 0x63, 0x5a, 0xd8, 0xbd, 0x3c, 0xd6, 0xd5, 0x85, 0xba, 0xc9,
|
||||
0x7e, 0x19, 0x51, 0x18, 0x4a, 0x80, 0xb5, 0x9f, 0x26, 0x81, 0x7a, 0xc2, 0xa7, 0x1c, 0xde, 0x05,
|
||||
0xd0, 0xdd, 0x67, 0xc4, 0xef, 0x10, 0xeb, 0x76, 0xf8, 0xc6, 0x8f, 0x36, 0xe8, 0x42, 0xb2, 0x5e,
|
||||
0x6d, 0xf7, 0x49, 0xa0, 0x01, 0x5a, 0xd0, 0x06, 0xb3, 0x41, 0x6a, 0xf3, 0x1b, 0xe7, 0x45, 0x20,
|
||||
0x03, 0x4b, 0x2f, 0x8e, 0xc6, 0x62, 0xaf, 0xab, 0x66, 0x56, 0x49, 0x94, 0x81, 0x87, 0x26, 0x00,
|
||||
0x66, 0x72, 0x7b, 0x61, 0x03, 0xd4, 0x47, 0x1b, 0x67, 0xc9, 0x9d, 0xc5, 0x9f, 0xa0, 0xd4, 0x75,
|
||||
0xa5, 0x60, 0xb5, 0xbf, 0x14, 0x00, 0x92, 0xae, 0x80, 0x17, 0x41, 0xea, 0x19, 0x2f, 0xbf, 0x62,
|
||||
0x45, 0x0e, 0x81, 0x52, 0x74, 0xb8, 0x0a, 0xa6, 0x6d, 0xc2, 0x18, 0x6e, 0x44, 0xef, 0x80, 0xf8,
|
||||
0x5f, 0x86, 0xcd, 0x90, 0x8c, 0x22, 0x3e, 0xdc, 0x03, 0x53, 0x3e, 0xc1, 0xcc, 0x75, 0xe4, 0xff,
|
||||
0x11, 0xef, 0xf1, 0xb5, 0x0a, 0x09, 0xca, 0x71, 0x57, 0x5d, 0x1b, 0xe5, 0x5f, 0x20, 0x5d, 0x6e,
|
||||
0x61, 0x42, 0x09, 0x49, 0x38, 0x78, 0x1b, 0x54, 0xa4, 0x8d, 0x94, 0xc3, 0x61, 0xd7, 0x9e, 0x97,
|
||||
0xde, 0x54, 0x36, 0xf3, 0x02, 0xa8, 0x5f, 0x47, 0xbb, 0x0b, 0x66, 0xa2, 0xea, 0x82, 0x55, 0x50,
|
||||
0x4c, 0x7d, 0xbe, 0xc3, 0xc0, 0x05, 0x25, 0x97, 0x98, 0xc9, 0xc1, 0x89, 0x31, 0xb6, 0x9f, 0x3c,
|
||||
0xaf, 0x4d, 0x3c, 0x7d, 0x5e, 0x9b, 0x78, 0xf6, 0xbc, 0x36, 0xf1, 0xb8, 0x57, 0x53, 0x9e, 0xf4,
|
||||
0x6a, 0xca, 0xd3, 0x5e, 0x4d, 0x79, 0xd6, 0xab, 0x29, 0xbf, 0xf6, 0x6a, 0xca, 0xb7, 0xbf, 0xd5,
|
||||
0x26, 0x3e, 0x5c, 0x1d, 0xf9, 0x5f, 0xbc, 0xbf, 0x03, 0x00, 0x00, 0xff, 0xff, 0xad, 0xe2, 0x61,
|
||||
0x96, 0x0a, 0x14, 0x00, 0x00,
|
||||
}
|
||||
|
||||
func (m *AuditAnnotation) Marshal() (dAtA []byte, err error) {
|
||||
@@ -917,6 +920,25 @@ func (m *ParamRef) MarshalToSizedBuffer(dAtA []byte) (int, error) {
|
||||
_ = i
|
||||
var l int
|
||||
_ = l
|
||||
if m.ParameterNotFoundAction != nil {
|
||||
i -= len(*m.ParameterNotFoundAction)
|
||||
copy(dAtA[i:], *m.ParameterNotFoundAction)
|
||||
i = encodeVarintGenerated(dAtA, i, uint64(len(*m.ParameterNotFoundAction)))
|
||||
i--
|
||||
dAtA[i] = 0x22
|
||||
}
|
||||
if m.Selector != nil {
|
||||
{
|
||||
size, err := m.Selector.MarshalToSizedBuffer(dAtA[:i])
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
i -= size
|
||||
i = encodeVarintGenerated(dAtA, i, uint64(size))
|
||||
}
|
||||
i--
|
||||
dAtA[i] = 0x1a
|
||||
}
|
||||
i -= len(m.Namespace)
|
||||
copy(dAtA[i:], m.Namespace)
|
||||
i = encodeVarintGenerated(dAtA, i, uint64(len(m.Namespace)))
|
||||
@@ -1581,6 +1603,14 @@ func (m *ParamRef) Size() (n int) {
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
l = len(m.Namespace)
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
if m.Selector != nil {
|
||||
l = m.Selector.Size()
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
}
|
||||
if m.ParameterNotFoundAction != nil {
|
||||
l = len(*m.ParameterNotFoundAction)
|
||||
n += 1 + l + sovGenerated(uint64(l))
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
@@ -1875,6 +1905,8 @@ func (this *ParamRef) String() string {
|
||||
s := strings.Join([]string{`&ParamRef{`,
|
||||
`Name:` + fmt.Sprintf("%v", this.Name) + `,`,
|
||||
`Namespace:` + fmt.Sprintf("%v", this.Namespace) + `,`,
|
||||
`Selector:` + strings.Replace(fmt.Sprintf("%v", this.Selector), "LabelSelector", "v1.LabelSelector", 1) + `,`,
|
||||
`ParameterNotFoundAction:` + valueToStringGenerated(this.ParameterNotFoundAction) + `,`,
|
||||
`}`,
|
||||
}, "")
|
||||
return s
|
||||
@@ -2934,6 +2966,75 @@ func (m *ParamRef) Unmarshal(dAtA []byte) error {
|
||||
}
|
||||
m.Namespace = string(dAtA[iNdEx:postIndex])
|
||||
iNdEx = postIndex
|
||||
case 3:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field Selector", 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
|
||||
}
|
||||
if m.Selector == nil {
|
||||
m.Selector = &v1.LabelSelector{}
|
||||
}
|
||||
if err := m.Selector.Unmarshal(dAtA[iNdEx:postIndex]); err != nil {
|
||||
return err
|
||||
}
|
||||
iNdEx = postIndex
|
||||
case 4:
|
||||
if wireType != 2 {
|
||||
return fmt.Errorf("proto: wrong wireType = %d for field ParameterNotFoundAction", 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
|
||||
}
|
||||
s := ParameterNotFoundActionType(dAtA[iNdEx:postIndex])
|
||||
m.ParameterNotFoundAction = &s
|
||||
iNdEx = postIndex
|
||||
default:
|
||||
iNdEx = preIndex
|
||||
skippy, err := skipGenerated(dAtA[iNdEx:])
|
||||
|
@@ -227,16 +227,59 @@ message ParamKind {
|
||||
optional string kind = 2;
|
||||
}
|
||||
|
||||
// ParamRef references a parameter resource
|
||||
// ParamRef describes how to locate the params to be used as input to
|
||||
// expressions of rules applied by a policy binding.
|
||||
// +structType=atomic
|
||||
message ParamRef {
|
||||
// Name of the resource being referenced.
|
||||
// `name` is the name of the resource being referenced.
|
||||
//
|
||||
// `name` and `selector` are mutually exclusive properties. If one is set,
|
||||
// the other must be unset.
|
||||
//
|
||||
// +optional
|
||||
optional string name = 1;
|
||||
|
||||
// Namespace of the referenced resource.
|
||||
// Should be empty for the cluster-scoped resources
|
||||
// namespace is the namespace of the referenced resource. Allows limiting
|
||||
// the search for params to a specific namespace. Applies to both `name` and
|
||||
// `selector` fields.
|
||||
//
|
||||
// A per-namespace parameter may be used by specifying a namespace-scoped
|
||||
// `paramKind` in the policy and leaving this field empty.
|
||||
//
|
||||
// - If `paramKind` is cluster-scoped, this field MUST be unset. Setting this
|
||||
// field results in a configuration error.
|
||||
//
|
||||
// - If `paramKind` is namespace-scoped, the namespace of the object being
|
||||
// evaluated for admission will be used when this field is left unset. Take
|
||||
// care that if this is left empty the binding must not match any cluster-scoped
|
||||
// resources, which will result in an error.
|
||||
//
|
||||
// +optional
|
||||
optional string namespace = 2;
|
||||
|
||||
// selector can be used to match multiple param objects based on their labels.
|
||||
// Supply selector: {} to match all resources of the ParamKind.
|
||||
//
|
||||
// If multiple params are found, they are all evaluated with the policy expressions
|
||||
// and the results are ANDed together.
|
||||
//
|
||||
// One of `name` or `selector` must be set, but `name` and `selector` are
|
||||
// mutually exclusive properties. If one is set, the other must be unset.
|
||||
//
|
||||
// +optional
|
||||
optional k8s.io.apimachinery.pkg.apis.meta.v1.LabelSelector selector = 3;
|
||||
|
||||
// `parameterNotFoundAction` controls the behavior of the binding when the resource
|
||||
// exists, and name or selector is valid, but there are no parameters
|
||||
// matched by the binding. If the value is set to `Allow`, then no
|
||||
// matched parameters will be treated as successful validation by the binding.
|
||||
// If set to `Deny`, then no matched parameters will be subject to the
|
||||
// `failurePolicy` of the policy.
|
||||
//
|
||||
// Allowed values are `Allow` or `Deny`
|
||||
// Default to `Deny`
|
||||
// +optional
|
||||
optional string parameterNotFoundAction = 4;
|
||||
}
|
||||
|
||||
// TypeChecking contains results of type checking the expressions in the
|
||||
@@ -267,6 +310,15 @@ message ValidatingAdmissionPolicy {
|
||||
|
||||
// ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources.
|
||||
// ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.
|
||||
//
|
||||
// For a given admission request, each binding will cause its policy to be
|
||||
// evaluated N times, where N is 1 for policies/bindings that don't use
|
||||
// params, otherwise N is the number of parameters selected by the binding.
|
||||
//
|
||||
// The CEL expressions of a policy must have a computed CEL cost below the maximum
|
||||
// CEL budget. Each evaluation of the policy is given an independent CEL cost budget.
|
||||
// Adding/removing policies, bindings, or params can not affect whether a
|
||||
// given (policy, binding, param) combination is within its own CEL budget.
|
||||
message ValidatingAdmissionPolicyBinding {
|
||||
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
|
||||
// +optional
|
||||
@@ -294,9 +346,10 @@ message ValidatingAdmissionPolicyBindingSpec {
|
||||
// Required.
|
||||
optional string policyName = 1;
|
||||
|
||||
// ParamRef specifies the parameter resource used to configure the admission control policy.
|
||||
// paramRef specifies the parameter resource used to configure the admission control policy.
|
||||
// It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy.
|
||||
// If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied.
|
||||
// If the policy does not specify a ParamKind then this field is ignored, and the rules are evaluated without a param.
|
||||
// +optional
|
||||
optional ParamRef paramRef = 2;
|
||||
|
||||
|
@@ -39,6 +39,18 @@ const (
|
||||
AllScopes ScopeType = v1.AllScopes
|
||||
)
|
||||
|
||||
// ParameterNotFoundActionType specifies a failure policy that defines how a binding
|
||||
// is evaluated when the param referred by its perNamespaceParamRef is not found.
|
||||
// +enum
|
||||
type ParameterNotFoundActionType string
|
||||
|
||||
const (
|
||||
// Ignore means that an error finding params for a binding is ignored
|
||||
AllowAction ParameterNotFoundActionType = "Allow"
|
||||
// Fail means that an error finding params for a binding is ignored
|
||||
DenyAction ParameterNotFoundActionType = "Deny"
|
||||
)
|
||||
|
||||
// FailurePolicyType specifies a failure policy that defines how unrecognized errors from the admission endpoint are handled.
|
||||
// +enum
|
||||
type FailurePolicyType string
|
||||
@@ -363,6 +375,15 @@ type AuditAnnotation struct {
|
||||
|
||||
// ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources.
|
||||
// ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.
|
||||
//
|
||||
// For a given admission request, each binding will cause its policy to be
|
||||
// evaluated N times, where N is 1 for policies/bindings that don't use
|
||||
// params, otherwise N is the number of parameters selected by the binding.
|
||||
//
|
||||
// The CEL expressions of a policy must have a computed CEL cost below the maximum
|
||||
// CEL budget. Each evaluation of the policy is given an independent CEL cost budget.
|
||||
// Adding/removing policies, bindings, or params can not affect whether a
|
||||
// given (policy, binding, param) combination is within its own CEL budget.
|
||||
type ValidatingAdmissionPolicyBinding struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
// Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.
|
||||
@@ -393,9 +414,10 @@ type ValidatingAdmissionPolicyBindingSpec struct {
|
||||
// Required.
|
||||
PolicyName string `json:"policyName,omitempty" protobuf:"bytes,1,rep,name=policyName"`
|
||||
|
||||
// ParamRef specifies the parameter resource used to configure the admission control policy.
|
||||
// paramRef specifies the parameter resource used to configure the admission control policy.
|
||||
// It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy.
|
||||
// If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied.
|
||||
// If the policy does not specify a ParamKind then this field is ignored, and the rules are evaluated without a param.
|
||||
// +optional
|
||||
ParamRef *ParamRef `json:"paramRef,omitempty" protobuf:"bytes,2,rep,name=paramRef"`
|
||||
|
||||
@@ -450,15 +472,59 @@ type ValidatingAdmissionPolicyBindingSpec struct {
|
||||
ValidationActions []ValidationAction `json:"validationActions,omitempty" protobuf:"bytes,4,rep,name=validationActions"`
|
||||
}
|
||||
|
||||
// ParamRef references a parameter resource
|
||||
// ParamRef describes how to locate the params to be used as input to
|
||||
// expressions of rules applied by a policy binding.
|
||||
// +structType=atomic
|
||||
type ParamRef struct {
|
||||
// Name of the resource being referenced.
|
||||
// `name` is the name of the resource being referenced.
|
||||
//
|
||||
// `name` and `selector` are mutually exclusive properties. If one is set,
|
||||
// the other must be unset.
|
||||
//
|
||||
// +optional
|
||||
Name string `json:"name,omitempty" protobuf:"bytes,1,rep,name=name"`
|
||||
// Namespace of the referenced resource.
|
||||
// Should be empty for the cluster-scoped resources
|
||||
|
||||
// namespace is the namespace of the referenced resource. Allows limiting
|
||||
// the search for params to a specific namespace. Applies to both `name` and
|
||||
// `selector` fields.
|
||||
//
|
||||
// A per-namespace parameter may be used by specifying a namespace-scoped
|
||||
// `paramKind` in the policy and leaving this field empty.
|
||||
//
|
||||
// - If `paramKind` is cluster-scoped, this field MUST be unset. Setting this
|
||||
// field results in a configuration error.
|
||||
//
|
||||
// - If `paramKind` is namespace-scoped, the namespace of the object being
|
||||
// evaluated for admission will be used when this field is left unset. Take
|
||||
// care that if this is left empty the binding must not match any cluster-scoped
|
||||
// resources, which will result in an error.
|
||||
//
|
||||
// +optional
|
||||
Namespace string `json:"namespace,omitempty" protobuf:"bytes,2,rep,name=namespace"`
|
||||
|
||||
// selector can be used to match multiple param objects based on their labels.
|
||||
// Supply selector: {} to match all resources of the ParamKind.
|
||||
//
|
||||
// If multiple params are found, they are all evaluated with the policy expressions
|
||||
// and the results are ANDed together.
|
||||
//
|
||||
// One of `name` or `selector` must be set, but `name` and `selector` are
|
||||
// mutually exclusive properties. If one is set, the other must be unset.
|
||||
//
|
||||
// +optional
|
||||
Selector *metav1.LabelSelector `json:"selector,omitempty" protobuf:"bytes,3,rep,name=selector"`
|
||||
|
||||
// `parameterNotFoundAction` controls the behavior of the binding when the resource
|
||||
// exists, and name or selector is valid, but there are no parameters
|
||||
// matched by the binding. If the value is set to `Allow`, then no
|
||||
// matched parameters will be treated as successful validation by the binding.
|
||||
// If set to `Deny`, then no matched parameters will be subject to the
|
||||
// `failurePolicy` of the policy.
|
||||
//
|
||||
// Allowed values are `Allow` or `Deny`
|
||||
// Default to `Deny`
|
||||
// +optional
|
||||
ParameterNotFoundAction *ParameterNotFoundActionType `json:"parameterNotFoundAction,omitempty" protobuf:"bytes,4,rep,name=parameterNotFoundAction"`
|
||||
}
|
||||
|
||||
// MatchResources decides whether to run the admission control policy on an object based
|
||||
|
@@ -80,9 +80,11 @@ func (ParamKind) SwaggerDoc() map[string]string {
|
||||
}
|
||||
|
||||
var map_ParamRef = map[string]string{
|
||||
"": "ParamRef references a parameter resource",
|
||||
"name": "Name of the resource being referenced.",
|
||||
"namespace": "Namespace of the referenced resource. Should be empty for the cluster-scoped resources",
|
||||
"": "ParamRef describes how to locate the params to be used as input to expressions of rules applied by a policy binding.",
|
||||
"name": "`name` is the name of the resource being referenced.\n\n`name` and `selector` are mutually exclusive properties. If one is set, the other must be unset.",
|
||||
"namespace": "namespace is the namespace of the referenced resource. Allows limiting the search for params to a specific namespace. Applies to both `name` and `selector` fields.\n\nA per-namespace parameter may be used by specifying a namespace-scoped `paramKind` in the policy and leaving this field empty.\n\n- If `paramKind` is cluster-scoped, this field MUST be unset. Setting this field results in a configuration error.\n\n- If `paramKind` is namespace-scoped, the namespace of the object being evaluated for admission will be used when this field is left unset. Take care that if this is left empty the binding must not match any cluster-scoped resources, which will result in an error.",
|
||||
"selector": "selector can be used to match multiple param objects based on their labels. Supply selector: {} to match all resources of the ParamKind.\n\nIf multiple params are found, they are all evaluated with the policy expressions and the results are ANDed together.\n\nOne of `name` or `selector` must be set, but `name` and `selector` are mutually exclusive properties. If one is set, the other must be unset.",
|
||||
"parameterNotFoundAction": "`parameterNotFoundAction` controls the behavior of the binding when the resource exists, and name or selector is valid, but there are no parameters matched by the binding. If the value is set to `Allow`, then no matched parameters will be treated as successful validation by the binding. If set to `Deny`, then no matched parameters will be subject to the `failurePolicy` of the policy.\n\nAllowed values are `Allow` or `Deny` Default to `Deny`",
|
||||
}
|
||||
|
||||
func (ParamRef) SwaggerDoc() map[string]string {
|
||||
@@ -110,7 +112,7 @@ func (ValidatingAdmissionPolicy) SwaggerDoc() map[string]string {
|
||||
}
|
||||
|
||||
var map_ValidatingAdmissionPolicyBinding = map[string]string{
|
||||
"": "ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources. ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.",
|
||||
"": "ValidatingAdmissionPolicyBinding binds the ValidatingAdmissionPolicy with paramerized resources. ValidatingAdmissionPolicyBinding and parameter CRDs together define how cluster administrators configure policies for clusters.\n\nFor a given admission request, each binding will cause its policy to be evaluated N times, where N is 1 for policies/bindings that don't use params, otherwise N is the number of parameters selected by the binding.\n\nThe CEL expressions of a policy must have a computed CEL cost below the maximum CEL budget. Each evaluation of the policy is given an independent CEL cost budget. Adding/removing policies, bindings, or params can not affect whether a given (policy, binding, param) combination is within its own CEL budget.",
|
||||
"metadata": "Standard object metadata; More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata.",
|
||||
"spec": "Specification of the desired behavior of the ValidatingAdmissionPolicyBinding.",
|
||||
}
|
||||
@@ -132,7 +134,7 @@ func (ValidatingAdmissionPolicyBindingList) SwaggerDoc() map[string]string {
|
||||
var map_ValidatingAdmissionPolicyBindingSpec = map[string]string{
|
||||
"": "ValidatingAdmissionPolicyBindingSpec is the specification of the ValidatingAdmissionPolicyBinding.",
|
||||
"policyName": "PolicyName references a ValidatingAdmissionPolicy name which the ValidatingAdmissionPolicyBinding binds to. If the referenced resource does not exist, this binding is considered invalid and will be ignored Required.",
|
||||
"paramRef": "ParamRef specifies the parameter resource used to configure the admission control policy. It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy. If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied.",
|
||||
"paramRef": "paramRef specifies the parameter resource used to configure the admission control policy. It should point to a resource of the type specified in ParamKind of the bound ValidatingAdmissionPolicy. If the policy specifies a ParamKind and the resource referred to by ParamRef does not exist, this binding is considered mis-configured and the FailurePolicy of the ValidatingAdmissionPolicy applied. If the policy does not specify a ParamKind then this field is ignored, and the rules are evaluated without a param.",
|
||||
"matchResources": "MatchResources declares what resources match this binding and will be validated by it. Note that this is intersected with the policy's matchConstraints, so only requests that are matched by the policy can be selected by this. If this is unset, all resources matched by the policy are validated by this binding When resourceRules is unset, it does not constrain resource matching. If a resource is matched by the other fields of this object, it will be validated. Note that this is differs from ValidatingAdmissionPolicy matchConstraints, where resourceRules are required.",
|
||||
"validationActions": "validationActions declares how Validations of the referenced ValidatingAdmissionPolicy are enforced. If a validation evaluates to false it is always enforced according to these actions.\n\nFailures defined by the ValidatingAdmissionPolicy's FailurePolicy are enforced according to these actions only if the FailurePolicy is set to Fail, otherwise the failures are ignored. This includes compilation errors, runtime errors and misconfigurations of the policy.\n\nvalidationActions is declared as a set of action values. Order does not matter. validationActions may not contain duplicates of the same action.\n\nThe supported actions values are:\n\n\"Deny\" specifies that a validation failure results in a denied request.\n\n\"Warn\" specifies that a validation failure is reported to the request client in HTTP Warning headers, with a warning code of 299. Warnings can be sent both for allowed or denied admission responses.\n\n\"Audit\" specifies that a validation failure is included in the published audit event for the request. The audit event will contain a `validation.policy.admission.k8s.io/validation_failure` audit annotation with a value containing the details of the validation failures, formatted as a JSON list of objects, each with the following fields: - message: The validation failure message string - policy: The resource name of the ValidatingAdmissionPolicy - binding: The resource name of the ValidatingAdmissionPolicyBinding - expressionIndex: The index of the failed validations in the ValidatingAdmissionPolicy - validationActions: The enforcement actions enacted for the validation failure Example audit annotation: `\"validation.policy.admission.k8s.io/validation_failure\": \"[{\"message\": \"Invalid value\", {\"policy\": \"policy.example.com\", {\"binding\": \"policybinding.example.com\", {\"expressionIndex\": \"1\", {\"validationActions\": [\"Audit\"]}]\"`\n\nClients should expect to handle additional values by ignoring any values not recognized.\n\n\"Deny\" and \"Warn\" may not be used together since this combination needlessly duplicates the validation failure both in the API response body and the HTTP warning headers.\n\nRequired.",
|
||||
}
|
||||
|
@@ -160,6 +160,16 @@ func (in *ParamKind) DeepCopy() *ParamKind {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ParamRef) DeepCopyInto(out *ParamRef) {
|
||||
*out = *in
|
||||
if in.Selector != nil {
|
||||
in, out := &in.Selector, &out.Selector
|
||||
*out = new(v1.LabelSelector)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.ParameterNotFoundAction != nil {
|
||||
in, out := &in.ParameterNotFoundAction, &out.ParameterNotFoundAction
|
||||
*out = new(ParameterNotFoundActionType)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -288,7 +298,7 @@ func (in *ValidatingAdmissionPolicyBindingSpec) DeepCopyInto(out *ValidatingAdmi
|
||||
if in.ParamRef != nil {
|
||||
in, out := &in.ParamRef, &out.ParamRef
|
||||
*out = new(ParamRef)
|
||||
**out = **in
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
if in.MatchResources != nil {
|
||||
in, out := &in.MatchResources, &out.MatchResources
|
||||
|
@@ -47,7 +47,22 @@
|
||||
"policyName": "policyNameValue",
|
||||
"paramRef": {
|
||||
"name": "nameValue",
|
||||
"namespace": "namespaceValue"
|
||||
"namespace": "namespaceValue",
|
||||
"selector": {
|
||||
"matchLabels": {
|
||||
"matchLabelsKey": "matchLabelsValue"
|
||||
},
|
||||
"matchExpressions": [
|
||||
{
|
||||
"key": "keyValue",
|
||||
"operator": "operatorValue",
|
||||
"values": [
|
||||
"valuesValue"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"parameterNotFoundAction": "parameterNotFoundActionValue"
|
||||
},
|
||||
"matchResources": {
|
||||
"namespaceSelector": {
|
||||
|
Binary file not shown.
@@ -78,6 +78,15 @@ spec:
|
||||
paramRef:
|
||||
name: nameValue
|
||||
namespace: namespaceValue
|
||||
parameterNotFoundAction: parameterNotFoundActionValue
|
||||
selector:
|
||||
matchExpressions:
|
||||
- key: keyValue
|
||||
operator: operatorValue
|
||||
values:
|
||||
- valuesValue
|
||||
matchLabels:
|
||||
matchLabelsKey: matchLabelsValue
|
||||
policyName: policyNameValue
|
||||
validationActions:
|
||||
- validationActionsValue
|
||||
|
@@ -36,6 +36,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
|
||||
"k8s.io/apimachinery/pkg/labels"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utiljson "k8s.io/apimachinery/pkg/util/json"
|
||||
@@ -70,6 +71,13 @@ var (
|
||||
Kind: paramsGVK.Kind + "List",
|
||||
}, &unstructured.UnstructuredList{})
|
||||
|
||||
res.AddKnownTypeWithName(clusterScopedParamsGVK, &unstructured.Unstructured{})
|
||||
res.AddKnownTypeWithName(schema.GroupVersionKind{
|
||||
Group: clusterScopedParamsGVK.Group,
|
||||
Version: clusterScopedParamsGVK.Version,
|
||||
Kind: clusterScopedParamsGVK.Kind + "List",
|
||||
}, &unstructured.UnstructuredList{})
|
||||
|
||||
if err := v1alpha1.AddToScheme(res); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -80,6 +88,13 @@ var (
|
||||
|
||||
return res
|
||||
}()
|
||||
|
||||
clusterScopedParamsGVK schema.GroupVersionKind = schema.GroupVersionKind{
|
||||
Group: "example.com",
|
||||
Version: "v1",
|
||||
Kind: "ClusterScopedParamsConfig",
|
||||
}
|
||||
|
||||
paramsGVK schema.GroupVersionKind = schema.GroupVersionKind{
|
||||
Group: "example.com",
|
||||
Version: "v1",
|
||||
@@ -95,6 +110,7 @@ var (
|
||||
})
|
||||
|
||||
res.Add(paramsGVK, meta.RESTScopeNamespace)
|
||||
res.Add(clusterScopedParamsGVK, meta.RESTScopeRoot)
|
||||
res.Add(definitionGVK, meta.RESTScopeRoot)
|
||||
res.Add(bindingGVK, meta.RESTScopeRoot)
|
||||
res.Add(v1.SchemeGroupVersion.WithKind("ConfigMap"), meta.RESTScopeNamespace)
|
||||
@@ -133,6 +149,7 @@ var (
|
||||
"kind": paramsGVK.Kind,
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "replicas-test.example.com",
|
||||
"namespace": "default",
|
||||
"resourceVersion": "1",
|
||||
},
|
||||
"maxReplicas": int64(3),
|
||||
@@ -149,6 +166,8 @@ var (
|
||||
ParamRef: &v1alpha1.ParamRef{
|
||||
Name: fakeParams.GetName(),
|
||||
Namespace: fakeParams.GetNamespace(),
|
||||
// fake object tracker does not populate defaults
|
||||
ParameterNotFoundAction: ptrTo(v1alpha1.DenyAction),
|
||||
},
|
||||
ValidationActions: []v1alpha1.ValidationAction{v1alpha1.Deny},
|
||||
},
|
||||
@@ -196,6 +215,40 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
func newParam(name, namespace string, labels map[string]string) *unstructured.Unstructured {
|
||||
if len(namespace) == 0 {
|
||||
namespace = metav1.NamespaceDefault
|
||||
}
|
||||
res := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": paramsGVK.GroupVersion().String(),
|
||||
"kind": paramsGVK.Kind,
|
||||
"metadata": map[string]interface{}{
|
||||
"name": name,
|
||||
"namespace": namespace,
|
||||
"resourceVersion": "1",
|
||||
},
|
||||
},
|
||||
}
|
||||
res.SetLabels(labels)
|
||||
return res
|
||||
}
|
||||
|
||||
func newClusterScopedParam(name string, labels map[string]string) *unstructured.Unstructured {
|
||||
res := &unstructured.Unstructured{
|
||||
Object: map[string]interface{}{
|
||||
"apiVersion": clusterScopedParamsGVK.GroupVersion().String(),
|
||||
"kind": clusterScopedParamsGVK.Kind,
|
||||
"metadata": map[string]interface{}{
|
||||
"name": name,
|
||||
"resourceVersion": "1",
|
||||
},
|
||||
},
|
||||
}
|
||||
res.SetLabels(labels)
|
||||
return res
|
||||
}
|
||||
|
||||
// Interface which has fake compile functionality for use in tests
|
||||
// So that we can test the controller without pulling in any CEL functionality
|
||||
type fakeCompiler struct {
|
||||
@@ -562,7 +615,14 @@ func (c *celAdmissionController) getCurrentObject(obj runtime.Object) (runtime.O
|
||||
}
|
||||
|
||||
// Param type. Just check informer for its GVK
|
||||
item, err := paramInformer.Get(accessor.GetName())
|
||||
var item runtime.Object
|
||||
var err error
|
||||
if namespace := accessor.GetNamespace(); len(namespace) > 0 {
|
||||
item, err = paramInformer.Namespaced(namespace).Get(accessor.GetName())
|
||||
} else {
|
||||
item, err = paramInformer.Get(accessor.GetName())
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if k8serrors.IsNotFound(err) {
|
||||
return nil, nil
|
||||
@@ -940,7 +1000,9 @@ func TestReconfigureBinding(t *testing.T) {
|
||||
"apiVersion": paramsGVK.GroupVersion().String(),
|
||||
"kind": paramsGVK.Kind,
|
||||
"metadata": map[string]interface{}{
|
||||
"name": "replicas-test2.example.com",
|
||||
"name": "replicas-test2.example.com",
|
||||
// fake object tracker does not populate missing namespace
|
||||
"namespace": "default",
|
||||
"resourceVersion": "2",
|
||||
},
|
||||
"maxReplicas": int64(35),
|
||||
@@ -976,8 +1038,9 @@ func TestReconfigureBinding(t *testing.T) {
|
||||
Spec: v1alpha1.ValidatingAdmissionPolicyBindingSpec{
|
||||
PolicyName: denyPolicy.Name,
|
||||
ParamRef: &v1alpha1.ParamRef{
|
||||
Name: fakeParams2.GetName(),
|
||||
Namespace: fakeParams2.GetNamespace(),
|
||||
Name: fakeParams2.GetName(),
|
||||
Namespace: fakeParams2.GetNamespace(),
|
||||
ParameterNotFoundAction: ptrTo(v1alpha1.DenyAction),
|
||||
},
|
||||
ValidationActions: []v1alpha1.ValidationAction{v1alpha1.Deny},
|
||||
},
|
||||
@@ -1019,7 +1082,7 @@ func TestReconfigureBinding(t *testing.T) {
|
||||
&admission.RuntimeObjectInterfaces{},
|
||||
)
|
||||
|
||||
require.ErrorContains(t, err, `failed to configure binding: replicas-test2.example.com not found`)
|
||||
require.ErrorContains(t, err, "no params found for policy binding with `Deny` parameterNotFoundAction")
|
||||
|
||||
// Add the missing params
|
||||
require.NoError(t, paramTracker.Add(fakeParams2))
|
||||
@@ -1275,10 +1338,80 @@ func TestInvalidParamSourceInstanceName(t *testing.T) {
|
||||
// expect the specific error to be that the param was not found, not that CRD
|
||||
// is not existing
|
||||
require.ErrorContains(t, err,
|
||||
`failed to configure binding: replicas-test.example.com not found`)
|
||||
"no params found for policy binding with `Deny` parameterNotFoundAction")
|
||||
require.Len(t, passedParams, 0)
|
||||
}
|
||||
|
||||
// Show that policy still gets evaluated with `nil` param if paramRef & namespaceParamRef
|
||||
// are both unset
|
||||
func TestEmptyParamRef(t *testing.T) {
|
||||
reset()
|
||||
testContext, testContextCancel := context.WithCancel(context.Background())
|
||||
defer testContextCancel()
|
||||
|
||||
compiler := &fakeCompiler{}
|
||||
validator := &fakeValidator{}
|
||||
matcher := &fakeMatcher{
|
||||
DefaultMatch: true,
|
||||
}
|
||||
|
||||
handler, _, tracker, controller := setupFakeTest(t, compiler, matcher)
|
||||
|
||||
datalock := sync.Mutex{}
|
||||
numCompiles := 0
|
||||
|
||||
compiler.RegisterDefinition(denyPolicy, func([]cel.ExpressionAccessor, cel.OptionalVariableDeclarations) cel.Filter {
|
||||
datalock.Lock()
|
||||
numCompiles += 1
|
||||
datalock.Unlock()
|
||||
|
||||
return &fakeFilter{
|
||||
keyId: denyPolicy.Spec.Validations[0].Expression,
|
||||
}
|
||||
})
|
||||
|
||||
validator.RegisterDefinition(denyPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
|
||||
// Versioned params must be nil to pass the test
|
||||
if versionedParams != nil {
|
||||
return ValidateResult{
|
||||
Decisions: []PolicyDecision{
|
||||
{
|
||||
Action: ActionAdmit,
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return ValidateResult{
|
||||
Decisions: []PolicyDecision{
|
||||
{
|
||||
Action: ActionDeny,
|
||||
Message: "Denied",
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
require.NoError(t, tracker.Create(definitionsGVR, denyPolicy, denyPolicy.Namespace))
|
||||
require.NoError(t, tracker.Create(bindingsGVR, denyBindingWithNoParamRef, denyBindingWithNoParamRef.Namespace))
|
||||
|
||||
// Wait for controller to reconcile given objects
|
||||
require.NoError(t,
|
||||
waitForReconcile(
|
||||
testContext, controller,
|
||||
denyBindingWithNoParamRef, denyPolicy))
|
||||
|
||||
err := handler.Validate(
|
||||
testContext,
|
||||
// Object is irrelevant/unchecked for this test. Just test that
|
||||
// the evaluator is executed, and returns a denial
|
||||
attributeRecord(nil, fakeParams, admission.Create),
|
||||
&admission.RuntimeObjectInterfaces{},
|
||||
)
|
||||
|
||||
require.ErrorContains(t, err, `Denied`)
|
||||
require.Equal(t, 1, numCompiles)
|
||||
}
|
||||
|
||||
// Shows that a definition with no param source works just fine, and has
|
||||
// nil params passed to its evaluator.
|
||||
//
|
||||
@@ -1574,7 +1707,7 @@ func TestNativeTypeParam(t *testing.T) {
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "replicas-test.example.com",
|
||||
Namespace: "",
|
||||
Namespace: "default",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Data: map[string]string{
|
||||
@@ -1807,6 +1940,615 @@ func TestAllValidationActions(t *testing.T) {
|
||||
require.ErrorContains(t, err, "I'm sorry Dave")
|
||||
}
|
||||
|
||||
func TestNamespaceParamRefName(t *testing.T) {
|
||||
reset()
|
||||
testContext, testContextCancel := context.WithCancel(context.Background())
|
||||
defer testContextCancel()
|
||||
|
||||
compiler := &fakeCompiler{}
|
||||
validator := &fakeValidator{}
|
||||
matcher := &fakeMatcher{
|
||||
DefaultMatch: true,
|
||||
}
|
||||
handler, _, tracker, controller := setupFakeTest(t, compiler, matcher)
|
||||
|
||||
compiles := atomic.Int64{}
|
||||
evaluations := atomic.Int64{}
|
||||
|
||||
// Use ConfigMap native-typed param
|
||||
nativeTypeParamPolicy := *denyPolicy
|
||||
nativeTypeParamPolicy.Spec.ParamKind = &v1alpha1.ParamKind{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
}
|
||||
|
||||
namespaceParamBinding := *denyBinding
|
||||
namespaceParamBinding.Spec.ParamRef = &v1alpha1.ParamRef{
|
||||
Name: "replicas-test.example.com",
|
||||
}
|
||||
|
||||
compiler.RegisterDefinition(&nativeTypeParamPolicy, func([]cel.ExpressionAccessor, cel.OptionalVariableDeclarations) cel.Filter {
|
||||
compiles.Add(1)
|
||||
|
||||
return &fakeFilter{
|
||||
keyId: nativeTypeParamPolicy.Spec.Validations[0].Expression,
|
||||
}
|
||||
})
|
||||
|
||||
lock := sync.Mutex{}
|
||||
observedParamNamespaces := []string{}
|
||||
validator.RegisterDefinition(&nativeTypeParamPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
|
||||
evaluations.Add(1)
|
||||
if p, ok := versionedParams.(*v1.ConfigMap); ok {
|
||||
observedParamNamespaces = append(observedParamNamespaces, p.Namespace)
|
||||
return ValidateResult{
|
||||
Decisions: []PolicyDecision{
|
||||
{
|
||||
Action: ActionDeny,
|
||||
Message: "correct type",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return ValidateResult{
|
||||
Decisions: []PolicyDecision{
|
||||
{
|
||||
Action: ActionDeny,
|
||||
Message: "Incorrect param type",
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
configMapParam := &v1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "replicas-test.example.com",
|
||||
Namespace: "default",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"coolkey": "default",
|
||||
},
|
||||
}
|
||||
configMapParam2 := &v1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "replicas-test.example.com",
|
||||
Namespace: "mynamespace",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"coolkey": "mynamespace",
|
||||
},
|
||||
}
|
||||
configMapParam3 := &v1.ConfigMap{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: "v1",
|
||||
Kind: "ConfigMap",
|
||||
},
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: "replicas-test.example.com",
|
||||
Namespace: "othernamespace",
|
||||
ResourceVersion: "1",
|
||||
},
|
||||
Data: map[string]string{
|
||||
"coolkey": "othernamespace",
|
||||
},
|
||||
}
|
||||
require.NoError(t, tracker.Create(definitionsGVR, &nativeTypeParamPolicy, nativeTypeParamPolicy.Namespace))
|
||||
require.NoError(t, tracker.Create(bindingsGVR, &namespaceParamBinding, namespaceParamBinding.Namespace))
|
||||
require.NoError(t, tracker.Add(configMapParam))
|
||||
require.NoError(t, tracker.Add(configMapParam2))
|
||||
require.NoError(t, tracker.Add(configMapParam3))
|
||||
|
||||
// Wait for controller to reconcile given objects
|
||||
require.NoError(t,
|
||||
waitForReconcile(
|
||||
testContext, controller,
|
||||
&namespaceParamBinding, &nativeTypeParamPolicy, configMapParam, configMapParam2, configMapParam3))
|
||||
|
||||
// Object is irrelevant/unchecked for this test. Just test that
|
||||
// the evaluator is executed with correct namespace, and returns admit
|
||||
// meaning the params passed was a configmap
|
||||
err := handler.Validate(
|
||||
testContext,
|
||||
attributeRecord(nil, configMapParam, admission.Create),
|
||||
&admission.RuntimeObjectInterfaces{},
|
||||
)
|
||||
|
||||
func() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
require.ErrorContains(t, err, "correct type")
|
||||
require.EqualValues(t, 1, compiles.Load())
|
||||
require.EqualValues(t, 1, evaluations.Load())
|
||||
}()
|
||||
|
||||
err = handler.Validate(
|
||||
testContext,
|
||||
attributeRecord(nil, configMapParam2, admission.Create),
|
||||
&admission.RuntimeObjectInterfaces{},
|
||||
)
|
||||
|
||||
func() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
require.ErrorContains(t, err, "correct type")
|
||||
require.EqualValues(t, 1, compiles.Load())
|
||||
require.EqualValues(t, 2, evaluations.Load())
|
||||
}()
|
||||
|
||||
err = handler.Validate(
|
||||
testContext,
|
||||
attributeRecord(nil, configMapParam3, admission.Create),
|
||||
&admission.RuntimeObjectInterfaces{},
|
||||
)
|
||||
|
||||
func() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
require.ErrorContains(t, err, "correct type")
|
||||
require.EqualValues(t, 1, compiles.Load())
|
||||
require.EqualValues(t, 3, evaluations.Load())
|
||||
}()
|
||||
|
||||
err = handler.Validate(
|
||||
testContext,
|
||||
attributeRecord(nil, configMapParam, admission.Create),
|
||||
&admission.RuntimeObjectInterfaces{},
|
||||
)
|
||||
|
||||
func() {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
require.ErrorContains(t, err, "correct type")
|
||||
require.EqualValues(t, []string{"default", "mynamespace", "othernamespace", "default"}, observedParamNamespaces)
|
||||
require.EqualValues(t, 1, compiles.Load())
|
||||
require.EqualValues(t, 4, evaluations.Load())
|
||||
}()
|
||||
}
|
||||
|
||||
func TestParamRef(t *testing.T) {
|
||||
for _, paramIsClusterScoped := range []bool{false, true} {
|
||||
for _, nameIsSet := range []bool{false, true} {
|
||||
for _, namespaceIsSet := range []bool{false, true} {
|
||||
if paramIsClusterScoped && namespaceIsSet {
|
||||
// Skip invalid configuration
|
||||
continue
|
||||
}
|
||||
|
||||
for _, selectorIsSet := range []bool{false, true} {
|
||||
if selectorIsSet && nameIsSet {
|
||||
// SKip invalid configuration
|
||||
continue
|
||||
}
|
||||
|
||||
for _, denyNotFound := range []bool{false, true} {
|
||||
|
||||
name := "ParamRef"
|
||||
|
||||
if paramIsClusterScoped {
|
||||
name = "ClusterScoped" + name
|
||||
}
|
||||
|
||||
if nameIsSet {
|
||||
name = name + "WithName"
|
||||
} else if selectorIsSet {
|
||||
name = name + "WithLabelSelector"
|
||||
} else {
|
||||
name = name + "WithEverythingSelector"
|
||||
}
|
||||
|
||||
if namespaceIsSet {
|
||||
name = name + "WithNamespace"
|
||||
}
|
||||
|
||||
if denyNotFound {
|
||||
name = name + "DenyNotFound"
|
||||
} else {
|
||||
name = name + "AllowNotFound"
|
||||
}
|
||||
|
||||
t.Run(name, func(t *testing.T) {
|
||||
// Test creating a policy with a cluster or namesapce-scoped param
|
||||
// and binding with the provided configuration. Test will ensure
|
||||
// that the provided configuration is capable of matching
|
||||
// params as expected, and not matching params when not expected.
|
||||
// Also ensures the NotFound setting works as expected with this particular
|
||||
// configuration of ParamRef when all the previously
|
||||
// matched params are deleted.
|
||||
testParamRefCase(t, paramIsClusterScoped, nameIsSet, namespaceIsSet, selectorIsSet, denyNotFound)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// testParamRefCase constructs a ParamRef and policy with appropriate ParamKind
|
||||
// for the given parameters, then constructs a scenario with several matching/non-matching params
|
||||
// of varying names, namespaces, labels.
|
||||
//
|
||||
// Test then selects subset of params that should match provided configuration
|
||||
// and ensuers those params are the only ones used.
|
||||
//
|
||||
// Also ensures NotFound action is enforced correctly by deleting all found
|
||||
// params and ensuring the Action is used.
|
||||
//
|
||||
// This test is not meant to test every possible scenario of matching/not matching:
|
||||
// only that each ParamRef CAN be evaluated correctly for both cluster scoped
|
||||
// and namespace-scoped request kinds, and that the failure action is correctly
|
||||
// applied.
|
||||
func testParamRefCase(t *testing.T, paramIsClusterScoped, nameIsSet, namespaceIsSet, selectorIsSet, denyNotFound bool) {
|
||||
// Create a cluster scoped and a namespace scoped CRD
|
||||
policy := *denyPolicy
|
||||
binding := *denyBinding
|
||||
binding.Spec.ParamRef = &v1alpha1.ParamRef{}
|
||||
paramRef := binding.Spec.ParamRef
|
||||
|
||||
shouldErrorOnClusterScopedRequests := !namespaceIsSet && !paramIsClusterScoped
|
||||
|
||||
matchingParamName := "replicas-test.example.com"
|
||||
matchingNamespace := "mynamespace"
|
||||
nonMatchingNamespace := "othernamespace"
|
||||
|
||||
matchingLabels := labels.Set{"doesitmatch": "yes"}
|
||||
nonmatchingLabels := labels.Set{"doesitmatch": "no"}
|
||||
otherNonmatchingLabels := labels.Set{"notaffiliated": "no"}
|
||||
|
||||
if paramIsClusterScoped {
|
||||
policy.Spec.ParamKind = &v1alpha1.ParamKind{
|
||||
APIVersion: clusterScopedParamsGVK.GroupVersion().String(),
|
||||
Kind: clusterScopedParamsGVK.Kind,
|
||||
}
|
||||
} else {
|
||||
policy.Spec.ParamKind = &v1alpha1.ParamKind{
|
||||
APIVersion: paramsGVK.GroupVersion().String(),
|
||||
Kind: paramsGVK.Kind,
|
||||
}
|
||||
}
|
||||
|
||||
if nameIsSet {
|
||||
paramRef.Name = matchingParamName
|
||||
} else if selectorIsSet {
|
||||
paramRef.Selector = metav1.SetAsLabelSelector(matchingLabels)
|
||||
} else {
|
||||
paramRef.Selector = &metav1.LabelSelector{}
|
||||
}
|
||||
|
||||
if namespaceIsSet {
|
||||
paramRef.Namespace = matchingNamespace
|
||||
}
|
||||
|
||||
if denyNotFound {
|
||||
paramRef.ParameterNotFoundAction = ptrTo(v1alpha1.DenyAction)
|
||||
} else {
|
||||
paramRef.ParameterNotFoundAction = ptrTo(v1alpha1.AllowAction)
|
||||
}
|
||||
|
||||
compiler := &fakeCompiler{}
|
||||
validator := &fakeValidator{}
|
||||
matcher := &fakeMatcher{
|
||||
DefaultMatch: true,
|
||||
}
|
||||
|
||||
var matchedParams []runtime.Object
|
||||
paramLock := sync.Mutex{}
|
||||
observeParam := func(p runtime.Object) {
|
||||
paramLock.Lock()
|
||||
defer paramLock.Unlock()
|
||||
matchedParams = append(matchedParams, p)
|
||||
}
|
||||
getAndResetObservedParams := func() []runtime.Object {
|
||||
paramLock.Lock()
|
||||
defer paramLock.Unlock()
|
||||
oldParams := matchedParams
|
||||
matchedParams = nil
|
||||
return oldParams
|
||||
}
|
||||
|
||||
compiler.RegisterDefinition(&policy, func(ea []cel.ExpressionAccessor, ovd cel.OptionalVariableDeclarations) cel.Filter {
|
||||
return &fakeFilter{
|
||||
keyId: policy.Spec.Validations[0].Expression,
|
||||
}
|
||||
})
|
||||
|
||||
validator.RegisterDefinition(&policy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
|
||||
observeParam(versionedParams)
|
||||
return ValidateResult{
|
||||
Decisions: []PolicyDecision{
|
||||
{
|
||||
Action: ActionDeny,
|
||||
Message: "Denied by policy",
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
handler, paramTracker, tracker, controller := setupFakeTest(t, compiler, matcher)
|
||||
|
||||
// Create library of params to try to fool the controller
|
||||
params := []*unstructured.Unstructured{
|
||||
newParam(matchingParamName, v1.NamespaceDefault, nonmatchingLabels),
|
||||
newParam(matchingParamName, matchingNamespace, nonmatchingLabels),
|
||||
newParam(matchingParamName, nonMatchingNamespace, nonmatchingLabels),
|
||||
|
||||
newParam(matchingParamName+"1", v1.NamespaceDefault, matchingLabels),
|
||||
newParam(matchingParamName+"1", matchingNamespace, matchingLabels),
|
||||
newParam(matchingParamName+"1", nonMatchingNamespace, matchingLabels),
|
||||
|
||||
newParam(matchingParamName+"2", v1.NamespaceDefault, otherNonmatchingLabels),
|
||||
newParam(matchingParamName+"2", matchingNamespace, otherNonmatchingLabels),
|
||||
newParam(matchingParamName+"2", nonMatchingNamespace, otherNonmatchingLabels),
|
||||
|
||||
newParam(matchingParamName+"3", v1.NamespaceDefault, otherNonmatchingLabels),
|
||||
newParam(matchingParamName+"3", matchingNamespace, matchingLabels),
|
||||
newParam(matchingParamName+"3", nonMatchingNamespace, matchingLabels),
|
||||
|
||||
newClusterScopedParam(matchingParamName, matchingLabels),
|
||||
newClusterScopedParam(matchingParamName+"1", nonmatchingLabels),
|
||||
newClusterScopedParam(matchingParamName+"2", otherNonmatchingLabels),
|
||||
newClusterScopedParam(matchingParamName+"3", matchingLabels),
|
||||
newClusterScopedParam(matchingParamName+"4", nonmatchingLabels),
|
||||
newClusterScopedParam(matchingParamName+"5", otherNonmatchingLabels),
|
||||
}
|
||||
|
||||
require.NoError(t, tracker.Create(definitionsGVR, &policy, policy.Namespace))
|
||||
require.NoError(t, tracker.Create(bindingsGVR, &binding, binding.Namespace))
|
||||
require.NoError(t, waitForReconcile(context.TODO(), controller, &policy, &binding))
|
||||
|
||||
for _, p := range params {
|
||||
paramTracker.Add(p)
|
||||
}
|
||||
|
||||
namespacedRequestObject := newParam("some param", nonMatchingNamespace, nil)
|
||||
clusterScopedRequestObject := newClusterScopedParam("other param", nil)
|
||||
|
||||
// Validate a namespaced object, and verify that the params being validated
|
||||
// are the ones we would expect
|
||||
var expectedParamsForNamespacedRequest []*unstructured.Unstructured
|
||||
for _, p := range params {
|
||||
if p.GetAPIVersion() != policy.Spec.ParamKind.APIVersion || p.GetKind() != policy.Spec.ParamKind.Kind {
|
||||
continue
|
||||
} else if len(paramRef.Name) > 0 && p.GetName() != paramRef.Name {
|
||||
continue
|
||||
} else if len(paramRef.Namespace) > 0 && p.GetNamespace() != paramRef.Namespace {
|
||||
continue
|
||||
}
|
||||
|
||||
if !paramIsClusterScoped {
|
||||
if len(paramRef.Namespace) == 0 && p.GetNamespace() != namespacedRequestObject.GetNamespace() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if paramRef.Selector != nil {
|
||||
ls := p.GetLabels()
|
||||
matched := true
|
||||
|
||||
for k, v := range paramRef.Selector.MatchLabels {
|
||||
if l, hasLabel := ls[k]; !hasLabel {
|
||||
matched = false
|
||||
break
|
||||
} else if l != v {
|
||||
matched = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Empty selector matches everything
|
||||
if len(paramRef.Selector.MatchExpressions) == 0 && len(paramRef.Selector.MatchLabels) == 0 {
|
||||
matched = true
|
||||
}
|
||||
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
expectedParamsForNamespacedRequest = append(expectedParamsForNamespacedRequest, p)
|
||||
require.NoError(t, waitForReconcile(context.TODO(), controller, p))
|
||||
}
|
||||
require.NotEmpty(t, expectedParamsForNamespacedRequest, "all test cases should match at least one param")
|
||||
require.ErrorContains(t, handler.Validate(context.TODO(), attributeRecord(nil, namespacedRequestObject, admission.Create), &admission.RuntimeObjectInterfaces{}), "Denied by policy")
|
||||
require.ElementsMatch(t, expectedParamsForNamespacedRequest, getAndResetObservedParams(), "should exactly match expected params")
|
||||
|
||||
// Validate a cluster-scoped object, and verify that the params being validated
|
||||
// are the ones we would expect
|
||||
var expectedParamsForClusterScopedRequest []*unstructured.Unstructured
|
||||
for _, p := range params {
|
||||
if shouldErrorOnClusterScopedRequests {
|
||||
continue
|
||||
} else if p.GetAPIVersion() != policy.Spec.ParamKind.APIVersion || p.GetKind() != policy.Spec.ParamKind.Kind {
|
||||
continue
|
||||
} else if len(paramRef.Name) > 0 && p.GetName() != paramRef.Name {
|
||||
continue
|
||||
} else if len(paramRef.Namespace) > 0 && p.GetNamespace() != paramRef.Namespace {
|
||||
continue
|
||||
} else if !paramIsClusterScoped && len(paramRef.Namespace) == 0 && p.GetNamespace() != v1.NamespaceDefault {
|
||||
continue
|
||||
}
|
||||
|
||||
if paramRef.Selector != nil {
|
||||
ls := p.GetLabels()
|
||||
matched := true
|
||||
for k, v := range paramRef.Selector.MatchLabels {
|
||||
if l, hasLabel := ls[k]; !hasLabel {
|
||||
matched = false
|
||||
break
|
||||
} else if l != v {
|
||||
matched = false
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Empty selector matches everything
|
||||
if len(paramRef.Selector.MatchExpressions) == 0 && len(paramRef.Selector.MatchLabels) == 0 {
|
||||
matched = true
|
||||
}
|
||||
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
expectedParamsForClusterScopedRequest = append(expectedParamsForClusterScopedRequest, p)
|
||||
require.NoError(t, waitForReconcile(context.TODO(), controller, p))
|
||||
}
|
||||
|
||||
err := handler.Validate(context.TODO(), attributeRecord(nil, clusterScopedRequestObject, admission.Create), &admission.RuntimeObjectInterfaces{})
|
||||
if shouldErrorOnClusterScopedRequests {
|
||||
// Cannot validate cliuster-scoped resources against a paramRef that sets namespace
|
||||
require.ErrorContains(t, err, "failed to configure binding: cannot use namespaced paramRef in policy binding that matches cluster-scoped resources")
|
||||
} else {
|
||||
require.NotEmpty(t, expectedParamsForClusterScopedRequest, "all test cases should match at least one param")
|
||||
require.ErrorContains(t, err, "Denied by policy")
|
||||
}
|
||||
require.ElementsMatch(t, expectedParamsForClusterScopedRequest, getAndResetObservedParams(), "should exactly match expected params")
|
||||
|
||||
// Remove all params matched by namespaced, and cluster-scoped validation.
|
||||
// Validate again to make sure NotFoundAction is respected
|
||||
var deleted []runtime.Object
|
||||
for _, p := range expectedParamsForNamespacedRequest {
|
||||
if paramIsClusterScoped {
|
||||
require.NoError(t, paramTracker.Delete(paramsGVK.GroupVersion().WithResource("clusterscopedparamsconfigs"), p.GetNamespace(), p.GetName()))
|
||||
} else {
|
||||
require.NoError(t, paramTracker.Delete(paramsGVK.GroupVersion().WithResource("paramsconfigs"), p.GetNamespace(), p.GetName()))
|
||||
}
|
||||
deleted = append(deleted, p)
|
||||
}
|
||||
|
||||
for _, p := range expectedParamsForClusterScopedRequest {
|
||||
// Tracker.Delete docs says it wont raise error for not found, but its implmenetation
|
||||
// pretty plainly does...
|
||||
rsrsc := "paramsconfigs"
|
||||
if paramIsClusterScoped {
|
||||
rsrsc = "clusterscopedparamsconfigs"
|
||||
}
|
||||
if err := paramTracker.Delete(paramsGVK.GroupVersion().WithResource(rsrsc), p.GetNamespace(), p.GetName()); err != nil && !k8serrors.IsNotFound(err) {
|
||||
require.NoError(t, err)
|
||||
deleted = append(deleted, p)
|
||||
}
|
||||
}
|
||||
require.NoError(t, waitForReconcileDeletion(context.TODO(), controller, deleted...))
|
||||
|
||||
controller.refreshPolicies()
|
||||
|
||||
// Check that NotFound is working correctly for both namespaeed & non-namespaced
|
||||
// request object
|
||||
err = handler.Validate(context.TODO(), attributeRecord(nil, namespacedRequestObject, admission.Create), &admission.RuntimeObjectInterfaces{})
|
||||
if denyNotFound {
|
||||
require.ErrorContains(t, err, "no params found for policy binding with `Deny` parameterNotFoundAction")
|
||||
} else {
|
||||
require.NoError(t, err, "Allow not found expects no error when no params found. Policy should have been skipped")
|
||||
}
|
||||
require.Empty(t, getAndResetObservedParams(), "policy should not have been evaluated")
|
||||
|
||||
err = handler.Validate(context.TODO(), attributeRecord(nil, clusterScopedRequestObject, admission.Create), &admission.RuntimeObjectInterfaces{})
|
||||
if shouldErrorOnClusterScopedRequests {
|
||||
require.ErrorContains(t, err, "failed to configure binding: cannot use namespaced paramRef in policy binding that matches cluster-scoped resources")
|
||||
|
||||
} else if denyNotFound {
|
||||
require.ErrorContains(t, err, "no params found for policy binding with `Deny` parameterNotFoundAction")
|
||||
} else {
|
||||
require.NoError(t, err, "Allow not found expects no error when no params found. Policy should have been skipped")
|
||||
}
|
||||
require.Empty(t, getAndResetObservedParams(), "policy should not have been evaluated")
|
||||
}
|
||||
|
||||
// If the ParamKind is ClusterScoped, and namespace param is used.
|
||||
// This is a Configuration Error of the policy
|
||||
func TestNamespaceParamRefClusterScopedParamError(t *testing.T) {
|
||||
reset()
|
||||
testContext, testContextCancel := context.WithCancel(context.Background())
|
||||
defer testContextCancel()
|
||||
|
||||
compiler := &fakeCompiler{}
|
||||
validator := &fakeValidator{}
|
||||
matcher := &fakeMatcher{
|
||||
DefaultMatch: true,
|
||||
}
|
||||
handler, _, tracker, controller := setupFakeTest(t, compiler, matcher)
|
||||
|
||||
compiles := atomic.Int64{}
|
||||
evaluations := atomic.Int64{}
|
||||
|
||||
// Use ValidatingAdmissionPolicy for param type since it is cluster-scoped
|
||||
nativeTypeParamPolicy := *denyPolicy
|
||||
nativeTypeParamPolicy.Spec.ParamKind = &v1alpha1.ParamKind{
|
||||
APIVersion: "admissionregistration.k8s.io/v1alpha1",
|
||||
Kind: "ValidatingAdmissionPolicy",
|
||||
}
|
||||
|
||||
namespaceParamBinding := *denyBinding
|
||||
namespaceParamBinding.Spec.ParamRef = &v1alpha1.ParamRef{
|
||||
Name: "other-param-to-use-with-no-label.example.com",
|
||||
Namespace: "mynamespace",
|
||||
}
|
||||
|
||||
compiler.RegisterDefinition(&nativeTypeParamPolicy, func([]cel.ExpressionAccessor, cel.OptionalVariableDeclarations) cel.Filter {
|
||||
compiles.Add(1)
|
||||
|
||||
return &fakeFilter{
|
||||
keyId: nativeTypeParamPolicy.Spec.Validations[0].Expression,
|
||||
}
|
||||
})
|
||||
|
||||
validator.RegisterDefinition(&nativeTypeParamPolicy, func(ctx context.Context, versionedAttr *admission.VersionedAttributes, versionedParams runtime.Object, namespace *v1.Namespace, runtimeCELCostBudget int64, authz authorizer.Authorizer) ValidateResult {
|
||||
evaluations.Add(1)
|
||||
if _, ok := versionedParams.(*v1alpha1.ValidatingAdmissionPolicy); ok {
|
||||
return ValidateResult{
|
||||
Decisions: []PolicyDecision{
|
||||
{
|
||||
Action: ActionAdmit,
|
||||
Message: "correct type",
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
return ValidateResult{
|
||||
Decisions: []PolicyDecision{
|
||||
{
|
||||
Action: ActionDeny,
|
||||
Message: fmt.Sprintf("Incorrect param type %T", versionedParams),
|
||||
},
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
require.NoError(t, tracker.Create(definitionsGVR, &nativeTypeParamPolicy, nativeTypeParamPolicy.Namespace))
|
||||
require.NoError(t, tracker.Create(bindingsGVR, &namespaceParamBinding, namespaceParamBinding.Namespace))
|
||||
// Wait for controller to reconcile given objects
|
||||
require.NoError(t,
|
||||
waitForReconcile(
|
||||
testContext, controller,
|
||||
&namespaceParamBinding, &nativeTypeParamPolicy))
|
||||
|
||||
// Object is irrelevant/unchecked for this test. Just test that
|
||||
// the evaluator is executed with correct namespace, and returns admit
|
||||
// meaning the params passed was a configmap
|
||||
err := handler.Validate(
|
||||
testContext,
|
||||
attributeRecord(nil, fakeParams, admission.Create),
|
||||
&admission.RuntimeObjectInterfaces{},
|
||||
)
|
||||
|
||||
require.ErrorContains(t, err, "paramRef.namespace must not be provided for a cluster-scoped `paramKind`")
|
||||
require.EqualValues(t, 1, compiles.Load())
|
||||
require.EqualValues(t, 0, evaluations.Load())
|
||||
}
|
||||
|
||||
func TestAuditAnnotations(t *testing.T) {
|
||||
testContext, testContextCancel := context.WithCancel(context.Background())
|
||||
defer testContextCancel()
|
||||
|
@@ -31,6 +31,7 @@ import (
|
||||
"k8s.io/apimachinery/pkg/api/meta"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
utiljson "k8s.io/apimachinery/pkg/util/json"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/sets"
|
||||
@@ -72,8 +73,8 @@ type celAdmissionController struct {
|
||||
// against all of its registered bindings.
|
||||
type policyData struct {
|
||||
definitionInfo
|
||||
paramController generic.Controller[runtime.Object]
|
||||
bindings []bindingInfo
|
||||
paramInfo
|
||||
bindings []bindingInfo
|
||||
}
|
||||
|
||||
// contains the cel PolicyDecisions along with the ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding
|
||||
@@ -116,6 +117,9 @@ type paramInfo struct {
|
||||
// Function to call to stop the informer and clean up the controller
|
||||
stop func()
|
||||
|
||||
// Whether this param is cluster or namespace scoped
|
||||
scope meta.RESTScope
|
||||
|
||||
// Policy Definitions which refer to this param CRD
|
||||
dependentDefinitions sets.Set[namespacedName]
|
||||
}
|
||||
@@ -134,6 +138,7 @@ func NewAdmissionController(
|
||||
restMapper,
|
||||
client,
|
||||
dynamicClient,
|
||||
informerFactory,
|
||||
nil,
|
||||
NewMatcher(matching.NewMatcher(informerFactory.Core().V1().Namespaces().Lister(), client)),
|
||||
generic.NewInformer[*v1alpha1.ValidatingAdmissionPolicy](
|
||||
@@ -232,6 +237,12 @@ func (c *celAdmissionController) Validate(
|
||||
authz := newCachingAuthorizer(c.authz)
|
||||
|
||||
for _, definitionInfo := range policyDatas {
|
||||
// versionedAttributes will be set to non-nil inside of the loop, but
|
||||
// is scoped outside of the param loop so we only convert once. We defer
|
||||
// conversion so that it is only performed when we know a policy matches,
|
||||
// saving the cost of converting non-matching requests.
|
||||
var versionedAttr *admission.VersionedAttributes
|
||||
|
||||
definition := definitionInfo.lastReconciledValue
|
||||
matches, matchKind, err := c.policyController.matcher.DefinitionMatches(a, o, definition)
|
||||
if err != nil {
|
||||
@@ -263,63 +274,23 @@ func (c *celAdmissionController) Validate(
|
||||
continue
|
||||
}
|
||||
|
||||
var param runtime.Object
|
||||
|
||||
// versionedAttributes will be set to non-nil inside of the loop, but
|
||||
// is scoped outside of the param loop so we only convert once. We defer
|
||||
// conversion so that it is only performed when we know a policy matches,
|
||||
// saving the cost of converting non-matching requests.
|
||||
var versionedAttr *admission.VersionedAttributes
|
||||
|
||||
// If definition has paramKind, paramRef is required in binding.
|
||||
// If definition has no paramKind, paramRef set in binding will be ignored.
|
||||
paramKind := definition.Spec.ParamKind
|
||||
paramRef := binding.Spec.ParamRef
|
||||
if paramKind != nil && paramRef != nil {
|
||||
paramController := definitionInfo.paramController
|
||||
if paramController == nil {
|
||||
addConfigError(fmt.Errorf("paramKind kind `%v` not known",
|
||||
paramKind.String()), definition, binding)
|
||||
continue
|
||||
}
|
||||
|
||||
// If the param informer for this admission policy has not yet
|
||||
// had time to perform an initial listing, don't attempt to use
|
||||
// it.
|
||||
timeoutCtx, cancel := context.WithTimeout(c.policyController.context, 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if !cache.WaitForCacheSync(timeoutCtx.Done(), paramController.HasSynced) {
|
||||
addConfigError(fmt.Errorf("paramKind kind `%v` not yet synced to use for admission",
|
||||
paramKind.String()), definition, binding)
|
||||
continue
|
||||
}
|
||||
|
||||
if len(paramRef.Namespace) == 0 {
|
||||
param, err = paramController.Informer().Get(paramRef.Name)
|
||||
} else {
|
||||
param, err = paramController.Informer().Namespaced(paramRef.Namespace).Get(paramRef.Name)
|
||||
}
|
||||
|
||||
params, err := c.collectParams(definition.Spec.ParamKind, definitionInfo.paramInfo, binding.Spec.ParamRef, a.GetNamespace())
|
||||
if err != nil {
|
||||
addConfigError(err, definition, binding)
|
||||
continue
|
||||
} else if versionedAttr == nil && len(params) > 0 {
|
||||
// As optimization versionedAttr creation is deferred until
|
||||
// first use. Since > 0 params, we will validate
|
||||
va, err := admission.NewVersionedAttributes(a, matchKind, o)
|
||||
if err != nil {
|
||||
// Apply failure policy
|
||||
addConfigError(err, definition, binding)
|
||||
|
||||
if k8serrors.IsInvalid(err) {
|
||||
// Param mis-configured
|
||||
// require to set paramRef.namespace for namespaced resource and unset paramRef.namespace for cluster scoped resource
|
||||
continue
|
||||
} else if k8serrors.IsNotFound(err) {
|
||||
// Param not yet available. User may need to wait a bit
|
||||
// before being able to use it for validation.
|
||||
continue
|
||||
}
|
||||
|
||||
// There was a bad internal error
|
||||
utilruntime.HandleError(err)
|
||||
wrappedErr := fmt.Errorf("failed to convert object version: %w", err)
|
||||
addConfigError(wrappedErr, definition, binding)
|
||||
continue
|
||||
}
|
||||
versionedAttr = va
|
||||
}
|
||||
|
||||
var validationResults []ValidateResult
|
||||
var namespace *v1.Namespace
|
||||
namespaceName := a.GetNamespace()
|
||||
|
||||
@@ -338,72 +309,79 @@ func (c *celAdmissionController) Validate(
|
||||
}
|
||||
}
|
||||
|
||||
if versionedAttr == nil {
|
||||
va, err := admission.NewVersionedAttributes(a, matchKind, o)
|
||||
if err != nil {
|
||||
wrappedErr := fmt.Errorf("failed to convert object version: %w", err)
|
||||
addConfigError(wrappedErr, definition, binding)
|
||||
continue
|
||||
}
|
||||
versionedAttr = va
|
||||
}
|
||||
|
||||
validationResult := bindingInfo.validator.Validate(ctx, versionedAttr, param, namespace, celconfig.RuntimeCELCostBudget, authz)
|
||||
|
||||
for i, decision := range validationResult.Decisions {
|
||||
switch decision.Action {
|
||||
case ActionAdmit:
|
||||
if decision.Evaluation == EvalError {
|
||||
celmetrics.Metrics.ObserveAdmissionWithError(ctx, decision.Elapsed, definition.Name, binding.Name, "active")
|
||||
}
|
||||
case ActionDeny:
|
||||
for _, action := range binding.Spec.ValidationActions {
|
||||
switch action {
|
||||
case v1alpha1.Deny:
|
||||
deniedDecisions = append(deniedDecisions, policyDecisionWithMetadata{
|
||||
Definition: definition,
|
||||
Binding: binding,
|
||||
PolicyDecision: decision,
|
||||
})
|
||||
celmetrics.Metrics.ObserveRejection(ctx, decision.Elapsed, definition.Name, binding.Name, "active")
|
||||
case v1alpha1.Audit:
|
||||
c.publishValidationFailureAnnotation(binding, i, decision, versionedAttr)
|
||||
celmetrics.Metrics.ObserveAudit(ctx, decision.Elapsed, definition.Name, binding.Name, "active")
|
||||
case v1alpha1.Warn:
|
||||
warning.AddWarning(ctx, "", fmt.Sprintf("Validation failed for ValidatingAdmissionPolicy '%s' with binding '%s': %s", definition.Name, binding.Name, decision.Message))
|
||||
celmetrics.Metrics.ObserveWarn(ctx, decision.Elapsed, definition.Name, binding.Name, "active")
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unrecognized evaluation decision '%s' for ValidatingAdmissionPolicyBinding '%s' with ValidatingAdmissionPolicy '%s'",
|
||||
decision.Action, binding.Name, definition.Name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, auditAnnotation := range validationResult.AuditAnnotations {
|
||||
switch auditAnnotation.Action {
|
||||
case AuditAnnotationActionPublish:
|
||||
value := auditAnnotation.Value
|
||||
if len(auditAnnotation.Value) > maxAuditAnnotationValueLength {
|
||||
value = value[:maxAuditAnnotationValueLength]
|
||||
}
|
||||
auditAnnotationCollector.add(auditAnnotation.Key, value)
|
||||
case AuditAnnotationActionError:
|
||||
// When failurePolicy=fail, audit annotation errors result in deny
|
||||
deniedDecisions = append(deniedDecisions, policyDecisionWithMetadata{
|
||||
Definition: definition,
|
||||
Binding: binding,
|
||||
PolicyDecision: PolicyDecision{
|
||||
Action: ActionDeny,
|
||||
Evaluation: EvalError,
|
||||
Message: auditAnnotation.Error,
|
||||
Elapsed: auditAnnotation.Elapsed,
|
||||
for _, param := range params {
|
||||
var p runtime.Object = param
|
||||
if p != nil && p.GetObjectKind().GroupVersionKind().Empty() {
|
||||
// Make sure param has TypeMeta populated
|
||||
// This is a simple hack to make sure typeMeta is
|
||||
// available to CEL without making copies of objects, etc.
|
||||
p = &wrappedParam{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
APIVersion: definition.Spec.ParamKind.APIVersion,
|
||||
Kind: definition.Spec.ParamKind.Kind,
|
||||
},
|
||||
})
|
||||
celmetrics.Metrics.ObserveRejection(ctx, auditAnnotation.Elapsed, definition.Name, binding.Name, "active")
|
||||
case AuditAnnotationActionExclude: // skip it
|
||||
default:
|
||||
return fmt.Errorf("unsupported AuditAnnotation Action: %s", auditAnnotation.Action)
|
||||
nested: param,
|
||||
}
|
||||
}
|
||||
validationResults = append(validationResults, bindingInfo.validator.Validate(ctx, versionedAttr, p, namespace, celconfig.RuntimeCELCostBudget, authz))
|
||||
}
|
||||
|
||||
for _, validationResult := range validationResults {
|
||||
for i, decision := range validationResult.Decisions {
|
||||
switch decision.Action {
|
||||
case ActionAdmit:
|
||||
if decision.Evaluation == EvalError {
|
||||
celmetrics.Metrics.ObserveAdmissionWithError(ctx, decision.Elapsed, definition.Name, binding.Name, "active")
|
||||
}
|
||||
case ActionDeny:
|
||||
for _, action := range binding.Spec.ValidationActions {
|
||||
switch action {
|
||||
case v1alpha1.Deny:
|
||||
deniedDecisions = append(deniedDecisions, policyDecisionWithMetadata{
|
||||
Definition: definition,
|
||||
Binding: binding,
|
||||
PolicyDecision: decision,
|
||||
})
|
||||
celmetrics.Metrics.ObserveRejection(ctx, decision.Elapsed, definition.Name, binding.Name, "active")
|
||||
case v1alpha1.Audit:
|
||||
c.publishValidationFailureAnnotation(binding, i, decision, versionedAttr)
|
||||
celmetrics.Metrics.ObserveAudit(ctx, decision.Elapsed, definition.Name, binding.Name, "active")
|
||||
case v1alpha1.Warn:
|
||||
warning.AddWarning(ctx, "", fmt.Sprintf("Validation failed for ValidatingAdmissionPolicy '%s' with binding '%s': %s", definition.Name, binding.Name, decision.Message))
|
||||
celmetrics.Metrics.ObserveWarn(ctx, decision.Elapsed, definition.Name, binding.Name, "active")
|
||||
}
|
||||
}
|
||||
default:
|
||||
return fmt.Errorf("unrecognized evaluation decision '%s' for ValidatingAdmissionPolicyBinding '%s' with ValidatingAdmissionPolicy '%s'",
|
||||
decision.Action, binding.Name, definition.Name)
|
||||
}
|
||||
}
|
||||
|
||||
for _, auditAnnotation := range validationResult.AuditAnnotations {
|
||||
switch auditAnnotation.Action {
|
||||
case AuditAnnotationActionPublish:
|
||||
value := auditAnnotation.Value
|
||||
if len(auditAnnotation.Value) > maxAuditAnnotationValueLength {
|
||||
value = value[:maxAuditAnnotationValueLength]
|
||||
}
|
||||
auditAnnotationCollector.add(auditAnnotation.Key, value)
|
||||
case AuditAnnotationActionError:
|
||||
// When failurePolicy=fail, audit annotation errors result in deny
|
||||
deniedDecisions = append(deniedDecisions, policyDecisionWithMetadata{
|
||||
Definition: definition,
|
||||
Binding: binding,
|
||||
PolicyDecision: PolicyDecision{
|
||||
Action: ActionDeny,
|
||||
Evaluation: EvalError,
|
||||
Message: auditAnnotation.Error,
|
||||
Elapsed: auditAnnotation.Elapsed,
|
||||
},
|
||||
})
|
||||
celmetrics.Metrics.ObserveRejection(ctx, auditAnnotation.Elapsed, definition.Name, binding.Name, "active")
|
||||
case AuditAnnotationActionExclude: // skip it
|
||||
default:
|
||||
return fmt.Errorf("unsupported AuditAnnotation Action: %s", auditAnnotation.Action)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -432,6 +410,123 @@ func (c *celAdmissionController) Validate(
|
||||
return nil
|
||||
}
|
||||
|
||||
// Returns objects to use to evaluate the policy
|
||||
func (c *celAdmissionController) collectParams(
|
||||
paramKind *v1alpha1.ParamKind,
|
||||
info paramInfo,
|
||||
paramRef *v1alpha1.ParamRef,
|
||||
namespace string,
|
||||
) ([]runtime.Object, error) {
|
||||
// If definition has paramKind, paramRef is required in binding.
|
||||
// If definition has no paramKind, paramRef set in binding will be ignored.
|
||||
var params []runtime.Object
|
||||
var paramStore generic.NamespacedLister[runtime.Object]
|
||||
|
||||
// Make sure the param kind is ready to use
|
||||
if paramKind != nil && paramRef != nil {
|
||||
if info.controller == nil {
|
||||
return nil, fmt.Errorf("paramKind kind `%v` not known",
|
||||
paramKind.String())
|
||||
}
|
||||
|
||||
// Set up cluster-scoped, or namespaced access to the params
|
||||
// "default" if not provided, and paramKind is namespaced
|
||||
paramStore = info.controller.Informer()
|
||||
if info.scope.Name() == meta.RESTScopeNameNamespace {
|
||||
paramsNamespace := namespace
|
||||
if len(paramRef.Namespace) > 0 {
|
||||
paramsNamespace = paramRef.Namespace
|
||||
} else if len(paramsNamespace) == 0 {
|
||||
// You must supply namespace if your matcher can possibly
|
||||
// match a cluster-scoped resource
|
||||
return nil, fmt.Errorf("cannot use namespaced paramRef in policy binding that matches cluster-scoped resources")
|
||||
}
|
||||
|
||||
paramStore = info.controller.Informer().Namespaced(paramsNamespace)
|
||||
}
|
||||
|
||||
// If the param informer for this admission policy has not yet
|
||||
// had time to perform an initial listing, don't attempt to use
|
||||
// it.
|
||||
timeoutCtx, cancel := context.WithTimeout(c.policyController.context, 1*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if !cache.WaitForCacheSync(timeoutCtx.Done(), info.controller.HasSynced) {
|
||||
return nil, fmt.Errorf("paramKind kind `%v` not yet synced to use for admission",
|
||||
paramKind.String())
|
||||
}
|
||||
}
|
||||
|
||||
// Find params to use with policy
|
||||
switch {
|
||||
case paramKind == nil:
|
||||
// ParamKind is unset. Ignore any globalParamRef or namespaceParamRef
|
||||
// setting.
|
||||
return []runtime.Object{nil}, nil
|
||||
case paramRef == nil:
|
||||
// Policy ParamKind is set, but binding does not use it.
|
||||
// Validate with nil params
|
||||
return []runtime.Object{nil}, nil
|
||||
case len(paramRef.Namespace) > 0 && info.scope.Name() == meta.RESTScopeRoot.Name():
|
||||
// Not allowed to set namespace for cluster-scoped param
|
||||
return nil, fmt.Errorf("paramRef.namespace must not be provided for a cluster-scoped `paramKind`")
|
||||
|
||||
case len(paramRef.Name) > 0:
|
||||
if paramRef.Selector != nil {
|
||||
// This should be validated, but just in case.
|
||||
return nil, fmt.Errorf("paramRef.name and paramRef.selector are mutually exclusive")
|
||||
}
|
||||
|
||||
switch param, err := paramStore.Get(paramRef.Name); {
|
||||
case err == nil:
|
||||
params = []runtime.Object{param}
|
||||
case k8serrors.IsNotFound(err):
|
||||
// Param not yet available. User may need to wait a bit
|
||||
// before being able to use it for validation.
|
||||
//
|
||||
// Set params to nil to prepare for not found action
|
||||
params = nil
|
||||
case k8serrors.IsInvalid(err):
|
||||
// Param mis-configured
|
||||
// require to set namespace for namespaced resource
|
||||
// and unset namespace for cluster scoped resource
|
||||
return nil, err
|
||||
default:
|
||||
// Internal error
|
||||
utilruntime.HandleError(err)
|
||||
return nil, err
|
||||
}
|
||||
case paramRef.Selector != nil:
|
||||
// Select everything by default if empty name and selector
|
||||
selector, err := metav1.LabelSelectorAsSelector(paramRef.Selector)
|
||||
if err != nil {
|
||||
// Cannot parse label selector: configuration error
|
||||
return nil, err
|
||||
|
||||
}
|
||||
|
||||
paramList, err := paramStore.List(selector)
|
||||
if err != nil {
|
||||
// There was a bad internal error
|
||||
utilruntime.HandleError(err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Successfully grabbed params
|
||||
params = paramList
|
||||
default:
|
||||
// Should be unreachable due to validation
|
||||
return nil, fmt.Errorf("one of name or selector must be provided")
|
||||
}
|
||||
|
||||
// Apply fail action for params not found case
|
||||
if len(params) == 0 && paramRef.ParameterNotFoundAction != nil && *paramRef.ParameterNotFoundAction == v1alpha1.DenyAction {
|
||||
return nil, errors.New("no params found for policy binding with `Deny` parameterNotFoundAction")
|
||||
}
|
||||
|
||||
return params, nil
|
||||
}
|
||||
|
||||
func (c *celAdmissionController) publishValidationFailureAnnotation(binding *v1alpha1.ValidatingAdmissionPolicyBinding, expressionIndex int, decision PolicyDecision, attributes admission.Attributes) {
|
||||
key := "validation.policy.admission.k8s.io/validation_failure"
|
||||
// Marshal to a list of failures since, in the future, we may need to support multiple failures
|
||||
@@ -507,3 +602,48 @@ func (a auditAnnotationCollector) publish(policyName string, attributes admissio
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A workaround to fact that native types do not have TypeMeta populated, which
|
||||
// is needed for CEL expressions to be able to access the value.
|
||||
type wrappedParam struct {
|
||||
metav1.TypeMeta
|
||||
nested runtime.Object
|
||||
}
|
||||
|
||||
func (w *wrappedParam) MarshalJSON() ([]byte, error) {
|
||||
return nil, errors.New("MarshalJSON unimplemented for wrappedParam")
|
||||
}
|
||||
|
||||
func (w *wrappedParam) UnmarshalJSON(data []byte) error {
|
||||
return errors.New("UnmarshalJSON unimplemented for wrappedParam")
|
||||
}
|
||||
|
||||
func (w *wrappedParam) ToUnstructured() interface{} {
|
||||
res, err := runtime.DefaultUnstructuredConverter.ToUnstructured(w.nested)
|
||||
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
metaRes, err := runtime.DefaultUnstructuredConverter.ToUnstructured(&w.TypeMeta)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for k, v := range metaRes {
|
||||
res[k] = v
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func (w *wrappedParam) DeepCopyObject() runtime.Object {
|
||||
return &wrappedParam{
|
||||
TypeMeta: w.TypeMeta,
|
||||
nested: w.nested.DeepCopyObject(),
|
||||
}
|
||||
}
|
||||
|
||||
func (w *wrappedParam) GetObjectKind() schema.ObjectKind {
|
||||
return w
|
||||
}
|
||||
|
@@ -40,7 +40,6 @@ import (
|
||||
"k8s.io/client-go/dynamic/dynamicinformer"
|
||||
"k8s.io/client-go/informers"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
k8sscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"k8s.io/client-go/tools/cache"
|
||||
)
|
||||
|
||||
@@ -48,6 +47,7 @@ type policyController struct {
|
||||
once sync.Once
|
||||
context context.Context
|
||||
dynamicClient dynamic.Interface
|
||||
informerFactory informers.SharedInformerFactory
|
||||
restMapper meta.RESTMapper
|
||||
policyDefinitionsController generic.Controller[*v1alpha1.ValidatingAdmissionPolicy]
|
||||
policyBindingController generic.Controller[*v1alpha1.ValidatingAdmissionPolicyBinding]
|
||||
@@ -61,13 +61,10 @@ type policyController struct {
|
||||
|
||||
newValidator
|
||||
|
||||
// Lock which protects:
|
||||
// - cachedPolicies
|
||||
// - paramCRDControllers
|
||||
// - definitionInfo
|
||||
// - bindingInfos
|
||||
// - definitionsToBindings
|
||||
// All other fields should be assumed constant
|
||||
client kubernetes.Interface
|
||||
// Lock which protects
|
||||
// All Below fields
|
||||
// All above fields should be assumed constant
|
||||
mutex sync.RWMutex
|
||||
|
||||
cachedPolicies []policyData
|
||||
@@ -88,8 +85,6 @@ type policyController struct {
|
||||
// All keys must have at least one dependent binding
|
||||
// All binding names MUST exist as a key bindingInfos
|
||||
definitionsToBindings map[namespacedName]sets.Set[namespacedName]
|
||||
|
||||
client kubernetes.Interface
|
||||
}
|
||||
|
||||
type newValidator func(validationFilter cel.Filter, celMatcher matchconditions.Matcher, auditAnnotationFilter, messageFilter cel.Filter, failurePolicy *v1.FailurePolicyType) Validator
|
||||
@@ -98,6 +93,7 @@ func newPolicyController(
|
||||
restMapper meta.RESTMapper,
|
||||
client kubernetes.Interface,
|
||||
dynamicClient dynamic.Interface,
|
||||
informerFactory informers.SharedInformerFactory,
|
||||
filterCompiler cel.FilterCompiler,
|
||||
matcher Matcher,
|
||||
policiesInformer generic.Informer[*v1alpha1.ValidatingAdmissionPolicy],
|
||||
@@ -128,9 +124,10 @@ func newPolicyController(
|
||||
Name: "cel-policy-bindings",
|
||||
},
|
||||
),
|
||||
restMapper: restMapper,
|
||||
dynamicClient: dynamicClient,
|
||||
client: client,
|
||||
restMapper: restMapper,
|
||||
dynamicClient: dynamicClient,
|
||||
informerFactory: informerFactory,
|
||||
client: client,
|
||||
}
|
||||
return res
|
||||
}
|
||||
@@ -235,7 +232,6 @@ func (c *policyController) reconcilePolicyDefinitionSpec(namespace, name string,
|
||||
// Skip setting up controller for empty param type
|
||||
return nil
|
||||
}
|
||||
|
||||
// find GVR for params
|
||||
// Parse param source into a GVK
|
||||
|
||||
@@ -262,101 +258,75 @@ func (c *policyController) reconcilePolicyDefinitionSpec(namespace, name string,
|
||||
return info.configurationError
|
||||
}
|
||||
|
||||
paramInfo := c.ensureParamInfo(paramSource, paramsGVR)
|
||||
paramInfo.dependentDefinitions.Insert(nn)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Ensures that there is an informer started for the given GVK to be used as a
|
||||
// param
|
||||
func (c *policyController) ensureParamInfo(paramSource *v1alpha1.ParamKind, mapping *meta.RESTMapping) *paramInfo {
|
||||
if info, ok := c.paramsCRDControllers[*paramSource]; ok {
|
||||
// If a param controller is already active for this paramsource, make
|
||||
// sure it is tracking this policy's dependency upon it
|
||||
info.dependentDefinitions.Insert(nn)
|
||||
return info
|
||||
}
|
||||
|
||||
// We are not watching this param. Start an informer for it.
|
||||
instanceContext, instanceCancel := context.WithCancel(c.context)
|
||||
|
||||
var informer cache.SharedIndexInformer
|
||||
|
||||
// Try to see if our provided informer factory has an informer for this type.
|
||||
// We assume the informer is already started, and starts all types associated
|
||||
// with it.
|
||||
if genericInformer, err := c.informerFactory.ForResource(mapping.Resource); err == nil {
|
||||
informer = genericInformer.Informer()
|
||||
|
||||
// Ensure the informer is started
|
||||
// Use policyController's context rather than the instance context.
|
||||
// PolicyController context is expected to last until app shutdown
|
||||
// This is due to behavior of informerFactory which would cause the
|
||||
// informer to stop running once the context is cancelled, and
|
||||
// never started again.
|
||||
c.informerFactory.Start(c.context.Done())
|
||||
} else {
|
||||
instanceContext, instanceCancel := context.WithCancel(c.context)
|
||||
|
||||
var informer cache.SharedIndexInformer
|
||||
|
||||
// Informer Factory is optional
|
||||
if c.client != nil {
|
||||
// Create temporary informer factory
|
||||
// Cannot use the k8s shared informer factory for dynamic params informer.
|
||||
// Would leak unnecessary informers when we are done since we would have to
|
||||
// call informerFactory.Start() with a longer-lived stopCh than necessary.
|
||||
// SharedInformerFactory does not support temporary usage.
|
||||
dynamicFactory := informers.NewSharedInformerFactory(c.client, 10*time.Minute)
|
||||
|
||||
// Look for a typed informer. If it does not exist
|
||||
genericInformer, err := dynamicFactory.ForResource(paramsGVR.Resource)
|
||||
|
||||
// Ignore error. We fallback to dynamic informer if there is no
|
||||
// typed informer
|
||||
if err != nil {
|
||||
informer = nil
|
||||
} else {
|
||||
informer = genericInformer.Informer()
|
||||
|
||||
// Set transformer on the informer to workaround inconsistency
|
||||
// where typed objects have TypeMeta wiped out but dynamic
|
||||
// objects keep kind/apiVersion fields
|
||||
informer.SetTransform(func(i interface{}) (interface{}, error) {
|
||||
// Ensure param is populated with its GVK for consistency
|
||||
// (CRD dynamic informer always returns objects with kind/apiversion,
|
||||
// but native types do not include populated TypeMeta.
|
||||
if param := i.(runtime.Object); param != nil {
|
||||
if param.GetObjectKind().GroupVersionKind().Empty() {
|
||||
// https://github.com/kubernetes/client-go/issues/413#issue-324586398
|
||||
gvks, _, _ := k8sscheme.Scheme.ObjectKinds(param)
|
||||
for _, gvk := range gvks {
|
||||
if len(gvk.Kind) == 0 {
|
||||
continue
|
||||
}
|
||||
if len(gvk.Version) == 0 || gvk.Version == runtime.APIVersionInternal {
|
||||
continue
|
||||
}
|
||||
param.GetObjectKind().SetGroupVersionKind(gvk)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return i, nil
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
if informer == nil {
|
||||
// Dynamic JSON informer fallback.
|
||||
// Cannot use shared dynamic informer since it would be impossible
|
||||
// to clean CRD informers properly with multiple dependents
|
||||
// (cannot start ahead of time, and cannot track dependencies via stopCh)
|
||||
informer = dynamicinformer.NewFilteredDynamicInformer(
|
||||
c.dynamicClient,
|
||||
paramsGVR.Resource,
|
||||
corev1.NamespaceAll,
|
||||
// Use same interval as is used for k8s typed sharedInformerFactory
|
||||
// https://github.com/kubernetes/kubernetes/blob/7e0923899fed622efbc8679cca6b000d43633e38/cmd/kube-apiserver/app/server.go#L430
|
||||
10*time.Minute,
|
||||
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
|
||||
nil,
|
||||
).Informer()
|
||||
}
|
||||
|
||||
controller := generic.NewController(
|
||||
generic.NewInformer[runtime.Object](informer),
|
||||
c.reconcileParams,
|
||||
generic.ControllerOptions{
|
||||
Workers: 1,
|
||||
Name: paramSource.String() + "-controller",
|
||||
},
|
||||
)
|
||||
|
||||
c.paramsCRDControllers[*paramSource] = ¶mInfo{
|
||||
controller: controller,
|
||||
stop: instanceCancel,
|
||||
dependentDefinitions: sets.New(nn),
|
||||
}
|
||||
|
||||
go controller.Run(instanceContext)
|
||||
// Dynamic JSON informer fallback.
|
||||
// Cannot use shared dynamic informer since it would be impossible
|
||||
// to clean CRD informers properly with multiple dependents
|
||||
// (cannot start ahead of time, and cannot track dependencies via stopCh)
|
||||
informer = dynamicinformer.NewFilteredDynamicInformer(
|
||||
c.dynamicClient,
|
||||
mapping.Resource,
|
||||
corev1.NamespaceAll,
|
||||
// Use same interval as is used for k8s typed sharedInformerFactory
|
||||
// https://github.com/kubernetes/kubernetes/blob/7e0923899fed622efbc8679cca6b000d43633e38/cmd/kube-apiserver/app/server.go#L430
|
||||
10*time.Minute,
|
||||
cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc},
|
||||
nil,
|
||||
).Informer()
|
||||
go informer.Run(instanceContext.Done())
|
||||
}
|
||||
|
||||
return nil
|
||||
controller := generic.NewController(
|
||||
generic.NewInformer[runtime.Object](informer),
|
||||
c.reconcileParams,
|
||||
generic.ControllerOptions{
|
||||
Workers: 1,
|
||||
Name: paramSource.String() + "-controller",
|
||||
},
|
||||
)
|
||||
|
||||
ret := ¶mInfo{
|
||||
controller: controller,
|
||||
stop: instanceCancel,
|
||||
scope: mapping.Scope,
|
||||
dependentDefinitions: sets.New[namespacedName](),
|
||||
}
|
||||
c.paramsCRDControllers[*paramSource] = ret
|
||||
|
||||
go controller.Run(instanceContext)
|
||||
return ret
|
||||
|
||||
}
|
||||
|
||||
func (c *policyController) reconcilePolicyBinding(namespace, name string, binding *v1alpha1.ValidatingAdmissionPolicyBinding) error {
|
||||
@@ -494,17 +464,17 @@ func (c *policyController) latestPolicyData() []policyData {
|
||||
bindingInfos = append(bindingInfos, *bindingInfo)
|
||||
}
|
||||
|
||||
var paramController generic.Controller[runtime.Object]
|
||||
var pInfo paramInfo
|
||||
if paramKind := definitionInfo.lastReconciledValue.Spec.ParamKind; paramKind != nil {
|
||||
if info, ok := c.paramsCRDControllers[*paramKind]; ok {
|
||||
paramController = info.controller
|
||||
pInfo = *info
|
||||
}
|
||||
}
|
||||
|
||||
res = append(res, policyData{
|
||||
definitionInfo: *definitionInfo,
|
||||
paramController: paramController,
|
||||
bindings: bindingInfos,
|
||||
definitionInfo: *definitionInfo,
|
||||
paramInfo: pInfo,
|
||||
bindings: bindingInfos,
|
||||
})
|
||||
}
|
||||
|
||||
|
@@ -18,11 +18,18 @@ limitations under the License.
|
||||
|
||||
package v1alpha1
|
||||
|
||||
import (
|
||||
v1alpha1 "k8s.io/api/admissionregistration/v1alpha1"
|
||||
v1 "k8s.io/client-go/applyconfigurations/meta/v1"
|
||||
)
|
||||
|
||||
// ParamRefApplyConfiguration represents an declarative configuration of the ParamRef type for use
|
||||
// with apply.
|
||||
type ParamRefApplyConfiguration struct {
|
||||
Name *string `json:"name,omitempty"`
|
||||
Namespace *string `json:"namespace,omitempty"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
Namespace *string `json:"namespace,omitempty"`
|
||||
Selector *v1.LabelSelectorApplyConfiguration `json:"selector,omitempty"`
|
||||
ParameterNotFoundAction *v1alpha1.ParameterNotFoundActionType `json:"parameterNotFoundAction,omitempty"`
|
||||
}
|
||||
|
||||
// ParamRefApplyConfiguration constructs an declarative configuration of the ParamRef type for use with
|
||||
@@ -46,3 +53,19 @@ func (b *ParamRefApplyConfiguration) WithNamespace(value string) *ParamRefApplyC
|
||||
b.Namespace = &value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithSelector sets the Selector 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 Selector field is set to the value of the last call.
|
||||
func (b *ParamRefApplyConfiguration) WithSelector(value *v1.LabelSelectorApplyConfiguration) *ParamRefApplyConfiguration {
|
||||
b.Selector = value
|
||||
return b
|
||||
}
|
||||
|
||||
// WithParameterNotFoundAction sets the ParameterNotFoundAction 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 ParameterNotFoundAction field is set to the value of the last call.
|
||||
func (b *ParamRefApplyConfiguration) WithParameterNotFoundAction(value v1alpha1.ParameterNotFoundActionType) *ParamRefApplyConfiguration {
|
||||
b.ParameterNotFoundAction = &value
|
||||
return b
|
||||
}
|
||||
|
@@ -366,6 +366,12 @@ var schemaYAML = typed.YAMLObject(`types:
|
||||
- name: namespace
|
||||
type:
|
||||
scalar: string
|
||||
- name: parameterNotFoundAction
|
||||
type:
|
||||
scalar: string
|
||||
- name: selector
|
||||
type:
|
||||
namedType: io.k8s.apimachinery.pkg.apis.meta.v1.LabelSelector
|
||||
elementRelationship: atomic
|
||||
- name: io.k8s.api.admissionregistration.v1alpha1.TypeChecking
|
||||
map:
|
||||
|
@@ -2026,7 +2026,7 @@ func Test_ValidatingAdmissionPolicy_ParamResourceDeletedThenRecreated(t *testing
|
||||
return false, nil
|
||||
}
|
||||
|
||||
if !strings.Contains(err.Error(), "failed to configure binding: test not found") {
|
||||
if !strings.Contains(err.Error(), "failed to configure binding: no params found for policy binding with `Deny` parameterNotFoundAction") {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -2049,7 +2049,7 @@ func Test_ValidatingAdmissionPolicy_ParamResourceDeletedThenRecreated(t *testing
|
||||
|
||||
_, err = client.CoreV1().Namespaces().Create(context.TODO(), disallowedNamespace, metav1.CreateOptions{})
|
||||
// cache not synced with new object yet, try again
|
||||
if strings.Contains(err.Error(), "failed to configure binding: test not found") {
|
||||
if strings.Contains(err.Error(), "failed to configure binding: no params found for policy binding with `Deny` parameterNotFoundAction") {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user