k8s.io/apiserver@v0.31.1/pkg/admission/plugin/policy/matching/matching_test.go (about)

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package matching
    18  
    19  import (
    20  	"fmt"
    21  	"strings"
    22  	"testing"
    23  
    24  	v1 "k8s.io/api/admissionregistration/v1"
    25  	corev1 "k8s.io/api/core/v1"
    26  	"k8s.io/apimachinery/pkg/api/errors"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/labels"
    29  	"k8s.io/apimachinery/pkg/runtime"
    30  	"k8s.io/apimachinery/pkg/runtime/schema"
    31  	"k8s.io/apiserver/pkg/admission"
    32  	"k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace"
    33  	"k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/object"
    34  	"k8s.io/apiserver/pkg/apis/example"
    35  )
    36  
    37  var _ MatchCriteria = &fakeCriteria{}
    38  
    39  type fakeCriteria struct {
    40  	matchResources v1.MatchResources
    41  }
    42  
    43  func (fc *fakeCriteria) GetMatchResources() v1.MatchResources {
    44  	return fc.matchResources
    45  }
    46  
    47  func (fc *fakeCriteria) GetParsedNamespaceSelector() (labels.Selector, error) {
    48  	return metav1.LabelSelectorAsSelector(fc.matchResources.NamespaceSelector)
    49  }
    50  
    51  func (fc *fakeCriteria) GetParsedObjectSelector() (labels.Selector, error) {
    52  	return metav1.LabelSelectorAsSelector(fc.matchResources.ObjectSelector)
    53  }
    54  
    55  func gvr(group, version, resource string) schema.GroupVersionResource {
    56  	return schema.GroupVersionResource{Group: group, Version: version, Resource: resource}
    57  }
    58  
    59  func gvk(group, version, kind string) schema.GroupVersionKind {
    60  	return schema.GroupVersionKind{Group: group, Version: version, Kind: kind}
    61  }
    62  
    63  func TestMatcher(t *testing.T) {
    64  	a := &Matcher{namespaceMatcher: &namespace.Matcher{}, objectMatcher: &object.Matcher{}}
    65  
    66  	allScopes := v1.AllScopes
    67  	exactMatch := v1.Exact
    68  	equivalentMatch := v1.Equivalent
    69  
    70  	mapper := runtime.NewEquivalentResourceRegistryWithIdentity(func(resource schema.GroupResource) string {
    71  		if resource.Resource == "deployments" {
    72  			// co-locate deployments in all API groups
    73  			return "/deployments"
    74  		}
    75  		return ""
    76  	})
    77  	mapper.RegisterKindFor(gvr("extensions", "v1beta1", "deployments"), "", gvk("extensions", "v1beta1", "Deployment"))
    78  	mapper.RegisterKindFor(gvr("apps", "v1", "deployments"), "", gvk("apps", "v1", "Deployment"))
    79  	mapper.RegisterKindFor(gvr("apps", "v1beta1", "deployments"), "", gvk("apps", "v1beta1", "Deployment"))
    80  	mapper.RegisterKindFor(gvr("apps", "v1alpha1", "deployments"), "", gvk("apps", "v1alpha1", "Deployment"))
    81  
    82  	mapper.RegisterKindFor(gvr("extensions", "v1beta1", "deployments"), "scale", gvk("extensions", "v1beta1", "Scale"))
    83  	mapper.RegisterKindFor(gvr("apps", "v1", "deployments"), "scale", gvk("autoscaling", "v1", "Scale"))
    84  	mapper.RegisterKindFor(gvr("apps", "v1beta1", "deployments"), "scale", gvk("apps", "v1beta1", "Scale"))
    85  	mapper.RegisterKindFor(gvr("apps", "v1alpha1", "deployments"), "scale", gvk("apps", "v1alpha1", "Scale"))
    86  
    87  	// register invalid kinds to trigger an error
    88  	mapper.RegisterKindFor(gvr("example.com", "v1", "widgets"), "", gvk("", "", ""))
    89  	mapper.RegisterKindFor(gvr("example.com", "v2", "widgets"), "", gvk("", "", ""))
    90  
    91  	interfaces := &admission.RuntimeObjectInterfaces{EquivalentResourceMapper: mapper}
    92  
    93  	// TODO write test cases for name matching and exclude matching
    94  	testcases := []struct {
    95  		name string
    96  
    97  		criteria *v1.MatchResources
    98  		attrs    admission.Attributes
    99  
   100  		expectMatches       bool
   101  		expectMatchKind     schema.GroupVersionKind
   102  		expectMatchResource schema.GroupVersionResource
   103  		expectErr           string
   104  	}{
   105  		{
   106  			name:          "no rules (just write)",
   107  			criteria:      &v1.MatchResources{NamespaceSelector: &metav1.LabelSelector{}, ResourceRules: []v1.NamedRuleWithOperations{}},
   108  			attrs:         admission.NewAttributesRecord(nil, nil, gvk("apps", "v1", "Deployment"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
   109  			expectMatches: false,
   110  		},
   111  		{
   112  			name: "wildcard rule, match as requested",
   113  			criteria: &v1.MatchResources{
   114  				NamespaceSelector: &metav1.LabelSelector{},
   115  				ObjectSelector:    &metav1.LabelSelector{},
   116  				ResourceRules: []v1.NamedRuleWithOperations{{
   117  					RuleWithOperations: v1.RuleWithOperations{
   118  						Operations: []v1.OperationType{"*"},
   119  						Rule:       v1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*"}, Scope: &allScopes},
   120  					},
   121  				}}},
   122  			attrs:           admission.NewAttributesRecord(nil, nil, gvk("apps", "v1", "Deployment"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
   123  			expectMatches:   true,
   124  			expectMatchKind: gvk("apps", "v1", "Deployment"),
   125  		},
   126  		{
   127  			name: "specific rules, prefer exact match",
   128  			criteria: &v1.MatchResources{
   129  				NamespaceSelector: &metav1.LabelSelector{},
   130  				ObjectSelector:    &metav1.LabelSelector{},
   131  				ResourceRules: []v1.NamedRuleWithOperations{{
   132  					RuleWithOperations: v1.RuleWithOperations{
   133  						Operations: []v1.OperationType{"*"},
   134  						Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, Scope: &allScopes},
   135  					},
   136  				}, {
   137  					RuleWithOperations: v1.RuleWithOperations{
   138  						Operations: []v1.OperationType{"*"},
   139  						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
   140  					},
   141  				}, {
   142  					RuleWithOperations: v1.RuleWithOperations{
   143  						Operations: []v1.OperationType{"*"},
   144  						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, Scope: &allScopes},
   145  					},
   146  				}}},
   147  			attrs:           admission.NewAttributesRecord(nil, nil, gvk("apps", "v1", "Deployment"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
   148  			expectMatches:   true,
   149  			expectMatchKind: gvk("apps", "v1", "Deployment"),
   150  		},
   151  		{
   152  			name: "specific rules, match miss",
   153  			criteria: &v1.MatchResources{
   154  				NamespaceSelector: &metav1.LabelSelector{},
   155  				ObjectSelector:    &metav1.LabelSelector{},
   156  				ResourceRules: []v1.NamedRuleWithOperations{{
   157  					RuleWithOperations: v1.RuleWithOperations{
   158  						Operations: []v1.OperationType{"*"},
   159  						Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
   160  					},
   161  				}, {
   162  					RuleWithOperations: v1.RuleWithOperations{
   163  						Operations: []v1.OperationType{"*"},
   164  						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
   165  					},
   166  				}}},
   167  			attrs:         admission.NewAttributesRecord(nil, nil, gvk("apps", "v1", "Deployment"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
   168  			expectMatches: false,
   169  		},
   170  		{
   171  			name: "specific rules, exact match miss",
   172  			criteria: &v1.MatchResources{
   173  				MatchPolicy:       &exactMatch,
   174  				NamespaceSelector: &metav1.LabelSelector{},
   175  				ObjectSelector:    &metav1.LabelSelector{},
   176  				ResourceRules: []v1.NamedRuleWithOperations{{
   177  					RuleWithOperations: v1.RuleWithOperations{
   178  						Operations: []v1.OperationType{"*"},
   179  						Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
   180  					},
   181  				}, {
   182  					RuleWithOperations: v1.RuleWithOperations{
   183  						Operations: []v1.OperationType{"*"},
   184  						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
   185  					},
   186  				}}},
   187  			attrs:         admission.NewAttributesRecord(nil, nil, gvk("apps", "v1", "Deployment"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
   188  			expectMatches: false,
   189  		},
   190  		{
   191  			name: "specific rules, equivalent match, prefer extensions",
   192  			criteria: &v1.MatchResources{
   193  				MatchPolicy:       &equivalentMatch,
   194  				NamespaceSelector: &metav1.LabelSelector{},
   195  				ObjectSelector:    &metav1.LabelSelector{},
   196  				ResourceRules: []v1.NamedRuleWithOperations{{
   197  					RuleWithOperations: v1.RuleWithOperations{
   198  						Operations: []v1.OperationType{"*"},
   199  						Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
   200  					},
   201  				}, {
   202  					RuleWithOperations: v1.RuleWithOperations{
   203  						Operations: []v1.OperationType{"*"},
   204  						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
   205  					},
   206  				}}},
   207  			attrs:               admission.NewAttributesRecord(nil, nil, gvk("apps", "v1", "Deployment"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
   208  			expectMatches:       true,
   209  			expectMatchResource: gvr("extensions", "v1beta1", "deployments"),
   210  			expectMatchKind:     gvk("extensions", "v1beta1", "Deployment"),
   211  		},
   212  		{
   213  			name: "specific rules, equivalent match, prefer apps",
   214  			criteria: &v1.MatchResources{
   215  				MatchPolicy:       &equivalentMatch,
   216  				NamespaceSelector: &metav1.LabelSelector{},
   217  				ObjectSelector:    &metav1.LabelSelector{},
   218  				ResourceRules: []v1.NamedRuleWithOperations{{
   219  					RuleWithOperations: v1.RuleWithOperations{
   220  						Operations: []v1.OperationType{"*"},
   221  						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
   222  					},
   223  				}, {
   224  					RuleWithOperations: v1.RuleWithOperations{
   225  						Operations: []v1.OperationType{"*"},
   226  						Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
   227  					},
   228  				}}},
   229  			attrs:               admission.NewAttributesRecord(nil, nil, gvk("apps", "v1", "Deployment"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
   230  			expectMatches:       true,
   231  			expectMatchResource: gvr("apps", "v1beta1", "deployments"),
   232  			expectMatchKind:     gvk("apps", "v1beta1", "Deployment"),
   233  		},
   234  
   235  		{
   236  			name: "specific rules, subresource prefer exact match",
   237  			criteria: &v1.MatchResources{
   238  				NamespaceSelector: &metav1.LabelSelector{},
   239  				ObjectSelector:    &metav1.LabelSelector{},
   240  				ResourceRules: []v1.NamedRuleWithOperations{{
   241  					RuleWithOperations: v1.RuleWithOperations{
   242  						Operations: []v1.OperationType{"*"},
   243  						Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
   244  					},
   245  				}, {
   246  					RuleWithOperations: v1.RuleWithOperations{
   247  						Operations: []v1.OperationType{"*"},
   248  						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
   249  					},
   250  				}, {
   251  					RuleWithOperations: v1.RuleWithOperations{
   252  						Operations: []v1.OperationType{"*"},
   253  						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
   254  					},
   255  				}}},
   256  			attrs:           admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
   257  			expectMatches:   true,
   258  			expectMatchKind: gvk("autoscaling", "v1", "Scale"),
   259  		},
   260  		{
   261  			name: "specific rules, subresource match miss",
   262  			criteria: &v1.MatchResources{
   263  				NamespaceSelector: &metav1.LabelSelector{},
   264  				ObjectSelector:    &metav1.LabelSelector{},
   265  				ResourceRules: []v1.NamedRuleWithOperations{{
   266  					RuleWithOperations: v1.RuleWithOperations{
   267  						Operations: []v1.OperationType{"*"},
   268  						Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
   269  					},
   270  				}, {
   271  					RuleWithOperations: v1.RuleWithOperations{
   272  						Operations: []v1.OperationType{"*"},
   273  						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
   274  					},
   275  				}}},
   276  			attrs:         admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
   277  			expectMatches: false,
   278  		},
   279  		{
   280  			name: "specific rules, subresource exact match miss",
   281  			criteria: &v1.MatchResources{
   282  				MatchPolicy:       &exactMatch,
   283  				NamespaceSelector: &metav1.LabelSelector{},
   284  				ObjectSelector:    &metav1.LabelSelector{},
   285  				ResourceRules: []v1.NamedRuleWithOperations{{
   286  					RuleWithOperations: v1.RuleWithOperations{
   287  						Operations: []v1.OperationType{"*"},
   288  						Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
   289  					},
   290  				}, {
   291  					RuleWithOperations: v1.RuleWithOperations{
   292  						Operations: []v1.OperationType{"*"},
   293  						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
   294  					},
   295  				}}},
   296  			attrs:         admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
   297  			expectMatches: false,
   298  		},
   299  		{
   300  			name: "specific rules, subresource equivalent match, prefer extensions",
   301  			criteria: &v1.MatchResources{
   302  				MatchPolicy:       &equivalentMatch,
   303  				NamespaceSelector: &metav1.LabelSelector{},
   304  				ObjectSelector:    &metav1.LabelSelector{},
   305  				ResourceRules: []v1.NamedRuleWithOperations{{
   306  					RuleWithOperations: v1.RuleWithOperations{
   307  						Operations: []v1.OperationType{"*"},
   308  						Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
   309  					},
   310  				}, {
   311  					RuleWithOperations: v1.RuleWithOperations{
   312  						Operations: []v1.OperationType{"*"},
   313  						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
   314  					},
   315  				}}},
   316  			attrs:               admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
   317  			expectMatches:       true,
   318  			expectMatchResource: gvr("extensions", "v1beta1", "deployments"),
   319  			expectMatchKind:     gvk("extensions", "v1beta1", "Scale"),
   320  		},
   321  		{
   322  			name: "specific rules, subresource equivalent match, prefer apps",
   323  			criteria: &v1.MatchResources{
   324  				MatchPolicy:       &equivalentMatch,
   325  				NamespaceSelector: &metav1.LabelSelector{},
   326  				ObjectSelector:    &metav1.LabelSelector{},
   327  				ResourceRules: []v1.NamedRuleWithOperations{{
   328  					RuleWithOperations: v1.RuleWithOperations{
   329  						Operations: []v1.OperationType{"*"},
   330  						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
   331  					},
   332  				}, {
   333  					RuleWithOperations: v1.RuleWithOperations{
   334  						Operations: []v1.OperationType{"*"},
   335  						Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
   336  					},
   337  				}}},
   338  			attrs:               admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
   339  			expectMatches:       true,
   340  			expectMatchResource: gvr("apps", "v1beta1", "deployments"),
   341  			expectMatchKind:     gvk("apps", "v1beta1", "Scale"),
   342  		},
   343  		{
   344  			name: "specific rules, prefer exact match and name match",
   345  			criteria: &v1.MatchResources{
   346  				NamespaceSelector: &metav1.LabelSelector{},
   347  				ObjectSelector:    &metav1.LabelSelector{},
   348  				ResourceRules: []v1.NamedRuleWithOperations{{
   349  					ResourceNames: []string{"name"},
   350  					RuleWithOperations: v1.RuleWithOperations{
   351  						Operations: []v1.OperationType{"*"},
   352  						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, Scope: &allScopes},
   353  					},
   354  				}}},
   355  			attrs:           admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
   356  			expectMatches:   true,
   357  			expectMatchKind: gvk("autoscaling", "v1", "Scale"),
   358  		},
   359  		{
   360  			name: "specific rules, prefer exact match and name match miss",
   361  			criteria: &v1.MatchResources{
   362  				NamespaceSelector: &metav1.LabelSelector{},
   363  				ObjectSelector:    &metav1.LabelSelector{},
   364  				ResourceRules: []v1.NamedRuleWithOperations{{
   365  					ResourceNames: []string{"wrong-name"},
   366  					RuleWithOperations: v1.RuleWithOperations{
   367  						Operations: []v1.OperationType{"*"},
   368  						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments"}, Scope: &allScopes},
   369  					},
   370  				}}},
   371  			attrs:         admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
   372  			expectMatches: false,
   373  		},
   374  		{
   375  			name: "specific rules, subresource equivalent match, prefer extensions and name match",
   376  			criteria: &v1.MatchResources{
   377  				MatchPolicy:       &equivalentMatch,
   378  				NamespaceSelector: &metav1.LabelSelector{},
   379  				ObjectSelector:    &metav1.LabelSelector{},
   380  				ResourceRules: []v1.NamedRuleWithOperations{{
   381  					ResourceNames: []string{"name"},
   382  					RuleWithOperations: v1.RuleWithOperations{
   383  						Operations: []v1.OperationType{"*"},
   384  						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
   385  					},
   386  				}}},
   387  			attrs:               admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("extensions", "v1beta1", "deployments"), "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
   388  			expectMatches:       true,
   389  			expectMatchResource: gvr("apps", "v1", "deployments"),
   390  			expectMatchKind:     gvk("autoscaling", "v1", "Scale"),
   391  		},
   392  		{
   393  			name: "specific rules, subresource equivalent match, prefer extensions and name match miss",
   394  			criteria: &v1.MatchResources{
   395  				MatchPolicy:       &equivalentMatch,
   396  				NamespaceSelector: &metav1.LabelSelector{},
   397  				ObjectSelector:    &metav1.LabelSelector{},
   398  				ResourceRules: []v1.NamedRuleWithOperations{{
   399  					ResourceNames: []string{"wrong-name"},
   400  					RuleWithOperations: v1.RuleWithOperations{
   401  						Operations: []v1.OperationType{"*"},
   402  						Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
   403  					},
   404  				}}},
   405  			attrs:         admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("extensions", "v1beta1", "deployments"), "scale", admission.Create, &metav1.CreateOptions{}, false, nil),
   406  			expectMatches: false,
   407  		},
   408  		{
   409  			name: "exclude resource match on miss",
   410  			criteria: &v1.MatchResources{
   411  				NamespaceSelector: &metav1.LabelSelector{},
   412  				ObjectSelector:    &metav1.LabelSelector{},
   413  				ResourceRules: []v1.NamedRuleWithOperations{{
   414  					RuleWithOperations: v1.RuleWithOperations{
   415  						Operations: []v1.OperationType{"*"},
   416  						Rule:       v1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*"}, Scope: &allScopes},
   417  					},
   418  				}},
   419  				ExcludeResourceRules: []v1.NamedRuleWithOperations{{
   420  					RuleWithOperations: v1.RuleWithOperations{
   421  						Operations: []v1.OperationType{"*"},
   422  						Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
   423  					},
   424  				}},
   425  			},
   426  			attrs:           admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
   427  			expectMatches:   true,
   428  			expectMatchKind: gvk("autoscaling", "v1", "Scale"),
   429  		},
   430  		{
   431  			name: "exclude resource miss on match",
   432  			criteria: &v1.MatchResources{
   433  				NamespaceSelector: &metav1.LabelSelector{},
   434  				ObjectSelector:    &metav1.LabelSelector{},
   435  				ResourceRules: []v1.NamedRuleWithOperations{{
   436  					RuleWithOperations: v1.RuleWithOperations{
   437  						Operations: []v1.OperationType{"*"},
   438  						Rule:       v1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"*"}, Scope: &allScopes},
   439  					},
   440  				}},
   441  				ExcludeResourceRules: []v1.NamedRuleWithOperations{{
   442  					RuleWithOperations: v1.RuleWithOperations{
   443  						Operations: []v1.OperationType{"*"},
   444  						Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
   445  					},
   446  				}},
   447  			},
   448  			attrs:         admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("extensions", "v1beta1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
   449  			expectMatches: false,
   450  		},
   451  		{
   452  			name: "treat empty ResourceRules as match",
   453  			criteria: &v1.MatchResources{
   454  				NamespaceSelector: &metav1.LabelSelector{},
   455  				ObjectSelector:    &metav1.LabelSelector{},
   456  				ExcludeResourceRules: []v1.NamedRuleWithOperations{{
   457  					RuleWithOperations: v1.RuleWithOperations{
   458  						Operations: []v1.OperationType{"*"},
   459  						Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments"}, Scope: &allScopes},
   460  					},
   461  				}},
   462  			},
   463  			attrs:         admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
   464  			expectMatches: true,
   465  		},
   466  		{
   467  			name: "treat non-empty ResourceRules as no match",
   468  			criteria: &v1.MatchResources{
   469  				NamespaceSelector: &metav1.LabelSelector{},
   470  				ObjectSelector:    &metav1.LabelSelector{},
   471  				ResourceRules:     []v1.NamedRuleWithOperations{{}},
   472  			},
   473  			attrs:         admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
   474  			expectMatches: false,
   475  		},
   476  		{
   477  			name: "erroring namespace selector on otherwise non-matching rule doesn't error",
   478  			criteria: &v1.MatchResources{
   479  				NamespaceSelector: &metav1.LabelSelector{MatchExpressions: []metav1.LabelSelectorRequirement{{Key: "key ", Operator: "In", Values: []string{"bad value"}}}},
   480  				ObjectSelector:    &metav1.LabelSelector{},
   481  				ResourceRules: []v1.NamedRuleWithOperations{{
   482  					RuleWithOperations: v1.RuleWithOperations{
   483  						Rule:       v1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"deployments"}},
   484  						Operations: []v1.OperationType{"*"},
   485  					},
   486  				}},
   487  			},
   488  			attrs:         admission.NewAttributesRecord(&example.Pod{}, nil, gvk("example.apiserver.k8s.io", "v1", "Pod"), "ns", "name", gvr("example.apiserver.k8s.io", "v1", "pods"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
   489  			expectMatches: false,
   490  			expectErr:     "",
   491  		},
   492  		{
   493  			name: "erroring namespace selector on otherwise matching rule errors",
   494  			criteria: &v1.MatchResources{
   495  				NamespaceSelector: &metav1.LabelSelector{MatchExpressions: []metav1.LabelSelectorRequirement{{Key: "key", Operator: "In", Values: []string{"bad value"}}}},
   496  				ObjectSelector:    &metav1.LabelSelector{},
   497  				ResourceRules: []v1.NamedRuleWithOperations{{
   498  					RuleWithOperations: v1.RuleWithOperations{
   499  						Rule:       v1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"pods"}},
   500  						Operations: []v1.OperationType{"*"},
   501  					},
   502  				}},
   503  			},
   504  			attrs:         admission.NewAttributesRecord(&example.Pod{}, nil, gvk("example.apiserver.k8s.io", "v1", "Pod"), "ns", "name", gvr("example.apiserver.k8s.io", "v1", "pods"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
   505  			expectMatches: false,
   506  			expectErr:     "bad value",
   507  		},
   508  		{
   509  			name: "erroring object selector on otherwise non-matching rule doesn't error",
   510  			criteria: &v1.MatchResources{
   511  				NamespaceSelector: &metav1.LabelSelector{},
   512  				ObjectSelector:    &metav1.LabelSelector{MatchExpressions: []metav1.LabelSelectorRequirement{{Key: "key", Operator: "In", Values: []string{"bad value"}}}},
   513  				ResourceRules: []v1.NamedRuleWithOperations{{
   514  					RuleWithOperations: v1.RuleWithOperations{
   515  						Rule:       v1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"deployments"}},
   516  						Operations: []v1.OperationType{"*"},
   517  					},
   518  				}},
   519  			},
   520  			attrs:         admission.NewAttributesRecord(&example.Pod{}, nil, gvk("example.apiserver.k8s.io", "v1", "Pod"), "ns", "name", gvr("example.apiserver.k8s.io", "v1", "pods"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
   521  			expectMatches: false,
   522  			expectErr:     "",
   523  		},
   524  		{
   525  			name: "erroring object selector on otherwise matching rule errors",
   526  			criteria: &v1.MatchResources{
   527  				NamespaceSelector: &metav1.LabelSelector{},
   528  				ObjectSelector:    &metav1.LabelSelector{MatchExpressions: []metav1.LabelSelectorRequirement{{Key: "key", Operator: "In", Values: []string{"bad value"}}}},
   529  				ResourceRules: []v1.NamedRuleWithOperations{{
   530  					RuleWithOperations: v1.RuleWithOperations{
   531  						Rule:       v1.Rule{APIGroups: []string{"*"}, APIVersions: []string{"*"}, Resources: []string{"pods"}},
   532  						Operations: []v1.OperationType{"*"},
   533  					},
   534  				}},
   535  			},
   536  			attrs:         admission.NewAttributesRecord(&example.Pod{}, nil, gvk("example.apiserver.k8s.io", "v1", "Pod"), "ns", "name", gvr("example.apiserver.k8s.io", "v1", "pods"), "", admission.Create, &metav1.CreateOptions{}, false, nil),
   537  			expectMatches: false,
   538  			expectErr:     "bad value",
   539  		},
   540  	}
   541  
   542  	for _, testcase := range testcases {
   543  		t.Run(testcase.name, func(t *testing.T) {
   544  			matches, matchResource, matchKind, err := a.Matches(testcase.attrs, interfaces, &fakeCriteria{matchResources: *testcase.criteria})
   545  			if err != nil {
   546  				if len(testcase.expectErr) == 0 {
   547  					t.Fatal(err)
   548  				}
   549  				if !strings.Contains(err.Error(), testcase.expectErr) {
   550  					t.Fatalf("expected error containing %q, got %s", testcase.expectErr, err.Error())
   551  				}
   552  				return
   553  			} else if len(testcase.expectErr) > 0 {
   554  				t.Fatalf("expected error %q, got no error", testcase.expectErr)
   555  			}
   556  			var emptyGVK schema.GroupVersionKind
   557  			if testcase.expectMatchKind != emptyGVK {
   558  				if testcase.expectMatchKind != matchKind {
   559  					t.Fatalf("expected matchKind %v, got %v", testcase.expectMatchKind, matchKind)
   560  				}
   561  			}
   562  
   563  			if matches != testcase.expectMatches {
   564  				t.Fatalf("expected matches = %v; got %v", testcase.expectMatches, matches)
   565  			}
   566  
   567  			expectResource := testcase.expectMatchResource
   568  			if !expectResource.Empty() && !matches {
   569  				t.Fatalf("expectResource is non-empty, but did not match")
   570  			} else if expectResource.Empty() {
   571  				// Test for exact match by default. Tests that expect an equivalent
   572  				// resource to match should explicitly state so by supplying
   573  				// expectMatchResource
   574  				expectResource = testcase.attrs.GetResource()
   575  			}
   576  
   577  			if matches {
   578  				if matchResource != expectResource {
   579  					t.Fatalf("expected matchResource %v, got %v", expectResource, matchResource)
   580  				}
   581  			}
   582  		})
   583  	}
   584  }
   585  
   586  type fakeNamespaceLister struct {
   587  	namespaces map[string]*corev1.Namespace
   588  }
   589  
   590  func (f fakeNamespaceLister) List(selector labels.Selector) (ret []*corev1.Namespace, err error) {
   591  	return nil, nil
   592  }
   593  func (f fakeNamespaceLister) Get(name string) (*corev1.Namespace, error) {
   594  	ns, ok := f.namespaces[name]
   595  	if ok {
   596  		return ns, nil
   597  	}
   598  	return nil, errors.NewNotFound(corev1.Resource("namespaces"), name)
   599  }
   600  
   601  func BenchmarkMatcher(b *testing.B) {
   602  	allScopes := v1.AllScopes
   603  	equivalentMatch := v1.Equivalent
   604  
   605  	namespace1Labels := map[string]string{"ns": "ns1"}
   606  	namespace1 := corev1.Namespace{
   607  		ObjectMeta: metav1.ObjectMeta{
   608  			Name:   "ns1",
   609  			Labels: namespace1Labels,
   610  		},
   611  	}
   612  	namespaceLister := fakeNamespaceLister{map[string]*corev1.Namespace{"ns": &namespace1}}
   613  
   614  	mapper := runtime.NewEquivalentResourceRegistryWithIdentity(func(resource schema.GroupResource) string {
   615  		if resource.Resource == "deployments" {
   616  			// co-locate deployments in all API groups
   617  			return "/deployments"
   618  		}
   619  		return ""
   620  	})
   621  	mapper.RegisterKindFor(gvr("extensions", "v1beta1", "deployments"), "", gvk("extensions", "v1beta1", "Deployment"))
   622  	mapper.RegisterKindFor(gvr("apps", "v1", "deployments"), "", gvk("apps", "v1", "Deployment"))
   623  	mapper.RegisterKindFor(gvr("apps", "v1beta1", "deployments"), "", gvk("apps", "v1beta1", "Deployment"))
   624  	mapper.RegisterKindFor(gvr("apps", "v1alpha1", "deployments"), "", gvk("apps", "v1alpha1", "Deployment"))
   625  
   626  	mapper.RegisterKindFor(gvr("extensions", "v1beta1", "deployments"), "scale", gvk("extensions", "v1beta1", "Scale"))
   627  	mapper.RegisterKindFor(gvr("apps", "v1", "deployments"), "scale", gvk("autoscaling", "v1", "Scale"))
   628  	mapper.RegisterKindFor(gvr("apps", "v1beta1", "deployments"), "scale", gvk("apps", "v1beta1", "Scale"))
   629  	mapper.RegisterKindFor(gvr("apps", "v1alpha1", "deployments"), "scale", gvk("apps", "v1alpha1", "Scale"))
   630  
   631  	mapper.RegisterKindFor(gvr("apps", "v1", "statefulset"), "", gvk("apps", "v1", "StatefulSet"))
   632  	mapper.RegisterKindFor(gvr("apps", "v1beta1", "statefulset"), "", gvk("apps", "v1beta1", "StatefulSet"))
   633  	mapper.RegisterKindFor(gvr("apps", "v1beta2", "statefulset"), "", gvk("apps", "v1beta2", "StatefulSet"))
   634  
   635  	mapper.RegisterKindFor(gvr("apps", "v1", "statefulset"), "scale", gvk("apps", "v1", "Scale"))
   636  	mapper.RegisterKindFor(gvr("apps", "v1beta1", "statefulset"), "scale", gvk("apps", "v1beta1", "Scale"))
   637  	mapper.RegisterKindFor(gvr("apps", "v1alpha2", "statefulset"), "scale", gvk("apps", "v1beta2", "Scale"))
   638  
   639  	nsSelector := make(map[string]string)
   640  	for i := 0; i < 100; i++ {
   641  		nsSelector[fmt.Sprintf("key-%d", i)] = fmt.Sprintf("val-%d", i)
   642  	}
   643  
   644  	mr := v1.MatchResources{
   645  		MatchPolicy:       &equivalentMatch,
   646  		NamespaceSelector: &metav1.LabelSelector{MatchLabels: nsSelector},
   647  		ObjectSelector:    &metav1.LabelSelector{},
   648  		ResourceRules: []v1.NamedRuleWithOperations{
   649  			{
   650  				RuleWithOperations: v1.RuleWithOperations{
   651  					Operations: []v1.OperationType{"*"},
   652  					Rule:       v1.Rule{APIGroups: []string{"apps"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
   653  				},
   654  			},
   655  			{
   656  				RuleWithOperations: v1.RuleWithOperations{
   657  					Operations: []v1.OperationType{"*"},
   658  					Rule:       v1.Rule{APIGroups: []string{"extensions"}, APIVersions: []string{"v1beta1"}, Resources: []string{"deployments", "deployments/scale"}, Scope: &allScopes},
   659  				},
   660  			},
   661  		},
   662  	}
   663  
   664  	criteria := &fakeCriteria{matchResources: mr}
   665  	attrs := admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "scale", admission.Create, &metav1.CreateOptions{}, false, nil)
   666  	interfaces := &admission.RuntimeObjectInterfaces{EquivalentResourceMapper: mapper}
   667  	matcher := &Matcher{namespaceMatcher: &namespace.Matcher{NamespaceLister: namespaceLister}, objectMatcher: &object.Matcher{}}
   668  
   669  	for i := 0; i < b.N; i++ {
   670  		matcher.Matches(attrs, interfaces, criteria)
   671  	}
   672  }
   673  
   674  func BenchmarkShouldCallHookWithComplexRule(b *testing.B) {
   675  	allScopes := v1.AllScopes
   676  	equivalentMatch := v1.Equivalent
   677  
   678  	namespace1Labels := map[string]string{"ns": "ns1"}
   679  	namespace1 := corev1.Namespace{
   680  		ObjectMeta: metav1.ObjectMeta{
   681  			Name:   "ns1",
   682  			Labels: namespace1Labels,
   683  		},
   684  	}
   685  	namespaceLister := fakeNamespaceLister{map[string]*corev1.Namespace{"ns": &namespace1}}
   686  
   687  	mapper := runtime.NewEquivalentResourceRegistryWithIdentity(func(resource schema.GroupResource) string {
   688  		if resource.Resource == "deployments" {
   689  			// co-locate deployments in all API groups
   690  			return "/deployments"
   691  		}
   692  		return ""
   693  	})
   694  	mapper.RegisterKindFor(gvr("extensions", "v1beta1", "deployments"), "", gvk("extensions", "v1beta1", "Deployment"))
   695  	mapper.RegisterKindFor(gvr("apps", "v1", "deployments"), "", gvk("apps", "v1", "Deployment"))
   696  	mapper.RegisterKindFor(gvr("apps", "v1beta1", "deployments"), "", gvk("apps", "v1beta1", "Deployment"))
   697  	mapper.RegisterKindFor(gvr("apps", "v1alpha1", "deployments"), "", gvk("apps", "v1alpha1", "Deployment"))
   698  
   699  	mapper.RegisterKindFor(gvr("extensions", "v1beta1", "deployments"), "scale", gvk("extensions", "v1beta1", "Scale"))
   700  	mapper.RegisterKindFor(gvr("apps", "v1", "deployments"), "scale", gvk("autoscaling", "v1", "Scale"))
   701  	mapper.RegisterKindFor(gvr("apps", "v1beta1", "deployments"), "scale", gvk("apps", "v1beta1", "Scale"))
   702  	mapper.RegisterKindFor(gvr("apps", "v1alpha1", "deployments"), "scale", gvk("apps", "v1alpha1", "Scale"))
   703  
   704  	mapper.RegisterKindFor(gvr("apps", "v1", "statefulset"), "", gvk("apps", "v1", "StatefulSet"))
   705  	mapper.RegisterKindFor(gvr("apps", "v1beta1", "statefulset"), "", gvk("apps", "v1beta1", "StatefulSet"))
   706  	mapper.RegisterKindFor(gvr("apps", "v1beta2", "statefulset"), "", gvk("apps", "v1beta2", "StatefulSet"))
   707  
   708  	mapper.RegisterKindFor(gvr("apps", "v1", "statefulset"), "scale", gvk("apps", "v1", "Scale"))
   709  	mapper.RegisterKindFor(gvr("apps", "v1beta1", "statefulset"), "scale", gvk("apps", "v1beta1", "Scale"))
   710  	mapper.RegisterKindFor(gvr("apps", "v1alpha2", "statefulset"), "scale", gvk("apps", "v1beta2", "Scale"))
   711  
   712  	mr := v1.MatchResources{
   713  		MatchPolicy:       &equivalentMatch,
   714  		NamespaceSelector: &metav1.LabelSelector{MatchLabels: map[string]string{"a": "b"}},
   715  		ObjectSelector:    &metav1.LabelSelector{},
   716  		ResourceRules:     []v1.NamedRuleWithOperations{},
   717  	}
   718  
   719  	for i := 0; i < 100; i++ {
   720  		rule := v1.NamedRuleWithOperations{
   721  			RuleWithOperations: v1.RuleWithOperations{
   722  				Operations: []v1.OperationType{"*"},
   723  				Rule: v1.Rule{
   724  					APIGroups:   []string{fmt.Sprintf("app-%d", i)},
   725  					APIVersions: []string{fmt.Sprintf("v%d", i)},
   726  					Resources:   []string{fmt.Sprintf("resource%d", i), fmt.Sprintf("resource%d/scale", i)},
   727  					Scope:       &allScopes,
   728  				},
   729  			},
   730  		}
   731  		mr.ResourceRules = append(mr.ResourceRules, rule)
   732  	}
   733  
   734  	criteria := &fakeCriteria{matchResources: mr}
   735  	attrs := admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "scale", admission.Create, &metav1.CreateOptions{}, false, nil)
   736  	interfaces := &admission.RuntimeObjectInterfaces{EquivalentResourceMapper: mapper}
   737  	matcher := &Matcher{namespaceMatcher: &namespace.Matcher{NamespaceLister: namespaceLister}, objectMatcher: &object.Matcher{}}
   738  
   739  	for i := 0; i < b.N; i++ {
   740  		matcher.Matches(attrs, interfaces, criteria)
   741  	}
   742  }
   743  
   744  func BenchmarkShouldCallHookWithComplexSelectorAndRule(b *testing.B) {
   745  	allScopes := v1.AllScopes
   746  	equivalentMatch := v1.Equivalent
   747  
   748  	namespace1Labels := map[string]string{"ns": "ns1"}
   749  	namespace1 := corev1.Namespace{
   750  		ObjectMeta: metav1.ObjectMeta{
   751  			Name:   "ns1",
   752  			Labels: namespace1Labels,
   753  		},
   754  	}
   755  	namespaceLister := fakeNamespaceLister{map[string]*corev1.Namespace{"ns": &namespace1}}
   756  
   757  	mapper := runtime.NewEquivalentResourceRegistryWithIdentity(func(resource schema.GroupResource) string {
   758  		if resource.Resource == "deployments" {
   759  			// co-locate deployments in all API groups
   760  			return "/deployments"
   761  		}
   762  		return ""
   763  	})
   764  	mapper.RegisterKindFor(gvr("extensions", "v1beta1", "deployments"), "", gvk("extensions", "v1beta1", "Deployment"))
   765  	mapper.RegisterKindFor(gvr("apps", "v1", "deployments"), "", gvk("apps", "v1", "Deployment"))
   766  	mapper.RegisterKindFor(gvr("apps", "v1beta1", "deployments"), "", gvk("apps", "v1beta1", "Deployment"))
   767  	mapper.RegisterKindFor(gvr("apps", "v1alpha1", "deployments"), "", gvk("apps", "v1alpha1", "Deployment"))
   768  
   769  	mapper.RegisterKindFor(gvr("extensions", "v1beta1", "deployments"), "scale", gvk("extensions", "v1beta1", "Scale"))
   770  	mapper.RegisterKindFor(gvr("apps", "v1", "deployments"), "scale", gvk("autoscaling", "v1", "Scale"))
   771  	mapper.RegisterKindFor(gvr("apps", "v1beta1", "deployments"), "scale", gvk("apps", "v1beta1", "Scale"))
   772  	mapper.RegisterKindFor(gvr("apps", "v1alpha1", "deployments"), "scale", gvk("apps", "v1alpha1", "Scale"))
   773  
   774  	mapper.RegisterKindFor(gvr("apps", "v1", "statefulset"), "", gvk("apps", "v1", "StatefulSet"))
   775  	mapper.RegisterKindFor(gvr("apps", "v1beta1", "statefulset"), "", gvk("apps", "v1beta1", "StatefulSet"))
   776  	mapper.RegisterKindFor(gvr("apps", "v1beta2", "statefulset"), "", gvk("apps", "v1beta2", "StatefulSet"))
   777  
   778  	mapper.RegisterKindFor(gvr("apps", "v1", "statefulset"), "scale", gvk("apps", "v1", "Scale"))
   779  	mapper.RegisterKindFor(gvr("apps", "v1beta1", "statefulset"), "scale", gvk("apps", "v1beta1", "Scale"))
   780  	mapper.RegisterKindFor(gvr("apps", "v1alpha2", "statefulset"), "scale", gvk("apps", "v1beta2", "Scale"))
   781  
   782  	nsSelector := make(map[string]string)
   783  	for i := 0; i < 100; i++ {
   784  		nsSelector[fmt.Sprintf("key-%d", i)] = fmt.Sprintf("val-%d", i)
   785  	}
   786  
   787  	mr := v1.MatchResources{
   788  		MatchPolicy:       &equivalentMatch,
   789  		NamespaceSelector: &metav1.LabelSelector{MatchLabels: nsSelector},
   790  		ObjectSelector:    &metav1.LabelSelector{},
   791  		ResourceRules:     []v1.NamedRuleWithOperations{},
   792  	}
   793  
   794  	for i := 0; i < 100; i++ {
   795  		rule := v1.NamedRuleWithOperations{
   796  			RuleWithOperations: v1.RuleWithOperations{
   797  				Operations: []v1.OperationType{"*"},
   798  				Rule: v1.Rule{
   799  					APIGroups:   []string{fmt.Sprintf("app-%d", i)},
   800  					APIVersions: []string{fmt.Sprintf("v%d", i)},
   801  					Resources:   []string{fmt.Sprintf("resource%d", i), fmt.Sprintf("resource%d/scale", i)},
   802  					Scope:       &allScopes,
   803  				},
   804  			},
   805  		}
   806  		mr.ResourceRules = append(mr.ResourceRules, rule)
   807  	}
   808  
   809  	criteria := &fakeCriteria{matchResources: mr}
   810  	attrs := admission.NewAttributesRecord(nil, nil, gvk("autoscaling", "v1", "Scale"), "ns", "name", gvr("apps", "v1", "deployments"), "scale", admission.Create, &metav1.CreateOptions{}, false, nil)
   811  	interfaces := &admission.RuntimeObjectInterfaces{EquivalentResourceMapper: mapper}
   812  	matcher := &Matcher{namespaceMatcher: &namespace.Matcher{NamespaceLister: namespaceLister}, objectMatcher: &object.Matcher{}}
   813  
   814  	for i := 0; i < b.N; i++ {
   815  		matcher.Matches(attrs, interfaces, criteria)
   816  	}
   817  }