...

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

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

     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 antiaffinity
    18  
    19  import (
    20  	"context"
    21  	"testing"
    22  
    23  	v1 "k8s.io/api/core/v1"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/apimachinery/pkg/runtime"
    26  	"k8s.io/apiserver/pkg/admission"
    27  	api "k8s.io/kubernetes/pkg/apis/core"
    28  )
    29  
    30  // ensures the hard PodAntiAffinity is denied if it defines TopologyKey other than kubernetes.io/hostname.
    31  // TODO: Add test case "invalid topologyKey in requiredDuringSchedulingRequiredDuringExecution then admission fails"
    32  // after RequiredDuringSchedulingRequiredDuringExecution is implemented.
    33  func TestInterPodAffinityAdmission(t *testing.T) {
    34  	handler := NewInterPodAntiAffinity()
    35  	pod := api.Pod{
    36  		Spec: api.PodSpec{},
    37  	}
    38  	tests := []struct {
    39  		affinity      *api.Affinity
    40  		errorExpected bool
    41  	}{
    42  		// empty affinity its success.
    43  		{
    44  			affinity:      &api.Affinity{},
    45  			errorExpected: false,
    46  		},
    47  		// what ever topologyKey in preferredDuringSchedulingIgnoredDuringExecution, the admission should success.
    48  		{
    49  			affinity: &api.Affinity{
    50  				PodAntiAffinity: &api.PodAntiAffinity{
    51  					PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
    52  						{
    53  							Weight: 5,
    54  							PodAffinityTerm: api.PodAffinityTerm{
    55  								LabelSelector: &metav1.LabelSelector{
    56  									MatchExpressions: []metav1.LabelSelectorRequirement{
    57  										{
    58  											Key:      "security",
    59  											Operator: metav1.LabelSelectorOpIn,
    60  											Values:   []string{"S2"},
    61  										},
    62  									},
    63  								},
    64  								TopologyKey: "az",
    65  							},
    66  						},
    67  					},
    68  				},
    69  			},
    70  			errorExpected: false,
    71  		},
    72  		// valid topologyKey in requiredDuringSchedulingIgnoredDuringExecution,
    73  		// plus any topologyKey in preferredDuringSchedulingIgnoredDuringExecution, then admission success.
    74  		{
    75  			affinity: &api.Affinity{
    76  				PodAntiAffinity: &api.PodAntiAffinity{
    77  					PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
    78  						{
    79  							Weight: 5,
    80  							PodAffinityTerm: api.PodAffinityTerm{
    81  								LabelSelector: &metav1.LabelSelector{
    82  									MatchExpressions: []metav1.LabelSelectorRequirement{
    83  										{
    84  											Key:      "security",
    85  											Operator: metav1.LabelSelectorOpIn,
    86  											Values:   []string{"S2"},
    87  										},
    88  									},
    89  								},
    90  								TopologyKey: "az",
    91  							},
    92  						},
    93  					},
    94  					RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
    95  						{
    96  							LabelSelector: &metav1.LabelSelector{
    97  								MatchExpressions: []metav1.LabelSelectorRequirement{
    98  									{
    99  										Key:      "security",
   100  										Operator: metav1.LabelSelectorOpIn,
   101  										Values:   []string{"S2"},
   102  									},
   103  								},
   104  							},
   105  							TopologyKey: v1.LabelHostname,
   106  						},
   107  					},
   108  				},
   109  			},
   110  			errorExpected: false,
   111  		},
   112  		// valid topologyKey in requiredDuringSchedulingIgnoredDuringExecution then admission success.
   113  		{
   114  			affinity: &api.Affinity{
   115  				PodAntiAffinity: &api.PodAntiAffinity{
   116  					RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
   117  						{
   118  							LabelSelector: &metav1.LabelSelector{
   119  								MatchExpressions: []metav1.LabelSelectorRequirement{
   120  									{
   121  										Key:      "security",
   122  										Operator: metav1.LabelSelectorOpIn,
   123  										Values:   []string{"S2"},
   124  									},
   125  								},
   126  							},
   127  							TopologyKey: v1.LabelHostname,
   128  						},
   129  					},
   130  				},
   131  			},
   132  			errorExpected: false,
   133  		},
   134  		// invalid topologyKey in requiredDuringSchedulingIgnoredDuringExecution then admission fails.
   135  		{
   136  			affinity: &api.Affinity{
   137  				PodAntiAffinity: &api.PodAntiAffinity{
   138  					RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
   139  						{
   140  							LabelSelector: &metav1.LabelSelector{
   141  								MatchExpressions: []metav1.LabelSelectorRequirement{
   142  									{
   143  										Key:      "security",
   144  										Operator: metav1.LabelSelectorOpIn,
   145  										Values:   []string{"S2"},
   146  									},
   147  								},
   148  							},
   149  							TopologyKey: " zone ",
   150  						},
   151  					},
   152  				},
   153  			},
   154  			errorExpected: true,
   155  		},
   156  		// list of requiredDuringSchedulingIgnoredDuringExecution middle element topologyKey is not valid.
   157  		{
   158  			affinity: &api.Affinity{
   159  				PodAntiAffinity: &api.PodAntiAffinity{
   160  					RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
   161  						{
   162  							LabelSelector: &metav1.LabelSelector{
   163  								MatchExpressions: []metav1.LabelSelectorRequirement{
   164  									{
   165  										Key:      "security",
   166  										Operator: metav1.LabelSelectorOpIn,
   167  										Values:   []string{"S2"},
   168  									},
   169  								},
   170  							},
   171  							TopologyKey: v1.LabelHostname,
   172  						}, {
   173  							LabelSelector: &metav1.LabelSelector{
   174  								MatchExpressions: []metav1.LabelSelectorRequirement{
   175  									{
   176  										Key:      "security",
   177  										Operator: metav1.LabelSelectorOpIn,
   178  										Values:   []string{"S2"},
   179  									},
   180  								},
   181  							},
   182  							TopologyKey: " zone ",
   183  						}, {
   184  							LabelSelector: &metav1.LabelSelector{
   185  								MatchExpressions: []metav1.LabelSelectorRequirement{
   186  									{
   187  										Key:      "security",
   188  										Operator: metav1.LabelSelectorOpIn,
   189  										Values:   []string{"S2"},
   190  									},
   191  								},
   192  							},
   193  							TopologyKey: v1.LabelHostname,
   194  						},
   195  					},
   196  				},
   197  			},
   198  			errorExpected: true,
   199  		},
   200  	}
   201  	for _, test := range tests {
   202  		pod.Spec.Affinity = test.affinity
   203  		err := handler.Validate(context.TODO(), admission.NewAttributesRecord(&pod, nil, api.Kind("Pod").WithVersion("version"), "foo", "name", api.Resource("pods").WithVersion("version"), "", "ignored", nil, false, nil), nil)
   204  
   205  		if test.errorExpected && err == nil {
   206  			t.Errorf("Expected error for Anti Affinity %+v but did not get an error", test.affinity)
   207  		}
   208  
   209  		if !test.errorExpected && err != nil {
   210  			t.Errorf("Unexpected error %v for AntiAffinity %+v", err, test.affinity)
   211  		}
   212  	}
   213  }
   214  func TestHandles(t *testing.T) {
   215  	handler := NewInterPodAntiAffinity()
   216  	tests := map[admission.Operation]bool{
   217  		admission.Update:  true,
   218  		admission.Create:  true,
   219  		admission.Delete:  false,
   220  		admission.Connect: false,
   221  	}
   222  	for op, expected := range tests {
   223  		result := handler.Handles(op)
   224  		if result != expected {
   225  			t.Errorf("Unexpected result for operation %s: %v\n", op, result)
   226  		}
   227  	}
   228  }
   229  
   230  // TestOtherResources ensures that this admission controller is a no-op for other resources,
   231  // subresources, and non-pods.
   232  func TestOtherResources(t *testing.T) {
   233  	namespace := "testnamespace"
   234  	name := "testname"
   235  	pod := &api.Pod{
   236  		ObjectMeta: metav1.ObjectMeta{Name: name, Namespace: namespace},
   237  	}
   238  	tests := []struct {
   239  		name        string
   240  		kind        string
   241  		resource    string
   242  		subresource string
   243  		object      runtime.Object
   244  		expectError bool
   245  	}{
   246  		{
   247  			name:     "non-pod resource",
   248  			kind:     "Foo",
   249  			resource: "foos",
   250  			object:   pod,
   251  		},
   252  		{
   253  			name:        "pod subresource",
   254  			kind:        "Pod",
   255  			resource:    "pods",
   256  			subresource: "eviction",
   257  			object:      pod,
   258  		},
   259  		{
   260  			name:        "non-pod object",
   261  			kind:        "Pod",
   262  			resource:    "pods",
   263  			object:      &api.Service{},
   264  			expectError: true,
   265  		},
   266  	}
   267  
   268  	for _, tc := range tests {
   269  		handler := &Plugin{}
   270  
   271  		err := handler.Validate(context.TODO(), admission.NewAttributesRecord(tc.object, nil, api.Kind(tc.kind).WithVersion("version"), namespace, name, api.Resource(tc.resource).WithVersion("version"), tc.subresource, admission.Create, &metav1.CreateOptions{}, false, nil), nil)
   272  
   273  		if tc.expectError {
   274  			if err == nil {
   275  				t.Errorf("%s: unexpected nil error", tc.name)
   276  			}
   277  			continue
   278  		}
   279  
   280  		if err != nil {
   281  			t.Errorf("%s: unexpected error: %v", tc.name, err)
   282  			continue
   283  		}
   284  	}
   285  }
   286  

View as plain text