mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-06 02:34:03 +00:00
add generic policy dispatcher
similar to the generic policy source, applies common match logic for code sharing with validating/mutating
This commit is contained in:
parent
11ed3032c0
commit
96c418a7b7
@ -22,7 +22,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"k8s.io/apimachinery/pkg/api/meta"
|
"k8s.io/apimachinery/pkg/api/meta"
|
||||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
|
||||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
"k8s.io/apiserver/pkg/admission"
|
"k8s.io/apiserver/pkg/admission"
|
||||||
"k8s.io/apiserver/pkg/admission/initializer"
|
"k8s.io/apiserver/pkg/admission/initializer"
|
||||||
@ -37,12 +36,6 @@ import (
|
|||||||
type sourceFactory[H any] func(informers.SharedInformerFactory, kubernetes.Interface, dynamic.Interface, meta.RESTMapper) Source[H]
|
type sourceFactory[H any] func(informers.SharedInformerFactory, kubernetes.Interface, dynamic.Interface, meta.RESTMapper) Source[H]
|
||||||
type dispatcherFactory[H any] func(authorizer.Authorizer, *matching.Matcher) Dispatcher[H]
|
type dispatcherFactory[H any] func(authorizer.Authorizer, *matching.Matcher) Dispatcher[H]
|
||||||
|
|
||||||
type Invocation struct {
|
|
||||||
Resource schema.GroupVersionResource
|
|
||||||
Subresource string
|
|
||||||
Kind schema.GroupVersionKind
|
|
||||||
}
|
|
||||||
|
|
||||||
// AdmissionPolicyManager is an abstract admission plugin with all the
|
// AdmissionPolicyManager is an abstract admission plugin with all the
|
||||||
// infrastructure to define Admit or Validate on-top.
|
// infrastructure to define Admit or Validate on-top.
|
||||||
type Plugin[H any] struct {
|
type Plugin[H any] struct {
|
||||||
|
@ -0,0 +1,354 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2024 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"k8s.io/api/admissionregistration/v1beta1"
|
||||||
|
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
"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"
|
||||||
|
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||||
|
"k8s.io/apiserver/pkg/admission"
|
||||||
|
"k8s.io/apiserver/pkg/admission/plugin/policy/matching"
|
||||||
|
webhookgeneric "k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
|
||||||
|
"k8s.io/client-go/informers"
|
||||||
|
"k8s.io/client-go/tools/cache"
|
||||||
|
)
|
||||||
|
|
||||||
|
// A policy invocation is a single policy-binding-param tuple from a Policy Hook
|
||||||
|
// in the context of a specific request. The params have already been resolved
|
||||||
|
// and any error in configuration or setting up the invocation is stored in
|
||||||
|
// the Error field.
|
||||||
|
type PolicyInvocation[P runtime.Object, B runtime.Object, E Evaluator] struct {
|
||||||
|
// Relevant policy for this hook.
|
||||||
|
// This field is always populated
|
||||||
|
Policy P
|
||||||
|
|
||||||
|
// Matched Kind for the request given the policy's matchconstraints
|
||||||
|
// May be empty if there was an error matching the resource
|
||||||
|
Kind schema.GroupVersionKind
|
||||||
|
|
||||||
|
// Matched Resource for the request given the policy's matchconstraints
|
||||||
|
// May be empty if there was an error matching the resource
|
||||||
|
Resource schema.GroupVersionResource
|
||||||
|
|
||||||
|
// Relevant binding for this hook.
|
||||||
|
// May be empty if there was an error with the policy's configuration itself
|
||||||
|
Binding B
|
||||||
|
|
||||||
|
// Compiled policy evaluator
|
||||||
|
Evaluator E
|
||||||
|
|
||||||
|
// Params fetched by the binding to use to evaluate the policy
|
||||||
|
Param runtime.Object
|
||||||
|
|
||||||
|
// Error is set if there was an error with the policy or binding or its
|
||||||
|
// params, etc
|
||||||
|
Error error
|
||||||
|
}
|
||||||
|
|
||||||
|
// dispatcherDelegate is called during a request with a pre-filtered list
|
||||||
|
// of (Policy, Binding, Param) tuples that are active and match the request.
|
||||||
|
// The dispatcher delegate is responsible for updating the object on the
|
||||||
|
// admission attributes in the case of mutation, or returning a status error in
|
||||||
|
// the case of validation.
|
||||||
|
//
|
||||||
|
// The delegate provides the "validation" or "mutation" aspect of dispatcher functionality
|
||||||
|
// (in contrast to generic.PolicyDispatcher which only selects active policies and params)
|
||||||
|
type dispatcherDelegate[P, B runtime.Object, E Evaluator] func(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces, versionedAttributes webhookgeneric.VersionedAttributeAccessor, invocations []PolicyInvocation[P, B, E]) error
|
||||||
|
|
||||||
|
type policyDispatcher[P runtime.Object, B runtime.Object, E Evaluator] struct {
|
||||||
|
newPolicyAccessor func(P) PolicyAccessor
|
||||||
|
newBindingAccessor func(B) BindingAccessor
|
||||||
|
matcher PolicyMatcher
|
||||||
|
delegate dispatcherDelegate[P, B, E]
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPolicyDispatcher[P runtime.Object, B runtime.Object, E Evaluator](
|
||||||
|
newPolicyAccessor func(P) PolicyAccessor,
|
||||||
|
newBindingAccessor func(B) BindingAccessor,
|
||||||
|
matcher *matching.Matcher,
|
||||||
|
delegate dispatcherDelegate[P, B, E],
|
||||||
|
) Dispatcher[PolicyHook[P, B, E]] {
|
||||||
|
return &policyDispatcher[P, B, E]{
|
||||||
|
newPolicyAccessor: newPolicyAccessor,
|
||||||
|
newBindingAccessor: newBindingAccessor,
|
||||||
|
matcher: NewPolicyMatcher(matcher),
|
||||||
|
delegate: delegate,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dispatch implements generic.Dispatcher. It loops through all active hooks
|
||||||
|
// (policy x binding pairs) and selects those which are active for the current
|
||||||
|
// request. It then resolves all params and creates an Invocation for each
|
||||||
|
// matching policy-binding-param tuple. The delegate is then called with the
|
||||||
|
// list of tuples.
|
||||||
|
//
|
||||||
|
// Note: MatchConditions expressions are not evaluated here. The dispatcher delegate
|
||||||
|
// is expected to ignore the result of any policies whose match conditions dont pass.
|
||||||
|
// This may be possible to refactor so matchconditions are checked here instead.
|
||||||
|
func (d *policyDispatcher[P, B, E]) Dispatch(ctx context.Context, a admission.Attributes, o admission.ObjectInterfaces, hooks []PolicyHook[P, B, E]) error {
|
||||||
|
var relevantHooks []PolicyInvocation[P, B, E]
|
||||||
|
// Construct all the versions we need to call our webhooks
|
||||||
|
versionedAttrAccessor := &versionedAttributeAccessor{
|
||||||
|
versionedAttrs: map[schema.GroupVersionKind]*admission.VersionedAttributes{},
|
||||||
|
attr: a,
|
||||||
|
objectInterfaces: o,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, hook := range hooks {
|
||||||
|
policyAccessor := d.newPolicyAccessor(hook.Policy)
|
||||||
|
matches, matchGVR, matchGVK, err := d.matcher.DefinitionMatches(a, o, policyAccessor)
|
||||||
|
if err != nil {
|
||||||
|
// There was an error evaluating if this policy matches anything.
|
||||||
|
utilruntime.HandleError(err)
|
||||||
|
relevantHooks = append(relevantHooks, PolicyInvocation[P, B, E]{
|
||||||
|
Policy: hook.Policy,
|
||||||
|
Error: err,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
} else if !matches {
|
||||||
|
continue
|
||||||
|
} else if hook.ConfigurationError != nil {
|
||||||
|
// The policy matches but there is a configuration error with the
|
||||||
|
// policy itself
|
||||||
|
relevantHooks = append(relevantHooks, PolicyInvocation[P, B, E]{
|
||||||
|
Policy: hook.Policy,
|
||||||
|
Error: hook.ConfigurationError,
|
||||||
|
Resource: matchGVR,
|
||||||
|
Kind: matchGVK,
|
||||||
|
})
|
||||||
|
utilruntime.HandleError(hook.ConfigurationError)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, binding := range hook.Bindings {
|
||||||
|
bindingAccessor := d.newBindingAccessor(binding)
|
||||||
|
matches, err = d.matcher.BindingMatches(a, o, bindingAccessor)
|
||||||
|
if err != nil {
|
||||||
|
// There was an error evaluating if this binding matches anything.
|
||||||
|
utilruntime.HandleError(err)
|
||||||
|
relevantHooks = append(relevantHooks, PolicyInvocation[P, B, E]{
|
||||||
|
Policy: hook.Policy,
|
||||||
|
Binding: binding,
|
||||||
|
Error: err,
|
||||||
|
Resource: matchGVR,
|
||||||
|
Kind: matchGVK,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
} else if !matches {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect params for this binding
|
||||||
|
params, err := CollectParams(
|
||||||
|
policyAccessor.GetParamKind(),
|
||||||
|
hook.ParamInformer,
|
||||||
|
hook.ParamScope,
|
||||||
|
bindingAccessor.GetParamRef(),
|
||||||
|
a.GetNamespace(),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
// There was an error collecting params for this binding.
|
||||||
|
utilruntime.HandleError(err)
|
||||||
|
relevantHooks = append(relevantHooks, PolicyInvocation[P, B, E]{
|
||||||
|
Policy: hook.Policy,
|
||||||
|
Binding: binding,
|
||||||
|
Error: err,
|
||||||
|
Resource: matchGVR,
|
||||||
|
Kind: matchGVK,
|
||||||
|
})
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// If params is empty and there was no error, that means that
|
||||||
|
// ParamNotFoundAction is ignore, so it shouldnt be added to list
|
||||||
|
for _, param := range params {
|
||||||
|
relevantHooks = append(relevantHooks, PolicyInvocation[P, B, E]{
|
||||||
|
Policy: hook.Policy,
|
||||||
|
Binding: binding,
|
||||||
|
Kind: matchGVK,
|
||||||
|
Resource: matchGVR,
|
||||||
|
Param: param,
|
||||||
|
Evaluator: hook.Evaluator,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// VersionedAttr result will be cached and reused later during parallel
|
||||||
|
// hook calls
|
||||||
|
_, err = versionedAttrAccessor.VersionedAttribute(matchGVK)
|
||||||
|
if err != nil {
|
||||||
|
return apierrors.NewInternalError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(relevantHooks) == 0 {
|
||||||
|
// no matching hooks
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return d.delegate(ctx, a, o, versionedAttrAccessor, relevantHooks)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns params to use to evaluate a policy-binding with given param
|
||||||
|
// configuration. If the policy-binding has no param configuration, it
|
||||||
|
// returns a single-element list with a nil param.
|
||||||
|
func CollectParams(
|
||||||
|
paramKind *v1beta1.ParamKind,
|
||||||
|
paramInformer informers.GenericInformer,
|
||||||
|
paramScope meta.RESTScope,
|
||||||
|
paramRef *v1beta1.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 cache.GenericNamespaceLister
|
||||||
|
|
||||||
|
// Make sure the param kind is ready to use
|
||||||
|
if paramKind != nil && paramRef != nil {
|
||||||
|
if paramInformer == 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 = paramInformer.Lister()
|
||||||
|
if paramScope.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 = paramInformer.Lister().ByNamespace(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(context.Background(), 1*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
if !cache.WaitForCacheSync(timeoutCtx.Done(), paramInformer.Informer().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 && paramScope.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 apierrors.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 apierrors.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 == v1beta1.DenyAction {
|
||||||
|
return nil, errors.New("no params found for policy binding with `Deny` parameterNotFoundAction")
|
||||||
|
}
|
||||||
|
|
||||||
|
return params, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ webhookgeneric.VersionedAttributeAccessor = &versionedAttributeAccessor{}
|
||||||
|
|
||||||
|
type versionedAttributeAccessor struct {
|
||||||
|
versionedAttrs map[schema.GroupVersionKind]*admission.VersionedAttributes
|
||||||
|
attr admission.Attributes
|
||||||
|
objectInterfaces admission.ObjectInterfaces
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v *versionedAttributeAccessor) VersionedAttribute(gvk schema.GroupVersionKind) (*admission.VersionedAttributes, error) {
|
||||||
|
if val, ok := v.versionedAttrs[gvk]; ok {
|
||||||
|
return val, nil
|
||||||
|
}
|
||||||
|
versionedAttr, err := admission.NewVersionedAttributes(v.attr, gvk, v.objectInterfaces)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
v.versionedAttrs[gvk] = versionedAttr
|
||||||
|
return versionedAttr, nil
|
||||||
|
}
|
@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2022 The Kubernetes Authors.
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package generic
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"k8s.io/api/admissionregistration/v1beta1"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apiserver/pkg/admission"
|
||||||
|
"k8s.io/apiserver/pkg/admission/plugin/policy/matching"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Matcher is used for matching ValidatingAdmissionPolicy and ValidatingAdmissionPolicyBinding to attributes
|
||||||
|
type PolicyMatcher interface {
|
||||||
|
admission.InitializationValidator
|
||||||
|
|
||||||
|
// DefinitionMatches says whether this policy definition matches the provided admission
|
||||||
|
// resource request
|
||||||
|
DefinitionMatches(a admission.Attributes, o admission.ObjectInterfaces, definition PolicyAccessor) (bool, schema.GroupVersionResource, schema.GroupVersionKind, error)
|
||||||
|
|
||||||
|
// BindingMatches says whether this policy definition matches the provided admission
|
||||||
|
// resource request
|
||||||
|
BindingMatches(a admission.Attributes, o admission.ObjectInterfaces, binding BindingAccessor) (bool, error)
|
||||||
|
|
||||||
|
// GetNamespace retrieves the Namespace resource by the given name. The name may be empty, in which case
|
||||||
|
// GetNamespace must return nil, nil
|
||||||
|
GetNamespace(name string) (*corev1.Namespace, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
type matcher struct {
|
||||||
|
Matcher *matching.Matcher
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewPolicyMatcher(m *matching.Matcher) PolicyMatcher {
|
||||||
|
return &matcher{
|
||||||
|
Matcher: m,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateInitialization checks if Matcher is initialized.
|
||||||
|
func (c *matcher) ValidateInitialization() error {
|
||||||
|
return c.Matcher.ValidateInitialization()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DefinitionMatches returns whether this ValidatingAdmissionPolicy matches the provided admission resource request
|
||||||
|
func (c *matcher) DefinitionMatches(a admission.Attributes, o admission.ObjectInterfaces, definition PolicyAccessor) (bool, schema.GroupVersionResource, schema.GroupVersionKind, error) {
|
||||||
|
constraints := definition.GetMatchConstraints()
|
||||||
|
if constraints == nil {
|
||||||
|
return false, schema.GroupVersionResource{}, schema.GroupVersionKind{}, fmt.Errorf("policy contained no match constraints, a required field")
|
||||||
|
}
|
||||||
|
criteria := matchCriteria{constraints: constraints}
|
||||||
|
return c.Matcher.Matches(a, o, &criteria)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BindingMatches returns whether this ValidatingAdmissionPolicyBinding matches the provided admission resource request
|
||||||
|
func (c *matcher) BindingMatches(a admission.Attributes, o admission.ObjectInterfaces, binding BindingAccessor) (bool, error) {
|
||||||
|
matchResources := binding.GetMatchResources()
|
||||||
|
if matchResources == nil {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
criteria := matchCriteria{constraints: matchResources}
|
||||||
|
isMatch, _, _, err := c.Matcher.Matches(a, o, &criteria)
|
||||||
|
return isMatch, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *matcher) GetNamespace(name string) (*corev1.Namespace, error) {
|
||||||
|
return c.Matcher.GetNamespace(name)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ matching.MatchCriteria = &matchCriteria{}
|
||||||
|
|
||||||
|
type matchCriteria struct {
|
||||||
|
constraints *v1beta1.MatchResources
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetParsedNamespaceSelector returns the converted LabelSelector which implements labels.Selector
|
||||||
|
func (m *matchCriteria) GetParsedNamespaceSelector() (labels.Selector, error) {
|
||||||
|
return metav1.LabelSelectorAsSelector(m.constraints.NamespaceSelector)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetParsedObjectSelector returns the converted LabelSelector which implements labels.Selector
|
||||||
|
func (m *matchCriteria) GetParsedObjectSelector() (labels.Selector, error) {
|
||||||
|
return metav1.LabelSelectorAsSelector(m.constraints.ObjectSelector)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetMatchResources returns the matchConstraints
|
||||||
|
func (m *matchCriteria) GetMatchResources() v1beta1.MatchResources {
|
||||||
|
return *m.constraints
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user