k8s.io/apiserver@v0.31.1/pkg/admission/plugin/webhook/predicates/namespace/matcher_test.go (about)

     1  /*
     2  Copyright 2017 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 namespace_test
    18  
    19  import (
    20  	"reflect"
    21  	"testing"
    22  
    23  	registrationv1 "k8s.io/api/admissionregistration/v1"
    24  	corev1 "k8s.io/api/core/v1"
    25  	"k8s.io/apimachinery/pkg/api/errors"
    26  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/apimachinery/pkg/labels"
    28  	"k8s.io/apimachinery/pkg/runtime/schema"
    29  	"k8s.io/apiserver/pkg/admission"
    30  	"k8s.io/apiserver/pkg/admission/plugin/webhook"
    31  	"k8s.io/apiserver/pkg/admission/plugin/webhook/predicates/namespace"
    32  )
    33  
    34  type fakeNamespaceLister struct {
    35  	namespaces map[string]*corev1.Namespace
    36  }
    37  
    38  func (f fakeNamespaceLister) List(selector labels.Selector) (ret []*corev1.Namespace, err error) {
    39  	return nil, nil
    40  }
    41  func (f fakeNamespaceLister) Get(name string) (*corev1.Namespace, error) {
    42  	ns, ok := f.namespaces[name]
    43  	if ok {
    44  		return ns, nil
    45  	}
    46  	return nil, errors.NewNotFound(corev1.Resource("namespaces"), name)
    47  }
    48  
    49  func TestGetNamespaceLabels(t *testing.T) {
    50  	namespace1Labels := map[string]string{
    51  		"runlevel": "1",
    52  	}
    53  	namespace1 := corev1.Namespace{
    54  		ObjectMeta: metav1.ObjectMeta{
    55  			Name:   "1",
    56  			Labels: namespace1Labels,
    57  		},
    58  	}
    59  	namespace2Labels := map[string]string{
    60  		"runlevel": "2",
    61  	}
    62  	namespace2 := corev1.Namespace{
    63  		ObjectMeta: metav1.ObjectMeta{
    64  			Name:   "2",
    65  			Labels: namespace2Labels,
    66  		},
    67  	}
    68  	namespaceLister := fakeNamespaceLister{map[string]*corev1.Namespace{
    69  		"1": &namespace1,
    70  	},
    71  	}
    72  
    73  	tests := []struct {
    74  		name           string
    75  		attr           admission.Attributes
    76  		expectedLabels map[string]string
    77  	}{
    78  		{
    79  			name:           "request is for creating namespace, the labels should be from the object itself",
    80  			attr:           admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, "", namespace2.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
    81  			expectedLabels: namespace2Labels,
    82  		},
    83  		{
    84  			name:           "request is for updating namespace, the labels should be from the new object",
    85  			attr:           admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, namespace2.Name, namespace2.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Update, &metav1.UpdateOptions{}, false, nil),
    86  			expectedLabels: namespace2Labels,
    87  		},
    88  		{
    89  			name:           "request is for deleting namespace, the labels should be from the cache",
    90  			attr:           admission.NewAttributesRecord(&namespace2, nil, schema.GroupVersionKind{}, namespace1.Name, namespace1.Name, schema.GroupVersionResource{Resource: "namespaces"}, "", admission.Delete, &metav1.DeleteOptions{}, false, nil),
    91  			expectedLabels: namespace1Labels,
    92  		},
    93  		{
    94  			name:           "request is for namespace/finalizer",
    95  			attr:           admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, namespace1.Name, "mock-name", schema.GroupVersionResource{Resource: "namespaces"}, "finalizers", admission.Create, &metav1.CreateOptions{}, false, nil),
    96  			expectedLabels: namespace1Labels,
    97  		},
    98  		{
    99  			name:           "request is for pod",
   100  			attr:           admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, namespace1.Name, "mock-name", schema.GroupVersionResource{Resource: "pods"}, "", admission.Create, &metav1.CreateOptions{}, false, nil),
   101  			expectedLabels: namespace1Labels,
   102  		},
   103  	}
   104  	matcher := namespace.Matcher{
   105  		NamespaceLister: namespaceLister,
   106  	}
   107  	for _, tt := range tests {
   108  		actualLabels, err := matcher.GetNamespaceLabels(tt.attr)
   109  		if err != nil {
   110  			t.Error(err)
   111  		}
   112  		if !reflect.DeepEqual(actualLabels, tt.expectedLabels) {
   113  			t.Errorf("expected labels to be %#v, got %#v", tt.expectedLabels, actualLabels)
   114  		}
   115  	}
   116  }
   117  
   118  func TestNotExemptClusterScopedResource(t *testing.T) {
   119  	hook := &registrationv1.ValidatingWebhook{
   120  		NamespaceSelector: &metav1.LabelSelector{},
   121  	}
   122  	attr := admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "", "mock-name", schema.GroupVersionResource{Version: "v1", Resource: "nodes"}, "", admission.Create, &metav1.CreateOptions{}, false, nil)
   123  	matcher := namespace.Matcher{}
   124  	matches, err := matcher.MatchNamespaceSelector(webhook.NewValidatingWebhookAccessor("mock-hook", "mock-cfg", hook), attr)
   125  	if err != nil {
   126  		t.Fatal(err)
   127  	}
   128  	if !matches {
   129  		t.Errorf("cluster scoped resources (but not a namespace) should not be exempted from webhooks")
   130  	}
   131  }