...

Source file src/k8s.io/kubernetes/pkg/scheduler/framework/plugins/interpodaffinity/scoring_test.go

Documentation: k8s.io/kubernetes/pkg/scheduler/framework/plugins/interpodaffinity

     1  /*
     2  Copyright 2019 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 interpodaffinity
    18  
    19  import (
    20  	"context"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/google/go-cmp/cmp"
    26  	v1 "k8s.io/api/core/v1"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/runtime"
    29  	"k8s.io/klog/v2/ktesting"
    30  	"k8s.io/kubernetes/pkg/scheduler/apis/config"
    31  	"k8s.io/kubernetes/pkg/scheduler/framework"
    32  	plugintesting "k8s.io/kubernetes/pkg/scheduler/framework/plugins/testing"
    33  	"k8s.io/kubernetes/pkg/scheduler/internal/cache"
    34  	tf "k8s.io/kubernetes/pkg/scheduler/testing/framework"
    35  )
    36  
    37  var nsLabelT1 = map[string]string{"team": "team1"}
    38  var nsLabelT2 = map[string]string{"team": "team2"}
    39  var namespaces = []runtime.Object{
    40  	&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "subteam1.team1", Labels: nsLabelT1}},
    41  	&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "subteam2.team1", Labels: nsLabelT1}},
    42  	&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "subteam1.team2", Labels: nsLabelT2}},
    43  	&v1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: "subteam2.team2", Labels: nsLabelT2}},
    44  }
    45  
    46  func TestPreferredAffinity(t *testing.T) {
    47  	labelRgChina := map[string]string{
    48  		"region": "China",
    49  	}
    50  	labelRgIndia := map[string]string{
    51  		"region": "India",
    52  	}
    53  	labelAzAz1 := map[string]string{
    54  		"az": "az1",
    55  	}
    56  	labelAzAz2 := map[string]string{
    57  		"az": "az2",
    58  	}
    59  	labelRgChinaAzAz1 := map[string]string{
    60  		"region": "China",
    61  		"az":     "az1",
    62  	}
    63  	podLabelSecurityS1 := map[string]string{
    64  		"security": "S1",
    65  	}
    66  	podLabelSecurityS2 := map[string]string{
    67  		"security": "S2",
    68  	}
    69  	// considered only preferredDuringSchedulingIgnoredDuringExecution in pod affinity
    70  	stayWithS1InRegion := &v1.Affinity{
    71  		PodAffinity: &v1.PodAffinity{
    72  			PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
    73  				{
    74  					Weight: 5,
    75  					PodAffinityTerm: v1.PodAffinityTerm{
    76  						LabelSelector: &metav1.LabelSelector{
    77  							MatchExpressions: []metav1.LabelSelectorRequirement{
    78  								{
    79  									Key:      "security",
    80  									Operator: metav1.LabelSelectorOpIn,
    81  									Values:   []string{"S1"},
    82  								},
    83  							},
    84  						},
    85  						TopologyKey: "region",
    86  					},
    87  				},
    88  			},
    89  		},
    90  	}
    91  	stayWithS2InRegion := &v1.Affinity{
    92  		PodAffinity: &v1.PodAffinity{
    93  			PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
    94  				{
    95  					Weight: 6,
    96  					PodAffinityTerm: v1.PodAffinityTerm{
    97  						LabelSelector: &metav1.LabelSelector{
    98  							MatchExpressions: []metav1.LabelSelectorRequirement{
    99  								{
   100  									Key:      "security",
   101  									Operator: metav1.LabelSelectorOpIn,
   102  									Values:   []string{"S2"},
   103  								},
   104  							},
   105  						},
   106  						TopologyKey: "region",
   107  					},
   108  				},
   109  			},
   110  		},
   111  	}
   112  	affinity3 := &v1.Affinity{
   113  		PodAffinity: &v1.PodAffinity{
   114  			PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
   115  				{
   116  					Weight: 8,
   117  					PodAffinityTerm: v1.PodAffinityTerm{
   118  						LabelSelector: &metav1.LabelSelector{
   119  							MatchExpressions: []metav1.LabelSelectorRequirement{
   120  								{
   121  									Key:      "security",
   122  									Operator: metav1.LabelSelectorOpNotIn,
   123  									Values:   []string{"S1"},
   124  								}, {
   125  									Key:      "security",
   126  									Operator: metav1.LabelSelectorOpIn,
   127  									Values:   []string{"S2"},
   128  								},
   129  							},
   130  						},
   131  						TopologyKey: "region",
   132  					},
   133  				}, {
   134  					Weight: 2,
   135  					PodAffinityTerm: v1.PodAffinityTerm{
   136  						LabelSelector: &metav1.LabelSelector{
   137  							MatchExpressions: []metav1.LabelSelectorRequirement{
   138  								{
   139  									Key:      "security",
   140  									Operator: metav1.LabelSelectorOpExists,
   141  								}, {
   142  									Key:      "wrongkey",
   143  									Operator: metav1.LabelSelectorOpDoesNotExist,
   144  								},
   145  							},
   146  						},
   147  						TopologyKey: "region",
   148  					},
   149  				},
   150  			},
   151  		},
   152  	}
   153  	hardAffinity := &v1.Affinity{
   154  		PodAffinity: &v1.PodAffinity{
   155  			RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   156  				{
   157  					LabelSelector: &metav1.LabelSelector{
   158  						MatchExpressions: []metav1.LabelSelectorRequirement{
   159  							{
   160  								Key:      "security",
   161  								Operator: metav1.LabelSelectorOpIn,
   162  								Values:   []string{"S1", "value2"},
   163  							},
   164  						},
   165  					},
   166  					TopologyKey: "region",
   167  				}, {
   168  					LabelSelector: &metav1.LabelSelector{
   169  						MatchExpressions: []metav1.LabelSelectorRequirement{
   170  							{
   171  								Key:      "security",
   172  								Operator: metav1.LabelSelectorOpExists,
   173  							}, {
   174  								Key:      "wrongkey",
   175  								Operator: metav1.LabelSelectorOpDoesNotExist,
   176  							},
   177  						},
   178  					},
   179  					TopologyKey: "region",
   180  				},
   181  			},
   182  		},
   183  	}
   184  	awayFromS1InAz := &v1.Affinity{
   185  		PodAntiAffinity: &v1.PodAntiAffinity{
   186  			PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
   187  				{
   188  					Weight: 5,
   189  					PodAffinityTerm: v1.PodAffinityTerm{
   190  						LabelSelector: &metav1.LabelSelector{
   191  							MatchExpressions: []metav1.LabelSelectorRequirement{
   192  								{
   193  									Key:      "security",
   194  									Operator: metav1.LabelSelectorOpIn,
   195  									Values:   []string{"S1"},
   196  								},
   197  							},
   198  						},
   199  						TopologyKey: "az",
   200  					},
   201  				},
   202  			},
   203  		},
   204  	}
   205  	// to stay away from security S2 in any az.
   206  	awayFromS2InAz := &v1.Affinity{
   207  		PodAntiAffinity: &v1.PodAntiAffinity{
   208  			PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
   209  				{
   210  					Weight: 5,
   211  					PodAffinityTerm: v1.PodAffinityTerm{
   212  						LabelSelector: &metav1.LabelSelector{
   213  							MatchExpressions: []metav1.LabelSelectorRequirement{
   214  								{
   215  									Key:      "security",
   216  									Operator: metav1.LabelSelectorOpIn,
   217  									Values:   []string{"S2"},
   218  								},
   219  							},
   220  						},
   221  						TopologyKey: "az",
   222  					},
   223  				},
   224  			},
   225  		},
   226  	}
   227  	// to stay with security S1 in same region, stay away from security S2 in any az.
   228  	stayWithS1InRegionAwayFromS2InAz := &v1.Affinity{
   229  		PodAffinity: &v1.PodAffinity{
   230  			PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
   231  				{
   232  					Weight: 8,
   233  					PodAffinityTerm: v1.PodAffinityTerm{
   234  						LabelSelector: &metav1.LabelSelector{
   235  							MatchExpressions: []metav1.LabelSelectorRequirement{
   236  								{
   237  									Key:      "security",
   238  									Operator: metav1.LabelSelectorOpIn,
   239  									Values:   []string{"S1"},
   240  								},
   241  							},
   242  						},
   243  						TopologyKey: "region",
   244  					},
   245  				},
   246  			},
   247  		},
   248  		PodAntiAffinity: &v1.PodAntiAffinity{
   249  			PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
   250  				{
   251  					Weight: 5,
   252  					PodAffinityTerm: v1.PodAffinityTerm{
   253  						LabelSelector: &metav1.LabelSelector{
   254  							MatchExpressions: []metav1.LabelSelectorRequirement{
   255  								{
   256  									Key:      "security",
   257  									Operator: metav1.LabelSelectorOpIn,
   258  									Values:   []string{"S2"},
   259  								},
   260  							},
   261  						},
   262  						TopologyKey: "az",
   263  					},
   264  				},
   265  			},
   266  		},
   267  	}
   268  
   269  	affinityNamespaceSelector := &v1.Affinity{
   270  		PodAffinity: &v1.PodAffinity{
   271  			PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
   272  				{
   273  					Weight: 5,
   274  					PodAffinityTerm: v1.PodAffinityTerm{
   275  						LabelSelector: &metav1.LabelSelector{
   276  							MatchExpressions: []metav1.LabelSelectorRequirement{
   277  								{
   278  									Key:      "security",
   279  									Operator: metav1.LabelSelectorOpIn,
   280  									Values:   []string{"S1"},
   281  								},
   282  							},
   283  						},
   284  						TopologyKey: "region",
   285  						Namespaces:  []string{"subteam2.team2"},
   286  						NamespaceSelector: &metav1.LabelSelector{
   287  							MatchExpressions: []metav1.LabelSelectorRequirement{
   288  								{
   289  									Key:      "team",
   290  									Operator: metav1.LabelSelectorOpIn,
   291  									Values:   []string{"team1"},
   292  								},
   293  							},
   294  						},
   295  					},
   296  				},
   297  			},
   298  		},
   299  	}
   300  	antiAffinityNamespaceSelector := &v1.Affinity{
   301  		PodAntiAffinity: &v1.PodAntiAffinity{
   302  			PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
   303  				{
   304  					Weight: 5,
   305  					PodAffinityTerm: v1.PodAffinityTerm{
   306  						LabelSelector: &metav1.LabelSelector{
   307  							MatchExpressions: []metav1.LabelSelectorRequirement{
   308  								{
   309  									Key:      "security",
   310  									Operator: metav1.LabelSelectorOpIn,
   311  									Values:   []string{"S1"},
   312  								},
   313  							},
   314  						},
   315  						TopologyKey: "region",
   316  						Namespaces:  []string{"subteam2.team2"},
   317  						NamespaceSelector: &metav1.LabelSelector{
   318  							MatchExpressions: []metav1.LabelSelectorRequirement{
   319  								{
   320  									Key:      "team",
   321  									Operator: metav1.LabelSelectorOpIn,
   322  									Values:   []string{"team1"},
   323  								},
   324  							},
   325  						},
   326  					},
   327  				},
   328  			},
   329  		},
   330  	}
   331  	invalidAffinityLabels := &v1.Affinity{
   332  		PodAffinity: &v1.PodAffinity{
   333  			PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
   334  				{
   335  					Weight: 8,
   336  					PodAffinityTerm: v1.PodAffinityTerm{
   337  						LabelSelector: &metav1.LabelSelector{
   338  							MatchExpressions: []metav1.LabelSelectorRequirement{
   339  								{
   340  									Key:      "security",
   341  									Operator: metav1.LabelSelectorOpIn,
   342  									Values:   []string{"{{.bad-value.}}"},
   343  								},
   344  							},
   345  						},
   346  						TopologyKey: "region",
   347  					},
   348  				},
   349  			},
   350  		},
   351  	}
   352  	invalidAntiAffinityLabels := &v1.Affinity{
   353  		PodAntiAffinity: &v1.PodAntiAffinity{
   354  			PreferredDuringSchedulingIgnoredDuringExecution: []v1.WeightedPodAffinityTerm{
   355  				{
   356  					Weight: 5,
   357  					PodAffinityTerm: v1.PodAffinityTerm{
   358  						LabelSelector: &metav1.LabelSelector{
   359  							MatchExpressions: []metav1.LabelSelectorRequirement{
   360  								{
   361  									Key:      "security",
   362  									Operator: metav1.LabelSelectorOpIn,
   363  									Values:   []string{"{{.bad-value.}}"},
   364  								},
   365  							},
   366  						},
   367  						TopologyKey: "az",
   368  					},
   369  				},
   370  			},
   371  		},
   372  	}
   373  
   374  	tests := []struct {
   375  		pod                                *v1.Pod
   376  		pods                               []*v1.Pod
   377  		nodes                              []*v1.Node
   378  		expectedList                       framework.NodeScoreList
   379  		name                               string
   380  		ignorePreferredTermsOfExistingPods bool
   381  		wantStatus                         *framework.Status
   382  	}{
   383  		{
   384  			name: "all nodes are same priority as Affinity is nil",
   385  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   386  			nodes: []*v1.Node{
   387  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   388  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   389  				{ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
   390  			},
   391  			wantStatus: framework.NewStatus(framework.Skip),
   392  		},
   393  		// the node(node1) that have the label {"region": "China"} (match the topology key) and that have existing pods that match the labelSelector get high score
   394  		// the node(node3) that don't have the label {"region": "whatever the value is"} (mismatch the topology key) but that have existing pods that match the labelSelector get low score
   395  		// the node(node2) that have the label {"region": "China"} (match the topology key) but that have existing pods that mismatch the labelSelector get low score
   396  		{
   397  			name: "Affinity: pod that matches topology key & pods in nodes will get high score comparing to others" +
   398  				"which doesn't match either pods in nodes or in topology key",
   399  			pod: &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS1InRegion}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   400  			pods: []*v1.Pod{
   401  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   402  				{Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   403  				{Spec: v1.PodSpec{NodeName: "node3"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   404  			},
   405  			nodes: []*v1.Node{
   406  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   407  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   408  				{ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
   409  			},
   410  			expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: 0}, {Name: "node3", Score: 0}},
   411  		},
   412  		// the node1(node1) that have the label {"region": "China"} (match the topology key) and that have existing pods that match the labelSelector get high score
   413  		// the node2(node2) that have the label {"region": "China"}, match the topology key and have the same label value with node1, get the same high score with node1
   414  		// the node3(node3) that have the label {"region": "India"}, match the topology key but have a different label value, don't have existing pods that match the labelSelector,
   415  		// get a low score.
   416  		{
   417  			name: "All the nodes that have the same topology key & label value with one of them has an existing pod that match the affinity rules, have the same score",
   418  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS1InRegion}},
   419  			pods: []*v1.Pod{
   420  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   421  			},
   422  			nodes: []*v1.Node{
   423  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   424  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgChinaAzAz1}},
   425  				{ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelRgIndia}},
   426  			},
   427  			expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: framework.MaxNodeScore}, {Name: "node3", Score: 0}},
   428  		},
   429  		// there are 2 regions, say regionChina(node1,node3,node4) and regionIndia(node2,node5), both regions have nodes that match the preference.
   430  		// But there are more nodes(actually more existing pods) in regionChina that match the preference than regionIndia.
   431  		// Then, nodes in regionChina get higher score than nodes in regionIndia, and all the nodes in regionChina should get a same score(high score),
   432  		// while all the nodes in regionIndia should get another same score(low score).
   433  		{
   434  			name: "Affinity: nodes in one region has more matching pods comparing to other region, so the region which has more matches will get high score",
   435  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS2InRegion}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   436  			pods: []*v1.Pod{
   437  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   438  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   439  				{Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   440  				{Spec: v1.PodSpec{NodeName: "node3"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   441  				{Spec: v1.PodSpec{NodeName: "node4"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   442  				{Spec: v1.PodSpec{NodeName: "node5"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   443  			},
   444  			nodes: []*v1.Node{
   445  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   446  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   447  				{ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelRgChina}},
   448  				{ObjectMeta: metav1.ObjectMeta{Name: "node4", Labels: labelRgChina}},
   449  				{ObjectMeta: metav1.ObjectMeta{Name: "node5", Labels: labelRgIndia}},
   450  			},
   451  			expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: 0}, {Name: "node3", Score: framework.MaxNodeScore}, {Name: "node4", Score: framework.MaxNodeScore}, {Name: "node5", Score: 0}},
   452  		},
   453  		// Test with the different operators and values for pod affinity scheduling preference, including some match failures.
   454  		{
   455  			name: "Affinity: different Label operators and values for pod affinity scheduling preference, including some match failures ",
   456  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: affinity3}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   457  			pods: []*v1.Pod{
   458  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   459  				{Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   460  				{Spec: v1.PodSpec{NodeName: "node3"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   461  			},
   462  			nodes: []*v1.Node{
   463  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   464  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   465  				{ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
   466  			},
   467  			expectedList: []framework.NodeScore{{Name: "node1", Score: 20}, {Name: "node2", Score: framework.MaxNodeScore}, {Name: "node3", Score: 0}},
   468  		},
   469  		// Test the symmetry cases for affinity, the difference between affinity and symmetry is not the pod wants to run together with some existing pods,
   470  		// but the existing pods have the inter pod affinity preference while the pod to schedule satisfy the preference.
   471  		{
   472  			name: "Affinity symmetry: considered only the preferredDuringSchedulingIgnoredDuringExecution in pod affinity symmetry",
   473  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   474  			pods: []*v1.Pod{
   475  				{Spec: v1.PodSpec{NodeName: "node1", Affinity: stayWithS1InRegion}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   476  				{Spec: v1.PodSpec{NodeName: "node2", Affinity: stayWithS2InRegion}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   477  			},
   478  			nodes: []*v1.Node{
   479  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   480  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   481  				{ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
   482  			},
   483  			expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}, {Name: "node3", Score: 0}},
   484  		},
   485  		{
   486  			name: "Affinity symmetry with namespace selector",
   487  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
   488  			pods: []*v1.Pod{
   489  				{Spec: v1.PodSpec{NodeName: "node1", Affinity: affinityNamespaceSelector}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   490  				{Spec: v1.PodSpec{NodeName: "node2", Affinity: stayWithS2InRegion}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   491  			},
   492  			nodes: []*v1.Node{
   493  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   494  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   495  				{ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
   496  			},
   497  			expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: 0}, {Name: "node3", Score: 0}},
   498  		},
   499  		{
   500  			name: "AntiAffinity symmetry with namespace selector",
   501  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
   502  			pods: []*v1.Pod{
   503  				{Spec: v1.PodSpec{NodeName: "node1", Affinity: antiAffinityNamespaceSelector}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   504  				{Spec: v1.PodSpec{NodeName: "node2", Affinity: stayWithS2InRegion}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   505  			},
   506  			nodes: []*v1.Node{
   507  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   508  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   509  				{ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
   510  			},
   511  			expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}, {Name: "node3", Score: framework.MaxNodeScore}},
   512  		},
   513  		{
   514  			name: "Affinity symmetry: considered RequiredDuringSchedulingIgnoredDuringExecution in pod affinity symmetry",
   515  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   516  			pods: []*v1.Pod{
   517  				{Spec: v1.PodSpec{NodeName: "node1", Affinity: hardAffinity}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   518  				{Spec: v1.PodSpec{NodeName: "node2", Affinity: hardAffinity}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   519  			},
   520  			nodes: []*v1.Node{
   521  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   522  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   523  				{ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
   524  			},
   525  			expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: framework.MaxNodeScore}, {Name: "node3", Score: 0}},
   526  		},
   527  
   528  		// The pod to schedule prefer to stay away from some existing pods at node level using the pod anti affinity.
   529  		// the nodes that have the label {"node": "bar"} (match the topology key) and that have existing pods that match the labelSelector get low score
   530  		// the nodes that don't have the label {"node": "whatever the value is"} (mismatch the topology key) but that have existing pods that match the labelSelector get high score
   531  		// the nodes that have the label {"node": "bar"} (match the topology key) but that have existing pods that mismatch the labelSelector get high score
   532  		// there are 2 nodes, say node1 and node2, both nodes have pods that match the labelSelector and have topology-key in node.Labels.
   533  		// But there are more pods on node1 that match the preference than node2. Then, node1 get a lower score than node2.
   534  		{
   535  			name: "Anti Affinity: pod that does not match existing pods in node will get high score ",
   536  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: awayFromS1InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   537  			pods: []*v1.Pod{
   538  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   539  				{Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   540  			},
   541  			nodes: []*v1.Node{
   542  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelAzAz1}},
   543  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgChina}},
   544  			},
   545  			expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}},
   546  		},
   547  		{
   548  			name: "Anti Affinity: pod that does not match topology key & match the pods in nodes will get higher score comparing to others ",
   549  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: awayFromS1InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   550  			pods: []*v1.Pod{
   551  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   552  				{Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   553  			},
   554  			nodes: []*v1.Node{
   555  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelAzAz1}},
   556  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgChina}},
   557  			},
   558  			expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}},
   559  		},
   560  		{
   561  			name: "Anti Affinity: one node has more matching pods comparing to other node, so the node which has more unmatches will get high score",
   562  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: awayFromS1InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   563  			pods: []*v1.Pod{
   564  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   565  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   566  				{Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   567  			},
   568  			nodes: []*v1.Node{
   569  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelAzAz1}},
   570  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   571  			},
   572  			expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}},
   573  		},
   574  		// Test the symmetry cases for anti affinity
   575  		{
   576  			name: "Anti Affinity symmetry: the existing pods in node which has anti affinity match will get high score",
   577  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   578  			pods: []*v1.Pod{
   579  				{Spec: v1.PodSpec{NodeName: "node1", Affinity: awayFromS2InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   580  				{Spec: v1.PodSpec{NodeName: "node2", Affinity: awayFromS1InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   581  			},
   582  			nodes: []*v1.Node{
   583  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelAzAz1}},
   584  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelAzAz2}},
   585  			},
   586  			expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}},
   587  		},
   588  		// Test both  affinity and anti-affinity
   589  		{
   590  			name: "Affinity and Anti Affinity: considered only preferredDuringSchedulingIgnoredDuringExecution in both pod affinity & anti affinity",
   591  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS1InRegionAwayFromS2InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   592  			pods: []*v1.Pod{
   593  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   594  				{Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   595  			},
   596  			nodes: []*v1.Node{
   597  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   598  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelAzAz1}},
   599  			},
   600  			expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: 0}},
   601  		},
   602  		// Combined cases considering both affinity and anti-affinity, the pod to schedule and existing pods have the same labels (they are in the same RC/service),
   603  		// the pod prefer to run together with its brother pods in the same region, but wants to stay away from them at node level,
   604  		// so that all the pods of a RC/service can stay in a same region but trying to separate with each other
   605  		// node-1,node-3,node-4 are in ChinaRegion others node-2,node-5 are in IndiaRegion
   606  		{
   607  			name: "Affinity and Anti Affinity: considering both affinity and anti-affinity, the pod to schedule and existing pods have the same labels",
   608  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS1InRegionAwayFromS2InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   609  			pods: []*v1.Pod{
   610  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   611  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   612  				{Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   613  				{Spec: v1.PodSpec{NodeName: "node3"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   614  				{Spec: v1.PodSpec{NodeName: "node3"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   615  				{Spec: v1.PodSpec{NodeName: "node4"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   616  				{Spec: v1.PodSpec{NodeName: "node5"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   617  			},
   618  			nodes: []*v1.Node{
   619  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChinaAzAz1}},
   620  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   621  				{ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelRgChina}},
   622  				{ObjectMeta: metav1.ObjectMeta{Name: "node4", Labels: labelRgChina}},
   623  				{ObjectMeta: metav1.ObjectMeta{Name: "node5", Labels: labelRgIndia}},
   624  			},
   625  			expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: 0}, {Name: "node3", Score: framework.MaxNodeScore}, {Name: "node4", Score: framework.MaxNodeScore}, {Name: "node5", Score: 0}},
   626  		},
   627  		// Consider Affinity, Anti Affinity and symmetry together.
   628  		// for Affinity, the weights are:                8,  0,  0,  0
   629  		// for Anti Affinity, the weights are:           0, -5,  0,  0
   630  		// for Affinity symmetry, the weights are:       0,  0,  8,  0
   631  		// for Anti Affinity symmetry, the weights are:  0,  0,  0, -5
   632  		{
   633  			name: "Affinity and Anti Affinity and symmetry: considered only preferredDuringSchedulingIgnoredDuringExecution in both pod affinity & anti affinity & symmetry",
   634  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: stayWithS1InRegionAwayFromS2InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   635  			pods: []*v1.Pod{
   636  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   637  				{Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   638  				{Spec: v1.PodSpec{NodeName: "node3", Affinity: stayWithS1InRegionAwayFromS2InAz}},
   639  				{Spec: v1.PodSpec{NodeName: "node4", Affinity: awayFromS1InAz}},
   640  			},
   641  			nodes: []*v1.Node{
   642  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   643  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelAzAz1}},
   644  				{ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelRgIndia}},
   645  				{ObjectMeta: metav1.ObjectMeta{Name: "node4", Labels: labelAzAz2}},
   646  			},
   647  			expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: 0}, {Name: "node3", Score: framework.MaxNodeScore}, {Name: "node4", Score: 0}},
   648  		},
   649  		// Cover https://github.com/kubernetes/kubernetes/issues/82796 which panics upon:
   650  		// 1. Some nodes in a topology don't have pods with affinity, but other nodes in the same topology have.
   651  		// 2. The incoming pod doesn't have affinity.
   652  		{
   653  			name: "Avoid panic when partial nodes in a topology don't have pods with affinity",
   654  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   655  			pods: []*v1.Pod{
   656  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   657  				{Spec: v1.PodSpec{NodeName: "node2", Affinity: stayWithS1InRegionAwayFromS2InAz}},
   658  			},
   659  			nodes: []*v1.Node{
   660  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   661  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgChina}},
   662  			},
   663  			expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: 0}},
   664  		},
   665  		{
   666  			name:       "invalid Affinity fails PreScore",
   667  			pod:        &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: invalidAffinityLabels}},
   668  			wantStatus: framework.NewStatus(framework.Error, `Invalid value: "{{.bad-value.}}"`),
   669  			nodes: []*v1.Node{
   670  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   671  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgChina}},
   672  			},
   673  		},
   674  		{
   675  			name:       "invalid AntiAffinity fails PreScore",
   676  			pod:        &v1.Pod{Spec: v1.PodSpec{NodeName: "", Affinity: invalidAntiAffinityLabels}},
   677  			wantStatus: framework.NewStatus(framework.Error, `Invalid value: "{{.bad-value.}}"`),
   678  			nodes: []*v1.Node{
   679  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   680  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgChina}},
   681  			},
   682  		},
   683  		{
   684  			name: "Affinity with pods matching NamespaceSelector",
   685  			pod:  &v1.Pod{Spec: v1.PodSpec{Affinity: affinityNamespaceSelector}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
   686  			pods: []*v1.Pod{
   687  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
   688  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
   689  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team2", Labels: podLabelSecurityS1}},
   690  				{Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team1", Labels: podLabelSecurityS1}},
   691  			},
   692  			nodes: []*v1.Node{
   693  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   694  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   695  			},
   696  			expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: 0}},
   697  		},
   698  		{
   699  			name: "Affinity with pods matching both NamespaceSelector and Namespaces fields",
   700  			pod:  &v1.Pod{Spec: v1.PodSpec{Affinity: affinityNamespaceSelector}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
   701  			pods: []*v1.Pod{
   702  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
   703  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
   704  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team2", Labels: podLabelSecurityS1}},
   705  				{Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team1", Labels: podLabelSecurityS1}},
   706  			},
   707  			nodes: []*v1.Node{
   708  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   709  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   710  			},
   711  			expectedList: []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: 0}},
   712  		},
   713  		{
   714  			name: "Affinity with pods matching NamespaceSelector",
   715  			pod:  &v1.Pod{Spec: v1.PodSpec{Affinity: antiAffinityNamespaceSelector}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
   716  			pods: []*v1.Pod{
   717  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
   718  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
   719  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team2", Labels: podLabelSecurityS1}},
   720  				{Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team1", Labels: podLabelSecurityS1}},
   721  			},
   722  			nodes: []*v1.Node{
   723  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   724  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   725  			},
   726  			expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}},
   727  		},
   728  		{
   729  			name: "Affinity with pods matching both NamespaceSelector and Namespaces fields",
   730  			pod:  &v1.Pod{Spec: v1.PodSpec{Affinity: antiAffinityNamespaceSelector}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
   731  			pods: []*v1.Pod{
   732  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
   733  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelSecurityS1}},
   734  				{Spec: v1.PodSpec{NodeName: "node1"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team2", Labels: podLabelSecurityS1}},
   735  				{Spec: v1.PodSpec{NodeName: "node2"}, ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team1", Labels: podLabelSecurityS1}},
   736  			},
   737  			nodes: []*v1.Node{
   738  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   739  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   740  			},
   741  			expectedList: []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}},
   742  		},
   743  		{
   744  			name: "Ignore preferred terms of existing pods",
   745  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   746  			pods: []*v1.Pod{
   747  				{Spec: v1.PodSpec{NodeName: "node1", Affinity: stayWithS1InRegionAwayFromS2InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   748  				{Spec: v1.PodSpec{NodeName: "node2", Affinity: stayWithS2InRegion}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   749  			},
   750  			nodes: []*v1.Node{
   751  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   752  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   753  			},
   754  			expectedList:                       []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: 0}},
   755  			wantStatus:                         framework.NewStatus(framework.Skip),
   756  			ignorePreferredTermsOfExistingPods: true,
   757  		},
   758  		{
   759  			name: "Do not ignore preferred terms of existing pods",
   760  			pod:  &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   761  			pods: []*v1.Pod{
   762  				{Spec: v1.PodSpec{NodeName: "node1", Affinity: stayWithS1InRegionAwayFromS2InAz}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS1}},
   763  				{Spec: v1.PodSpec{NodeName: "node2", Affinity: stayWithS2InRegion}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   764  			},
   765  			nodes: []*v1.Node{
   766  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   767  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   768  			},
   769  			expectedList:                       []framework.NodeScore{{Name: "node1", Score: 0}, {Name: "node2", Score: framework.MaxNodeScore}},
   770  			ignorePreferredTermsOfExistingPods: false,
   771  		},
   772  		{
   773  			name:       "No nodes to score",
   774  			pod:        &v1.Pod{Spec: v1.PodSpec{NodeName: ""}, ObjectMeta: metav1.ObjectMeta{Labels: podLabelSecurityS2}},
   775  			pods:       []*v1.Pod{},
   776  			nodes:      []*v1.Node{},
   777  			wantStatus: framework.NewStatus(framework.Skip),
   778  		},
   779  	}
   780  	for _, test := range tests {
   781  		t.Run(test.name, func(t *testing.T) {
   782  			_, ctx := ktesting.NewTestContext(t)
   783  			ctx, cancel := context.WithCancel(ctx)
   784  			defer cancel()
   785  			state := framework.NewCycleState()
   786  			p := plugintesting.SetupPluginWithInformers(ctx, t, New, &config.InterPodAffinityArgs{HardPodAffinityWeight: 1, IgnorePreferredTermsOfExistingPods: test.ignorePreferredTermsOfExistingPods}, cache.NewSnapshot(test.pods, test.nodes), namespaces)
   787  			status := p.(framework.PreScorePlugin).PreScore(ctx, state, test.pod, tf.BuildNodeInfos(test.nodes))
   788  
   789  			if !status.IsSuccess() {
   790  				if status.Code() != test.wantStatus.Code() {
   791  					t.Errorf("InterPodAffinity#PreScore() returned unexpected status.Code got: %v, want: %v", status.Code(), test.wantStatus.Code())
   792  				}
   793  
   794  				if !strings.Contains(status.Message(), test.wantStatus.Message()) {
   795  					t.Errorf("InterPodAffinity#PreScore() returned unexpected status.Message got: %v, want: %v", status.Message(), test.wantStatus.Message())
   796  				}
   797  				return
   798  			}
   799  
   800  			var gotList framework.NodeScoreList
   801  			for _, n := range test.nodes {
   802  				nodeName := n.ObjectMeta.Name
   803  				score, status := p.(framework.ScorePlugin).Score(ctx, state, test.pod, nodeName)
   804  				if !status.IsSuccess() {
   805  					t.Errorf("unexpected error from Score: %v", status)
   806  				}
   807  				gotList = append(gotList, framework.NodeScore{Name: nodeName, Score: score})
   808  			}
   809  
   810  			status = p.(framework.ScorePlugin).ScoreExtensions().NormalizeScore(ctx, state, test.pod, gotList)
   811  			if !status.IsSuccess() {
   812  				t.Errorf("unexpected error from NormalizeScore: %v", status)
   813  			}
   814  
   815  			if diff := cmp.Diff(test.expectedList, gotList); diff != "" {
   816  				t.Errorf("node score list doesn't match (-want,+got): \n %s", diff)
   817  			}
   818  		})
   819  	}
   820  }
   821  
   822  func TestPreferredAffinityWithHardPodAffinitySymmetricWeight(t *testing.T) {
   823  	podLabelServiceS1 := map[string]string{
   824  		"service": "S1",
   825  	}
   826  	labelRgChina := map[string]string{
   827  		"region": "China",
   828  	}
   829  	labelRgIndia := map[string]string{
   830  		"region": "India",
   831  	}
   832  	labelAzAz1 := map[string]string{
   833  		"az": "az1",
   834  	}
   835  	hardPodAffinity := &v1.Affinity{
   836  		PodAffinity: &v1.PodAffinity{
   837  			RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   838  				{
   839  					LabelSelector: &metav1.LabelSelector{
   840  						MatchExpressions: []metav1.LabelSelectorRequirement{
   841  							{
   842  								Key:      "service",
   843  								Operator: metav1.LabelSelectorOpIn,
   844  								Values:   []string{"S1"},
   845  							},
   846  						},
   847  					},
   848  					Namespaces: []string{"", "subteam2.team2"},
   849  					NamespaceSelector: &metav1.LabelSelector{
   850  						MatchExpressions: []metav1.LabelSelectorRequirement{
   851  							{
   852  								Key:      "team",
   853  								Operator: metav1.LabelSelectorOpIn,
   854  								Values:   []string{"team1"},
   855  							},
   856  						},
   857  					},
   858  					TopologyKey: "region",
   859  				},
   860  			},
   861  		},
   862  	}
   863  	tests := []struct {
   864  		pod                   *v1.Pod
   865  		pods                  []*v1.Pod
   866  		nodes                 []*v1.Node
   867  		hardPodAffinityWeight int32
   868  		expectedList          framework.NodeScoreList
   869  		name                  string
   870  		wantStatus            *framework.Status
   871  	}{
   872  		{
   873  			name: "with default weight",
   874  			pod:  &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: podLabelServiceS1}},
   875  			pods: []*v1.Pod{
   876  				{Spec: v1.PodSpec{NodeName: "node1", Affinity: hardPodAffinity}},
   877  				{Spec: v1.PodSpec{NodeName: "node2", Affinity: hardPodAffinity}},
   878  			},
   879  			nodes: []*v1.Node{
   880  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   881  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   882  				{ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
   883  			},
   884  			hardPodAffinityWeight: v1.DefaultHardPodAffinitySymmetricWeight,
   885  			expectedList:          []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: framework.MaxNodeScore}, {Name: "node3", Score: 0}},
   886  		},
   887  		{
   888  			name: "with zero weight",
   889  			pod:  &v1.Pod{ObjectMeta: metav1.ObjectMeta{Labels: podLabelServiceS1}},
   890  			pods: []*v1.Pod{
   891  				{Spec: v1.PodSpec{NodeName: "node1", Affinity: hardPodAffinity}},
   892  				{Spec: v1.PodSpec{NodeName: "node2", Affinity: hardPodAffinity}},
   893  			},
   894  			nodes: []*v1.Node{
   895  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   896  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   897  				{ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
   898  			},
   899  			hardPodAffinityWeight: 0,
   900  			wantStatus:            framework.NewStatus(framework.Skip),
   901  		},
   902  		{
   903  			name: "with no matching namespace",
   904  			pod:  &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team2", Labels: podLabelServiceS1}},
   905  			pods: []*v1.Pod{
   906  				{Spec: v1.PodSpec{NodeName: "node1", Affinity: hardPodAffinity}},
   907  				{Spec: v1.PodSpec{NodeName: "node2", Affinity: hardPodAffinity}},
   908  			},
   909  			nodes: []*v1.Node{
   910  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   911  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   912  				{ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
   913  			},
   914  			hardPodAffinityWeight: v1.DefaultHardPodAffinitySymmetricWeight,
   915  			wantStatus:            framework.NewStatus(framework.Skip),
   916  		},
   917  		{
   918  			name: "with matching NamespaceSelector",
   919  			pod:  &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "subteam1.team1", Labels: podLabelServiceS1}},
   920  			pods: []*v1.Pod{
   921  				{Spec: v1.PodSpec{NodeName: "node1", Affinity: hardPodAffinity}},
   922  				{Spec: v1.PodSpec{NodeName: "node2", Affinity: hardPodAffinity}},
   923  			},
   924  			nodes: []*v1.Node{
   925  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   926  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   927  				{ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
   928  			},
   929  			hardPodAffinityWeight: v1.DefaultHardPodAffinitySymmetricWeight,
   930  			expectedList:          []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: framework.MaxNodeScore}, {Name: "node3", Score: 0}},
   931  		},
   932  		{
   933  			name: "with matching Namespaces",
   934  			pod:  &v1.Pod{ObjectMeta: metav1.ObjectMeta{Namespace: "subteam2.team2", Labels: podLabelServiceS1}},
   935  			pods: []*v1.Pod{
   936  				{Spec: v1.PodSpec{NodeName: "node1", Affinity: hardPodAffinity}},
   937  				{Spec: v1.PodSpec{NodeName: "node2", Affinity: hardPodAffinity}},
   938  			},
   939  			nodes: []*v1.Node{
   940  				{ObjectMeta: metav1.ObjectMeta{Name: "node1", Labels: labelRgChina}},
   941  				{ObjectMeta: metav1.ObjectMeta{Name: "node2", Labels: labelRgIndia}},
   942  				{ObjectMeta: metav1.ObjectMeta{Name: "node3", Labels: labelAzAz1}},
   943  			},
   944  			hardPodAffinityWeight: v1.DefaultHardPodAffinitySymmetricWeight,
   945  			expectedList:          []framework.NodeScore{{Name: "node1", Score: framework.MaxNodeScore}, {Name: "node2", Score: framework.MaxNodeScore}, {Name: "node3", Score: 0}},
   946  		},
   947  	}
   948  	for _, test := range tests {
   949  		t.Run(test.name, func(t *testing.T) {
   950  			_, ctx := ktesting.NewTestContext(t)
   951  			ctx, cancel := context.WithCancel(ctx)
   952  			defer cancel()
   953  			state := framework.NewCycleState()
   954  			p := plugintesting.SetupPluginWithInformers(ctx, t, New, &config.InterPodAffinityArgs{HardPodAffinityWeight: test.hardPodAffinityWeight}, cache.NewSnapshot(test.pods, test.nodes), namespaces)
   955  			status := p.(framework.PreScorePlugin).PreScore(ctx, state, test.pod, tf.BuildNodeInfos(test.nodes))
   956  			if !test.wantStatus.Equal(status) {
   957  				t.Errorf("InterPodAffinity#PreScore() returned unexpected status.Code got: %v, want: %v", status.Code(), test.wantStatus.Code())
   958  			}
   959  			if !status.IsSuccess() {
   960  				return
   961  			}
   962  
   963  			var gotList framework.NodeScoreList
   964  			for _, n := range test.nodes {
   965  				nodeName := n.ObjectMeta.Name
   966  				score, status := p.(framework.ScorePlugin).Score(ctx, state, test.pod, nodeName)
   967  				if !status.IsSuccess() {
   968  					t.Errorf("unexpected error: %v", status)
   969  				}
   970  				gotList = append(gotList, framework.NodeScore{Name: nodeName, Score: score})
   971  			}
   972  
   973  			status = p.(framework.ScorePlugin).ScoreExtensions().NormalizeScore(ctx, state, test.pod, gotList)
   974  			if !status.IsSuccess() {
   975  				t.Errorf("unexpected error: %v", status)
   976  			}
   977  
   978  			if !reflect.DeepEqual(test.expectedList, gotList) {
   979  				t.Errorf("expected:\n\t%+v,\ngot:\n\t%+v", test.expectedList, gotList)
   980  			}
   981  		})
   982  	}
   983  }
   984  

View as plain text