...

Source file src/k8s.io/kubernetes/plugin/pkg/admission/podnodeselector/admission_test.go

Documentation: k8s.io/kubernetes/plugin/pkg/admission/podnodeselector

     1  /*
     2  Copyright 2016 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 podnodeselector
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  	"time"
    23  
    24  	corev1 "k8s.io/api/core/v1"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/labels"
    27  	"k8s.io/apiserver/pkg/admission"
    28  	genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer"
    29  	"k8s.io/client-go/informers"
    30  	"k8s.io/client-go/kubernetes"
    31  	"k8s.io/client-go/kubernetes/fake"
    32  	api "k8s.io/kubernetes/pkg/apis/core"
    33  )
    34  
    35  // TestPodAdmission verifies various scenarios involving pod/namespace/global node label selectors
    36  func TestPodAdmission(t *testing.T) {
    37  	namespace := &corev1.Namespace{
    38  		ObjectMeta: metav1.ObjectMeta{
    39  			Name:      "testNamespace",
    40  			Namespace: "",
    41  		},
    42  	}
    43  
    44  	mockClient := fake.NewSimpleClientset(namespace)
    45  	handler, informerFactory, err := newHandlerForTest(mockClient)
    46  	if err != nil {
    47  		t.Errorf("unexpected error initializing handler: %v", err)
    48  	}
    49  	stopCh := make(chan struct{})
    50  	defer close(stopCh)
    51  	informerFactory.Start(stopCh)
    52  
    53  	pod := &api.Pod{
    54  		ObjectMeta: metav1.ObjectMeta{Name: "testPod", Namespace: "testNamespace"},
    55  	}
    56  
    57  	tests := []struct {
    58  		defaultNodeSelector             string
    59  		namespaceNodeSelector           string
    60  		whitelist                       string
    61  		podNodeSelector                 map[string]string
    62  		mergedNodeSelector              labels.Set
    63  		ignoreTestNamespaceNodeSelector bool
    64  		admit                           bool
    65  		testName                        string
    66  	}{
    67  		{
    68  			defaultNodeSelector:             "",
    69  			podNodeSelector:                 map[string]string{},
    70  			mergedNodeSelector:              labels.Set{},
    71  			ignoreTestNamespaceNodeSelector: true,
    72  			admit:                           true,
    73  			testName:                        "No node selectors",
    74  		},
    75  		{
    76  			defaultNodeSelector:             "infra = false",
    77  			podNodeSelector:                 map[string]string{},
    78  			mergedNodeSelector:              labels.Set{"infra": "false"},
    79  			ignoreTestNamespaceNodeSelector: true,
    80  			admit:                           true,
    81  			testName:                        "Default node selector and no conflicts",
    82  		},
    83  		{
    84  			defaultNodeSelector:   "",
    85  			namespaceNodeSelector: " infra = false ",
    86  			podNodeSelector:       map[string]string{},
    87  			mergedNodeSelector:    labels.Set{"infra": "false"},
    88  			admit:                 true,
    89  			testName:              "TestNamespace node selector with whitespaces and no conflicts",
    90  		},
    91  		{
    92  			defaultNodeSelector:   "infra = false",
    93  			namespaceNodeSelector: "infra=true",
    94  			podNodeSelector:       map[string]string{},
    95  			mergedNodeSelector:    labels.Set{"infra": "true"},
    96  			admit:                 true,
    97  			testName:              "Default and namespace node selector, no conflicts",
    98  		},
    99  		{
   100  			defaultNodeSelector:   "infra = false",
   101  			namespaceNodeSelector: "",
   102  			podNodeSelector:       map[string]string{},
   103  			mergedNodeSelector:    labels.Set{},
   104  			admit:                 true,
   105  			testName:              "Empty namespace node selector and no conflicts",
   106  		},
   107  		{
   108  			defaultNodeSelector:   "infra = false",
   109  			namespaceNodeSelector: "infra=true",
   110  			podNodeSelector:       map[string]string{"env": "test"},
   111  			mergedNodeSelector:    labels.Set{"infra": "true", "env": "test"},
   112  			admit:                 true,
   113  			testName:              "TestNamespace and pod node selector, no conflicts",
   114  		},
   115  		{
   116  			defaultNodeSelector:   "env = test",
   117  			namespaceNodeSelector: "infra=true",
   118  			podNodeSelector:       map[string]string{"infra": "false"},
   119  			admit:                 false,
   120  			testName:              "Conflicting pod and namespace node selector, one label",
   121  		},
   122  		{
   123  			defaultNodeSelector:   "env=dev",
   124  			namespaceNodeSelector: "infra=false, env = test",
   125  			podNodeSelector:       map[string]string{"env": "dev", "color": "blue"},
   126  			admit:                 false,
   127  			testName:              "Conflicting pod and namespace node selector, multiple labels",
   128  		},
   129  		{
   130  			defaultNodeSelector:   "env=dev",
   131  			namespaceNodeSelector: "infra=false, env = dev",
   132  			whitelist:             "env=dev, infra=false, color=blue",
   133  			podNodeSelector:       map[string]string{"env": "dev", "color": "blue"},
   134  			mergedNodeSelector:    labels.Set{"infra": "false", "env": "dev", "color": "blue"},
   135  			admit:                 true,
   136  			testName:              "Merged pod node selectors satisfy the whitelist",
   137  		},
   138  		{
   139  			defaultNodeSelector:   "env=dev",
   140  			namespaceNodeSelector: "infra=false, env = dev",
   141  			whitelist:             "env=dev, infra=true, color=blue",
   142  			podNodeSelector:       map[string]string{"env": "dev", "color": "blue"},
   143  			admit:                 false,
   144  			testName:              "Merged pod node selectors conflict with the whitelist",
   145  		},
   146  		{
   147  			defaultNodeSelector:             "env=dev",
   148  			ignoreTestNamespaceNodeSelector: true,
   149  			whitelist:                       "env=prd",
   150  			podNodeSelector:                 map[string]string{},
   151  			admit:                           false,
   152  			testName:                        "Default node selector conflict with the whitelist",
   153  		},
   154  	}
   155  	for _, test := range tests {
   156  		if !test.ignoreTestNamespaceNodeSelector {
   157  			namespace.ObjectMeta.Annotations = map[string]string{"scheduler.alpha.kubernetes.io/node-selector": test.namespaceNodeSelector}
   158  			informerFactory.Core().V1().Namespaces().Informer().GetStore().Update(namespace)
   159  		}
   160  		handler.clusterNodeSelectors = make(map[string]string)
   161  		handler.clusterNodeSelectors["clusterDefaultNodeSelector"] = test.defaultNodeSelector
   162  		handler.clusterNodeSelectors[namespace.Name] = test.whitelist
   163  		pod.Spec = api.PodSpec{NodeSelector: test.podNodeSelector}
   164  
   165  		err := handler.Admit(context.TODO(), admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
   166  		if test.admit && err != nil {
   167  			t.Errorf("Test: %s, expected no error but got: %s", test.testName, err)
   168  		} else if !test.admit && err == nil {
   169  			t.Errorf("Test: %s, expected an error", test.testName)
   170  		}
   171  		if test.admit && !labels.Equals(test.mergedNodeSelector, labels.Set(pod.Spec.NodeSelector)) {
   172  			t.Errorf("Test: %s, expected: %s but got: %s", test.testName, test.mergedNodeSelector, pod.Spec.NodeSelector)
   173  		}
   174  		err = handler.Validate(context.TODO(), admission.NewAttributesRecord(pod, nil, api.Kind("Pod").WithVersion("version"), "testNamespace", namespace.ObjectMeta.Name, api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil), nil)
   175  		if test.admit && err != nil {
   176  			t.Errorf("Test: %s, expected no error but got: %s", test.testName, err)
   177  		} else if !test.admit && err == nil {
   178  			t.Errorf("Test: %s, expected an error", test.testName)
   179  		}
   180  	}
   181  }
   182  
   183  func TestHandles(t *testing.T) {
   184  	for op, shouldHandle := range map[admission.Operation]bool{
   185  		admission.Create:  true,
   186  		admission.Update:  false,
   187  		admission.Connect: false,
   188  		admission.Delete:  false,
   189  	} {
   190  		nodeEnvionment := NewPodNodeSelector(nil)
   191  		if e, a := shouldHandle, nodeEnvionment.Handles(op); e != a {
   192  			t.Errorf("%v: shouldHandle=%t, handles=%t", op, e, a)
   193  		}
   194  	}
   195  }
   196  
   197  // newHandlerForTest returns the admission controller configured for testing.
   198  func newHandlerForTest(c kubernetes.Interface) (*Plugin, informers.SharedInformerFactory, error) {
   199  	f := informers.NewSharedInformerFactory(c, 5*time.Minute)
   200  	handler := NewPodNodeSelector(nil)
   201  	pluginInitializer := genericadmissioninitializer.New(c, nil, f, nil, nil, nil, nil)
   202  	pluginInitializer.Initialize(handler)
   203  	err := admission.ValidateInitialization(handler)
   204  	return handler, f, err
   205  }
   206  

View as plain text