Skip to content

Commit

Permalink
add namespaceParamRef to v1alpha1 and internal
Browse files Browse the repository at this point in the history
add required field to validation test

test multiple combinartions of paramRefs in binding tests

add validation test cases for new ParamRef fields
  • Loading branch information
alexzielenski committed Jul 20, 2023
1 parent 51bfe41 commit c8dbf47
Show file tree
Hide file tree
Showing 12 changed files with 634 additions and 162 deletions.
9 changes: 9 additions & 0 deletions pkg/apis/admissionregistration/fuzzer/fuzzer.go
Expand Up @@ -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
}
},
}
}
81 changes: 76 additions & 5 deletions pkg/apis/admissionregistration/types.go
Expand Up @@ -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

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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
Expand Down
8 changes: 8 additions & 0 deletions pkg/apis/admissionregistration/v1alpha1/defaults.go
Expand Up @@ -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
}
}
55 changes: 55 additions & 0 deletions pkg/apis/admissionregistration/v1alpha1/defaults_test.go
Expand Up @@ -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))
}
})
}
}
33 changes: 31 additions & 2 deletions pkg/apis/admissionregistration/validation/validation.go
Expand Up @@ -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
}

Expand Down

0 comments on commit c8dbf47

Please sign in to comment.