mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-29 22:46:12 +00:00
Add match check for policy and binding.
Co-authored-by: Max Smythe <smythe@google.com>
This commit is contained in:
parent
d3f48136d0
commit
46f97d4662
@ -0,0 +1,192 @@
|
|||||||
|
/*
|
||||||
|
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 matching
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
v1 "k8s.io/api/admissionregistration/v1"
|
||||||
|
"k8s.io/api/admissionregistration/v1alpha1"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apiserver/pkg/admission"
|
||||||
|
"k8s.io/client-go/kubernetes"
|
||||||
|
listersv1 "k8s.io/client-go/listers/core/v1"
|
||||||
|
|
||||||
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace"
|
||||||
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/object"
|
||||||
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/rules"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MatchCriteria interface {
|
||||||
|
namespace.NamespaceSelectorProvider
|
||||||
|
object.ObjectSelectorProvider
|
||||||
|
|
||||||
|
GetMatchResources() v1alpha1.MatchResources
|
||||||
|
}
|
||||||
|
|
||||||
|
// Matcher decides if a request matches against matchCriteria
|
||||||
|
type Matcher struct {
|
||||||
|
namespaceMatcher *namespace.Matcher
|
||||||
|
objectMatcher *object.Matcher
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewMatcher initialize the matcher with dependencies requires
|
||||||
|
func NewMatcher(
|
||||||
|
namespaceLister listersv1.NamespaceLister,
|
||||||
|
client kubernetes.Interface,
|
||||||
|
) *Matcher {
|
||||||
|
return &Matcher{
|
||||||
|
namespaceMatcher: &namespace.Matcher{
|
||||||
|
NamespaceLister: namespaceLister,
|
||||||
|
Client: client,
|
||||||
|
},
|
||||||
|
objectMatcher: &object.Matcher{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateInitialization verify if the matcher is ready before use
|
||||||
|
func (m *Matcher) ValidateInitialization() error {
|
||||||
|
if err := m.namespaceMatcher.Validate(); err != nil {
|
||||||
|
return fmt.Errorf("namespaceMatcher is not properly setup: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Matcher) Matches(attr admission.Attributes, o admission.ObjectInterfaces, criteria MatchCriteria) (bool, schema.GroupVersionKind, error) {
|
||||||
|
matches, matchNsErr := m.namespaceMatcher.MatchNamespaceSelector(criteria, attr)
|
||||||
|
// Should not return an error here for policy which do not apply to the request, even if err is an unexpected scenario.
|
||||||
|
if !matches && matchNsErr == nil {
|
||||||
|
return false, schema.GroupVersionKind{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
matches, matchObjErr := m.objectMatcher.MatchObjectSelector(criteria, attr)
|
||||||
|
// Should not return an error here for policy which do not apply to the request, even if err is an unexpected scenario.
|
||||||
|
if !matches && matchObjErr == nil {
|
||||||
|
return false, schema.GroupVersionKind{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
matchResources := criteria.GetMatchResources()
|
||||||
|
matchPolicy := matchResources.MatchPolicy
|
||||||
|
if isExcluded, _, err := matchesResourceRules(matchResources.ExcludeResourceRules, matchPolicy, attr, o); isExcluded || err != nil {
|
||||||
|
return false, schema.GroupVersionKind{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
isMatch bool
|
||||||
|
matchKind schema.GroupVersionKind
|
||||||
|
matchErr error
|
||||||
|
)
|
||||||
|
if len(matchResources.ResourceRules) == 0 {
|
||||||
|
isMatch = true
|
||||||
|
matchKind = attr.GetKind()
|
||||||
|
} else {
|
||||||
|
isMatch, matchKind, matchErr = matchesResourceRules(matchResources.ResourceRules, matchPolicy, attr, o)
|
||||||
|
}
|
||||||
|
if matchErr != nil {
|
||||||
|
return false, schema.GroupVersionKind{}, matchErr
|
||||||
|
}
|
||||||
|
if !isMatch {
|
||||||
|
return false, schema.GroupVersionKind{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// now that we know this applies to this request otherwise, if there were selector errors, return them
|
||||||
|
if matchNsErr != nil {
|
||||||
|
return false, schema.GroupVersionKind{}, matchNsErr
|
||||||
|
}
|
||||||
|
if matchObjErr != nil {
|
||||||
|
return false, schema.GroupVersionKind{}, matchObjErr
|
||||||
|
}
|
||||||
|
|
||||||
|
return true, matchKind, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func matchesResourceRules(namedRules []v1alpha1.NamedRuleWithOperations, matchPolicy *v1alpha1.MatchPolicyType, attr admission.Attributes, o admission.ObjectInterfaces) (bool, schema.GroupVersionKind, error) {
|
||||||
|
matchKind := attr.GetKind()
|
||||||
|
for _, namedRule := range namedRules {
|
||||||
|
rule := v1.RuleWithOperations(namedRule.RuleWithOperations)
|
||||||
|
ruleMatcher := rules.Matcher{
|
||||||
|
Rule: rule,
|
||||||
|
Attr: attr,
|
||||||
|
}
|
||||||
|
if !ruleMatcher.Matches() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
// an empty name list always matches
|
||||||
|
if len(namedRule.ResourceNames) == 0 {
|
||||||
|
return true, matchKind, nil
|
||||||
|
}
|
||||||
|
// TODO: GetName() can return an empty string if the user is relying on
|
||||||
|
// the API server to generate the name... figure out what to do for this edge case
|
||||||
|
name := attr.GetName()
|
||||||
|
for _, matchedName := range namedRule.ResourceNames {
|
||||||
|
if name == matchedName {
|
||||||
|
return true, matchKind, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if match policy is undefined or exact, don't perform fuzzy matching
|
||||||
|
// note that defaulting to fuzzy matching is set by the API
|
||||||
|
if matchPolicy == nil || *matchPolicy == v1alpha1.Exact {
|
||||||
|
return false, schema.GroupVersionKind{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
attrWithOverride := &attrWithResourceOverride{Attributes: attr}
|
||||||
|
equivalents := o.GetEquivalentResourceMapper().EquivalentResourcesFor(attr.GetResource(), attr.GetSubresource())
|
||||||
|
for _, namedRule := range namedRules {
|
||||||
|
for _, equivalent := range equivalents {
|
||||||
|
if equivalent == attr.GetResource() {
|
||||||
|
// we have already checked the original resource
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
attrWithOverride.resource = equivalent
|
||||||
|
rule := v1.RuleWithOperations(namedRule.RuleWithOperations)
|
||||||
|
m := rules.Matcher{
|
||||||
|
Rule: rule,
|
||||||
|
Attr: attrWithOverride,
|
||||||
|
}
|
||||||
|
if !m.Matches() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
matchKind = o.GetEquivalentResourceMapper().KindFor(equivalent, attr.GetSubresource())
|
||||||
|
if matchKind.Empty() {
|
||||||
|
return false, schema.GroupVersionKind{}, fmt.Errorf("unable to convert to %v: unknown kind", equivalent)
|
||||||
|
}
|
||||||
|
// an empty name list always matches
|
||||||
|
if len(namedRule.ResourceNames) == 0 {
|
||||||
|
return true, matchKind, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: GetName() can return an empty string if the user is relying on
|
||||||
|
// the API server to generate the name... figure out what to do for this edge case
|
||||||
|
name := attr.GetName()
|
||||||
|
for _, matchedName := range namedRule.ResourceNames {
|
||||||
|
if name == matchedName {
|
||||||
|
return true, matchKind, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false, schema.GroupVersionKind{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type attrWithResourceOverride struct {
|
||||||
|
admission.Attributes
|
||||||
|
resource schema.GroupVersionResource
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *attrWithResourceOverride) GetResource() schema.GroupVersionResource { return a.resource }
|
@ -0,0 +1,787 @@
|
|||||||
|
/*
|
||||||
|
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 matching
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
v1 "k8s.io/api/admissionregistration/v1"
|
||||||
|
"k8s.io/api/admissionregistration/v1alpha1"
|
||||||
|
corev1 "k8s.io/api/core/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/api/errors"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
"k8s.io/apimachinery/pkg/labels"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime"
|
||||||
|
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||||
|
"k8s.io/apiserver/pkg/admission"
|
||||||
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace"
|
||||||
|
"k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/object"
|
||||||
|
"k8s.io/apiserver/pkg/apis/example"
|
||||||
|
)
|
||||||
|
|
||||||
|
var _ MatchCriteria = &fakeCriteria{}
|
||||||
|
|
||||||
|
type fakeCriteria struct {
|
||||||
|
matchResources v1alpha1.MatchResources
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fc *fakeCriteria) GetMatchResources() v1alpha1.MatchResources {
|
||||||
|
return fc.matchResources
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fc *fakeCriteria) GetParsedNamespaceSelector() (labels.Selector, error) {
|
||||||
|
return metav1.LabelSelectorAsSelector(fc.matchResources.NamespaceSelector)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fc *fakeCriteria) GetParsedObjectSelector() (labels.Selector, error) {
|
||||||
|
return metav1.LabelSelectorAsSelector(fc.matchResources.ObjectSelector)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMatcher(t *testing.T) {
|
||||||
|
a := &Matcher{namespaceMatcher: &namespace.Matcher{}, objectMatcher: &object.Matcher{}}
|
||||||
|
|
||||||
|
allScopes := v1.AllScopes
|
||||||
|
exactMatch := v1alpha1.Exact
|
||||||
|
equivalentMatch := v1alpha1.Equivalent
|
||||||
|
|
||||||
|
mapper := runtime.NewEquivalentResourceRegistryWithIdentity(func(resource schema.GroupResource) string {
|
||||||
|
if resource.Resource == "deployments" {
|
||||||
|
// co-locate deployments in all API groups
|
||||||
|
return "/deployments"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"extensions", "v1beta1", "deployments"}, "", schema.GroupVersionKind{"extensions", "v1beta1", "Deployment"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1", "Deployment"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1beta1", "Deployment"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1alpha1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1alpha1", "Deployment"})
|
||||||
|
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"extensions", "v1beta1", "deployments"}, "scale", schema.GroupVersionKind{"extensions", "v1beta1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", schema.GroupVersionKind{"autoscaling", "v1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta1", "deployments"}, "scale", schema.GroupVersionKind{"apps", "v1beta1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1alpha1", "deployments"}, "scale", schema.GroupVersionKind{"apps", "v1alpha1", "Scale"})
|
||||||
|
|
||||||
|
// register invalid kinds to trigger an error
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"example.com", "v1", "widgets"}, "", schema.GroupVersionKind{"", "", ""})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"example.com", "v2", "widgets"}, "", schema.GroupVersionKind{"", "", ""})
|
||||||
|
|
||||||
|
interfaces := &admission.RuntimeObjectInterfaces{EquivalentResourceMapper: mapper}
|
||||||
|
|
||||||
|
// TODO write test cases for name matching and exclude matching
|
||||||
|
testcases := []struct {
|
||||||
|
name string
|
||||||
|
|
||||||
|
criteria *v1alpha1.MatchResources
|
||||||
|
attrs admission.Attributes
|
||||||
|
|
||||||
|
expectMatches bool
|
||||||
|
expectMatchKind *schema.GroupVersionKind
|
||||||
|
expectErr string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "no rules (just write)",
|
||||||
|
criteria: &v1alpha1.MatchResources{NamespaceSelector: &metav1.LabelSelector{}, ResourceRules: []v1alpha1.NamedRuleWithOperations{}},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"apps", "v1", "Deployment"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "wildcard rule, match as requested",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"apps", "v1", "Deployment"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: true,
|
||||||
|
expectMatchKind: &schema.GroupVersionKind{"apps", "v1", "Deployment"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "specific rules, prefer exact match",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"apps", "v1", "Deployment"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: true,
|
||||||
|
expectMatchKind: &schema.GroupVersionKind{"apps", "v1", "Deployment"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "specific rules, match miss",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"apps", "v1", "Deployment"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "specific rules, exact match miss",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
MatchPolicy: &exactMatch,
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"apps", "v1", "Deployment"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "specific rules, equivalent match, prefer extensions",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
MatchPolicy: &equivalentMatch,
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"apps", "v1", "Deployment"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: true,
|
||||||
|
expectMatchKind: &schema.GroupVersionKind{"extensions", "v1beta1", "Deployment"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "specific rules, equivalent match, prefer apps",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
MatchPolicy: &equivalentMatch,
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"apps", "v1", "Deployment"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: true,
|
||||||
|
expectMatchKind: &schema.GroupVersionKind{"apps", "v1beta1", "Deployment"},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
name: "specific rules, subresource prefer exact match",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: true,
|
||||||
|
expectMatchKind: &schema.GroupVersionKind{"autoscaling", "v1", "Scale"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "specific rules, subresource match miss",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "specific rules, subresource exact match miss",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
MatchPolicy: &exactMatch,
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "specific rules, subresource equivalent match, prefer extensions",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
MatchPolicy: &equivalentMatch,
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: true,
|
||||||
|
expectMatchKind: &schema.GroupVersionKind{"extensions", "v1beta1", "Scale"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "specific rules, subresource equivalent match, prefer apps",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
MatchPolicy: &equivalentMatch,
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: true,
|
||||||
|
expectMatchKind: &schema.GroupVersionKind{"apps", "v1beta1", "Scale"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "specific rules, prefer exact match and name match",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
ResourceNames: []string{"name"},
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: true,
|
||||||
|
expectMatchKind: &schema.GroupVersionKind{"autoscaling", "v1", "Scale"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "specific rules, prefer exact match and name match miss",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
ResourceNames: []string{"wrong-name"},
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "specific rules, subresource equivalent match, prefer extensions and name match",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
MatchPolicy: &equivalentMatch,
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
ResourceNames: []string{"name"},
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"extensions", "v1beta1", "deployments"}, "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: true,
|
||||||
|
expectMatchKind: &schema.GroupVersionKind{"autoscaling", "v1", "Scale"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "specific rules, subresource equivalent match, prefer extensions and name match miss",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
MatchPolicy: &equivalentMatch,
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
ResourceNames: []string{"wrong-name"},
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}}},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"extensions", "v1beta1", "deployments"}, "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "exclude resource match on miss",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
ExcludeResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: true,
|
||||||
|
expectMatchKind: &schema.GroupVersionKind{"autoscaling", "v1", "Scale"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "exclude resource miss on match",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
ExcludeResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"extensions", "v1beta1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "treat empty ResourceRules as match",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ExcludeResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "treat non-empty ResourceRules as no match",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{}},
|
||||||
|
},
|
||||||
|
attrs: admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "erroring namespace selector on otherwise non-matching rule doesn't error",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{MatchExpressions: []metav1.LabelSelectorRequirement{{Key: "key ", Operator: "In", Values: []string{"bad value"}}}},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Rule: v1alpha1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"deployments"}},
|
||||||
|
Operations: []v1alpha1.OperationType{"*"},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
attrs: admission.NewAttributesRecord(&example.Pod{}, nil, schema.GroupVersionKind{"example.apiserver.k8s.io", "v1", "Pod"}, "ns", "name", schema.GroupVersionResource{"example.apiserver.k8s.io", "v1", "pods"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: false,
|
||||||
|
expectErr: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "erroring namespace selector on otherwise matching rule errors",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{MatchExpressions: []metav1.LabelSelectorRequirement{{Key: "key", Operator: "In", Values: []string{"bad value"}}}},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Rule: v1alpha1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"pods"}},
|
||||||
|
Operations: []v1alpha1.OperationType{"*"},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
attrs: admission.NewAttributesRecord(&example.Pod{}, nil, schema.GroupVersionKind{"example.apiserver.k8s.io", "v1", "Pod"}, "ns", "name", schema.GroupVersionResource{"example.apiserver.k8s.io", "v1", "pods"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: false,
|
||||||
|
expectErr: "bad value",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "erroring object selector on otherwise non-matching rule doesn't error",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{MatchExpressions: []metav1.LabelSelectorRequirement{{Key: "key", Operator: "In", Values: []string{"bad value"}}}},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Rule: v1alpha1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"deployments"}},
|
||||||
|
Operations: []v1alpha1.OperationType{"*"},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
attrs: admission.NewAttributesRecord(&example.Pod{}, nil, schema.GroupVersionKind{"example.apiserver.k8s.io", "v1", "Pod"}, "ns", "name", schema.GroupVersionResource{"example.apiserver.k8s.io", "v1", "pods"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: false,
|
||||||
|
expectErr: "",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "erroring object selector on otherwise matching rule errors",
|
||||||
|
criteria: &v1alpha1.MatchResources{
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{MatchExpressions: []metav1.LabelSelectorRequirement{{Key: "key", Operator: "In", Values: []string{"bad value"}}}},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Rule: v1alpha1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"pods"}},
|
||||||
|
Operations: []v1alpha1.OperationType{"*"},
|
||||||
|
},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
attrs: admission.NewAttributesRecord(&example.Pod{}, nil, schema.GroupVersionKind{"example.apiserver.k8s.io", "v1", "Pod"}, "ns", "name", schema.GroupVersionResource{"example.apiserver.k8s.io", "v1", "pods"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
|
||||||
|
expectMatches: false,
|
||||||
|
expectErr: "bad value",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, testcase := range testcases {
|
||||||
|
t.Run(testcase.name, func(t *testing.T) {
|
||||||
|
matches, matchKind, err := a.Matches(testcase.attrs, interfaces, &fakeCriteria{matchResources: *testcase.criteria})
|
||||||
|
if err != nil {
|
||||||
|
if len(testcase.expectErr) == 0 {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
if !strings.Contains(err.Error(), testcase.expectErr) {
|
||||||
|
t.Fatalf("expected error containing %q, got %s", testcase.expectErr, err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else if len(testcase.expectErr) > 0 {
|
||||||
|
t.Fatalf("expected error %q, got no error", testcase.expectErr)
|
||||||
|
}
|
||||||
|
if testcase.expectMatchKind != nil {
|
||||||
|
if *testcase.expectMatchKind != matchKind {
|
||||||
|
t.Fatalf("expected matchKind %v, got %v", testcase.expectMatchKind, matchKind)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if matches != testcase.expectMatches {
|
||||||
|
t.Fatalf("expected matches = %v; got %v", testcase.expectMatches, matches)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type fakeNamespaceLister struct {
|
||||||
|
namespaces map[string]*corev1.Namespace
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f fakeNamespaceLister) List(selector labels.Selector) (ret []*corev1.Namespace, err error) {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
func (f fakeNamespaceLister) Get(name string) (*corev1.Namespace, error) {
|
||||||
|
ns, ok := f.namespaces[name]
|
||||||
|
if ok {
|
||||||
|
return ns, nil
|
||||||
|
}
|
||||||
|
return nil, errors.NewNotFound(corev1.Resource("namespaces"), name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkMatcher(b *testing.B) {
|
||||||
|
allScopes := v1.AllScopes
|
||||||
|
equivalentMatch := v1alpha1.Equivalent
|
||||||
|
|
||||||
|
namespace1Labels := map[string]string{"ns": "ns1"}
|
||||||
|
namespace1 := corev1.Namespace{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "ns1",
|
||||||
|
Labels: namespace1Labels,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
namespaceLister := fakeNamespaceLister{map[string]*corev1.Namespace{"ns": &namespace1}}
|
||||||
|
|
||||||
|
mapper := runtime.NewEquivalentResourceRegistryWithIdentity(func(resource schema.GroupResource) string {
|
||||||
|
if resource.Resource == "deployments" {
|
||||||
|
// co-locate deployments in all API groups
|
||||||
|
return "/deployments"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"extensions", "v1beta1", "deployments"}, "", schema.GroupVersionKind{"extensions", "v1beta1", "Deployment"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1", "Deployment"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1beta1", "Deployment"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1alpha1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1alpha1", "Deployment"})
|
||||||
|
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"extensions", "v1beta1", "deployments"}, "scale", schema.GroupVersionKind{"extensions", "v1beta1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", schema.GroupVersionKind{"autoscaling", "v1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta1", "deployments"}, "scale", schema.GroupVersionKind{"apps", "v1beta1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1alpha1", "deployments"}, "scale", schema.GroupVersionKind{"apps", "v1alpha1", "Scale"})
|
||||||
|
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1", "statefulset"}, "", schema.GroupVersionKind{"apps", "v1", "StatefulSet"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta1", "statefulset"}, "", schema.GroupVersionKind{"apps", "v1beta1", "StatefulSet"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta2", "statefulset"}, "", schema.GroupVersionKind{"apps", "v1beta2", "StatefulSet"})
|
||||||
|
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1", "statefulset"}, "scale", schema.GroupVersionKind{"apps", "v1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta1", "statefulset"}, "scale", schema.GroupVersionKind{"apps", "v1beta1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1alpha2", "statefulset"}, "scale", schema.GroupVersionKind{"apps", "v1beta2", "Scale"})
|
||||||
|
|
||||||
|
nsSelector := make(map[string]string)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
nsSelector[fmt.Sprintf("key-%d", i)] = fmt.Sprintf("val-%d", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
mr := v1alpha1.MatchResources{
|
||||||
|
MatchPolicy: &equivalentMatch,
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{MatchLabels: nsSelector},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{
|
||||||
|
{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
criteria := &fakeCriteria{matchResources: mr}
|
||||||
|
attrs := admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", admission.Create, &metav1.CreateOptions{}, false, nil)
|
||||||
|
interfaces := &admission.RuntimeObjectInterfaces{EquivalentResourceMapper: mapper}
|
||||||
|
matcher := &Matcher{namespaceMatcher: &namespace.Matcher{NamespaceLister: namespaceLister}, objectMatcher: &object.Matcher{}}
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
matcher.Matches(attrs, interfaces, criteria)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkShouldCallHookWithComplexRule(b *testing.B) {
|
||||||
|
allScopes := v1.AllScopes
|
||||||
|
equivalentMatch := v1alpha1.Equivalent
|
||||||
|
|
||||||
|
namespace1Labels := map[string]string{"ns": "ns1"}
|
||||||
|
namespace1 := corev1.Namespace{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "ns1",
|
||||||
|
Labels: namespace1Labels,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
namespaceLister := fakeNamespaceLister{map[string]*corev1.Namespace{"ns": &namespace1}}
|
||||||
|
|
||||||
|
mapper := runtime.NewEquivalentResourceRegistryWithIdentity(func(resource schema.GroupResource) string {
|
||||||
|
if resource.Resource == "deployments" {
|
||||||
|
// co-locate deployments in all API groups
|
||||||
|
return "/deployments"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"extensions", "v1beta1", "deployments"}, "", schema.GroupVersionKind{"extensions", "v1beta1", "Deployment"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1", "Deployment"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1beta1", "Deployment"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1alpha1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1alpha1", "Deployment"})
|
||||||
|
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"extensions", "v1beta1", "deployments"}, "scale", schema.GroupVersionKind{"extensions", "v1beta1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", schema.GroupVersionKind{"autoscaling", "v1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta1", "deployments"}, "scale", schema.GroupVersionKind{"apps", "v1beta1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1alpha1", "deployments"}, "scale", schema.GroupVersionKind{"apps", "v1alpha1", "Scale"})
|
||||||
|
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1", "statefulset"}, "", schema.GroupVersionKind{"apps", "v1", "StatefulSet"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta1", "statefulset"}, "", schema.GroupVersionKind{"apps", "v1beta1", "StatefulSet"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta2", "statefulset"}, "", schema.GroupVersionKind{"apps", "v1beta2", "StatefulSet"})
|
||||||
|
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1", "statefulset"}, "scale", schema.GroupVersionKind{"apps", "v1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta1", "statefulset"}, "scale", schema.GroupVersionKind{"apps", "v1beta1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1alpha2", "statefulset"}, "scale", schema.GroupVersionKind{"apps", "v1beta2", "Scale"})
|
||||||
|
|
||||||
|
mr := v1alpha1.MatchResources{
|
||||||
|
MatchPolicy: &equivalentMatch,
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
rule := v1alpha1.NamedRuleWithOperations{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{
|
||||||
|
APIGroups: []string{fmt.Sprintf("app-%d", i)},
|
||||||
|
APIVersions: []string{fmt.Sprintf("v%d", i)},
|
||||||
|
Resources: []string{fmt.Sprintf("resource%d", i), fmt.Sprintf("resource%d/scale", i)},
|
||||||
|
Scope: &allScopes,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mr.ResourceRules = append(mr.ResourceRules, rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
criteria := &fakeCriteria{matchResources: mr}
|
||||||
|
attrs := admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", admission.Create, &metav1.CreateOptions{}, false, nil)
|
||||||
|
interfaces := &admission.RuntimeObjectInterfaces{EquivalentResourceMapper: mapper}
|
||||||
|
matcher := &Matcher{namespaceMatcher: &namespace.Matcher{NamespaceLister: namespaceLister}, objectMatcher: &object.Matcher{}}
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
matcher.Matches(attrs, interfaces, criteria)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func BenchmarkShouldCallHookWithComplexSelectorAndRule(b *testing.B) {
|
||||||
|
allScopes := v1.AllScopes
|
||||||
|
equivalentMatch := v1alpha1.Equivalent
|
||||||
|
|
||||||
|
namespace1Labels := map[string]string{"ns": "ns1"}
|
||||||
|
namespace1 := corev1.Namespace{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "ns1",
|
||||||
|
Labels: namespace1Labels,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
namespaceLister := fakeNamespaceLister{map[string]*corev1.Namespace{"ns": &namespace1}}
|
||||||
|
|
||||||
|
mapper := runtime.NewEquivalentResourceRegistryWithIdentity(func(resource schema.GroupResource) string {
|
||||||
|
if resource.Resource == "deployments" {
|
||||||
|
// co-locate deployments in all API groups
|
||||||
|
return "/deployments"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"extensions", "v1beta1", "deployments"}, "", schema.GroupVersionKind{"extensions", "v1beta1", "Deployment"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1", "Deployment"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1beta1", "Deployment"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1alpha1", "deployments"}, "", schema.GroupVersionKind{"apps", "v1alpha1", "Deployment"})
|
||||||
|
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"extensions", "v1beta1", "deployments"}, "scale", schema.GroupVersionKind{"extensions", "v1beta1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", schema.GroupVersionKind{"autoscaling", "v1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta1", "deployments"}, "scale", schema.GroupVersionKind{"apps", "v1beta1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1alpha1", "deployments"}, "scale", schema.GroupVersionKind{"apps", "v1alpha1", "Scale"})
|
||||||
|
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1", "statefulset"}, "", schema.GroupVersionKind{"apps", "v1", "StatefulSet"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta1", "statefulset"}, "", schema.GroupVersionKind{"apps", "v1beta1", "StatefulSet"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta2", "statefulset"}, "", schema.GroupVersionKind{"apps", "v1beta2", "StatefulSet"})
|
||||||
|
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1", "statefulset"}, "scale", schema.GroupVersionKind{"apps", "v1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1beta1", "statefulset"}, "scale", schema.GroupVersionKind{"apps", "v1beta1", "Scale"})
|
||||||
|
mapper.RegisterKindFor(schema.GroupVersionResource{"apps", "v1alpha2", "statefulset"}, "scale", schema.GroupVersionKind{"apps", "v1beta2", "Scale"})
|
||||||
|
|
||||||
|
nsSelector := make(map[string]string)
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
nsSelector[fmt.Sprintf("key-%d", i)] = fmt.Sprintf("val-%d", i)
|
||||||
|
}
|
||||||
|
|
||||||
|
mr := v1alpha1.MatchResources{
|
||||||
|
MatchPolicy: &equivalentMatch,
|
||||||
|
NamespaceSelector: &metav1.LabelSelector{MatchLabels: nsSelector},
|
||||||
|
ObjectSelector: &metav1.LabelSelector{},
|
||||||
|
ResourceRules: []v1alpha1.NamedRuleWithOperations{},
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < 100; i++ {
|
||||||
|
rule := v1alpha1.NamedRuleWithOperations{
|
||||||
|
RuleWithOperations: v1alpha1.RuleWithOperations{
|
||||||
|
Operations: []v1.OperationType{"*"},
|
||||||
|
Rule: v1.Rule{
|
||||||
|
APIGroups: []string{fmt.Sprintf("app-%d", i)},
|
||||||
|
APIVersions: []string{fmt.Sprintf("v%d", i)},
|
||||||
|
Resources: []string{fmt.Sprintf("resource%d", i), fmt.Sprintf("resource%d/scale", i)},
|
||||||
|
Scope: &allScopes,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
mr.ResourceRules = append(mr.ResourceRules, rule)
|
||||||
|
}
|
||||||
|
|
||||||
|
criteria := &fakeCriteria{matchResources: mr}
|
||||||
|
attrs := admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{"autoscaling", "v1", "Scale"}, "ns", "name", schema.GroupVersionResource{"apps", "v1", "deployments"}, "scale", admission.Create, &metav1.CreateOptions{}, false, nil)
|
||||||
|
interfaces := &admission.RuntimeObjectInterfaces{EquivalentResourceMapper: mapper}
|
||||||
|
matcher := &Matcher{namespaceMatcher: &namespace.Matcher{NamespaceLister: namespaceLister}, objectMatcher: &object.Matcher{}}
|
||||||
|
|
||||||
|
for i := 0; i < b.N; i++ {
|
||||||
|
matcher.Matches(attrs, interfaces, criteria)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user