k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/test/integration/scheduler/filters/filters_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 filters
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"testing"
    23  	"time"
    24  
    25  	"github.com/google/go-cmp/cmp"
    26  	v1 "k8s.io/api/core/v1"
    27  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    28  	"k8s.io/apimachinery/pkg/api/resource"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/util/wait"
    31  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    32  	"k8s.io/client-go/kubernetes"
    33  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    34  	"k8s.io/component-helpers/storage/volume"
    35  	"k8s.io/kubernetes/pkg/features"
    36  	st "k8s.io/kubernetes/pkg/scheduler/testing"
    37  	testutils "k8s.io/kubernetes/test/integration/util"
    38  	imageutils "k8s.io/kubernetes/test/utils/image"
    39  	"k8s.io/kubernetes/test/utils/ktesting"
    40  	"k8s.io/utils/pointer"
    41  )
    42  
    43  var (
    44  	createAndWaitForNodesInCache = testutils.CreateAndWaitForNodesInCache
    45  	createNamespacesWithLabels   = testutils.CreateNamespacesWithLabels
    46  	createNode                   = testutils.CreateNode
    47  	updateNode                   = testutils.UpdateNode
    48  	createPausePod               = testutils.CreatePausePod
    49  	deletePod                    = testutils.DeletePod
    50  	getPod                       = testutils.GetPod
    51  	initPausePod                 = testutils.InitPausePod
    52  	initTest                     = testutils.InitTestSchedulerWithNS
    53  	podScheduledIn               = testutils.PodScheduledIn
    54  	podUnschedulable             = testutils.PodUnschedulable
    55  	waitForPodUnschedulable      = testutils.WaitForPodUnschedulable
    56  )
    57  
    58  // This file tests the scheduler predicates functionality.
    59  
    60  const pollInterval = 100 * time.Millisecond
    61  
    62  var (
    63  	ignorePolicy = v1.NodeInclusionPolicyIgnore
    64  	honorPolicy  = v1.NodeInclusionPolicyHonor
    65  	taints       = []v1.Taint{{Key: v1.TaintNodeUnschedulable, Value: "", Effect: v1.TaintEffectNoSchedule}}
    66  )
    67  
    68  // TestInterPodAffinity verifies that scheduler's inter pod affinity and
    69  // anti-affinity predicate functions works correctly.
    70  func TestInterPodAffinity(t *testing.T) {
    71  	podLabel := map[string]string{"service": "securityscan"}
    72  	podLabel2 := map[string]string{"security": "S1"}
    73  
    74  	defaultNS := "ns1"
    75  
    76  	tests := []struct {
    77  		name                           string
    78  		pod                            *v1.Pod
    79  		pods                           []*v1.Pod
    80  		fits                           bool
    81  		enableMatchLabelKeysInAffinity bool
    82  		errorType                      string
    83  	}{
    84  		{
    85  			name: "validates that a pod with an invalid podAffinity is rejected because of the LabelSelectorRequirement is invalid",
    86  			pod: &v1.Pod{
    87  				ObjectMeta: metav1.ObjectMeta{
    88  					Name:   "fakename",
    89  					Labels: podLabel2,
    90  				},
    91  				Spec: v1.PodSpec{
    92  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
    93  					Affinity: &v1.Affinity{
    94  						PodAffinity: &v1.PodAffinity{
    95  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
    96  								{
    97  									LabelSelector: &metav1.LabelSelector{
    98  										MatchExpressions: []metav1.LabelSelectorRequirement{
    99  											{
   100  												Key:      "security",
   101  												Operator: metav1.LabelSelectorOpDoesNotExist,
   102  											},
   103  										},
   104  									},
   105  									TopologyKey: "region",
   106  								},
   107  							},
   108  						},
   109  					},
   110  				},
   111  			},
   112  			fits:      false,
   113  			errorType: "invalidPod",
   114  		},
   115  		{
   116  			name: "validates that Inter-pod-Affinity is respected if not matching",
   117  			pod: &v1.Pod{
   118  				ObjectMeta: metav1.ObjectMeta{
   119  					Name:   "fakename",
   120  					Labels: podLabel2,
   121  				},
   122  				Spec: v1.PodSpec{
   123  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   124  					Affinity: &v1.Affinity{
   125  						PodAffinity: &v1.PodAffinity{
   126  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   127  								{
   128  									LabelSelector: &metav1.LabelSelector{
   129  										MatchExpressions: []metav1.LabelSelectorRequirement{
   130  											{
   131  												Key:      "security",
   132  												Operator: metav1.LabelSelectorOpIn,
   133  												Values:   []string{"securityscan"},
   134  											},
   135  										},
   136  									},
   137  									TopologyKey: "region",
   138  								},
   139  							},
   140  						},
   141  					},
   142  				},
   143  			},
   144  			fits: false,
   145  		},
   146  		{
   147  			name: "validates that InterPodAffinity is respected if matching. requiredDuringSchedulingIgnoredDuringExecution in PodAffinity using In operator that matches the existing pod",
   148  			pod: &v1.Pod{
   149  				ObjectMeta: metav1.ObjectMeta{
   150  					Name:   "fakename",
   151  					Labels: podLabel2,
   152  				},
   153  				Spec: v1.PodSpec{
   154  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   155  					Affinity: &v1.Affinity{
   156  						PodAffinity: &v1.PodAffinity{
   157  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   158  								{
   159  									LabelSelector: &metav1.LabelSelector{
   160  										MatchExpressions: []metav1.LabelSelectorRequirement{
   161  											{
   162  												Key:      "service",
   163  												Operator: metav1.LabelSelectorOpIn,
   164  												Values:   []string{"securityscan", "value2"},
   165  											},
   166  										},
   167  									},
   168  									TopologyKey: "region",
   169  								},
   170  							},
   171  						},
   172  					},
   173  				},
   174  			},
   175  			pods: []*v1.Pod{{
   176  				ObjectMeta: metav1.ObjectMeta{
   177  					Name:   "fakename2",
   178  					Labels: podLabel,
   179  				},
   180  				Spec: v1.PodSpec{
   181  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   182  					NodeName:   "testnode-0",
   183  				},
   184  			},
   185  			},
   186  			fits: true,
   187  		},
   188  		{
   189  			name: "validates that InterPodAffinity is respected if matching. requiredDuringSchedulingIgnoredDuringExecution in PodAffinity using not in operator in labelSelector that matches the existing pod",
   190  			pod: &v1.Pod{
   191  				ObjectMeta: metav1.ObjectMeta{
   192  					Name:   "fakename",
   193  					Labels: podLabel2,
   194  				},
   195  				Spec: v1.PodSpec{
   196  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   197  					Affinity: &v1.Affinity{
   198  						PodAffinity: &v1.PodAffinity{
   199  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   200  								{
   201  									LabelSelector: &metav1.LabelSelector{
   202  										MatchExpressions: []metav1.LabelSelectorRequirement{
   203  											{
   204  												Key:      "service",
   205  												Operator: metav1.LabelSelectorOpNotIn,
   206  												Values:   []string{"securityscan3", "value3"},
   207  											},
   208  										},
   209  									},
   210  									TopologyKey: "region",
   211  								},
   212  							},
   213  						},
   214  					},
   215  				},
   216  			},
   217  			pods: []*v1.Pod{{Spec: v1.PodSpec{
   218  				Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   219  				NodeName:   "testnode-0"},
   220  				ObjectMeta: metav1.ObjectMeta{
   221  					Name:   "fakename2",
   222  					Labels: podLabel}}},
   223  			fits: true,
   224  		},
   225  		{
   226  			name: "validates that inter-pod-affinity is respected when pods have different Namespaces",
   227  			pod: &v1.Pod{
   228  				ObjectMeta: metav1.ObjectMeta{
   229  					Name:   "fakename",
   230  					Labels: podLabel2,
   231  				},
   232  				Spec: v1.PodSpec{
   233  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   234  					Affinity: &v1.Affinity{
   235  						PodAffinity: &v1.PodAffinity{
   236  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   237  								{
   238  									LabelSelector: &metav1.LabelSelector{
   239  										MatchExpressions: []metav1.LabelSelectorRequirement{
   240  											{
   241  												Key:      "service",
   242  												Operator: metav1.LabelSelectorOpIn,
   243  												Values:   []string{"securityscan", "value2"},
   244  											},
   245  										},
   246  									},
   247  									TopologyKey: "region",
   248  									Namespaces:  []string{"diff-namespace"},
   249  								},
   250  							},
   251  						},
   252  					},
   253  				},
   254  			},
   255  			pods: []*v1.Pod{{Spec: v1.PodSpec{
   256  				Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   257  				NodeName:   "testnode-0"},
   258  				ObjectMeta: metav1.ObjectMeta{
   259  					Name:   "fakename2",
   260  					Labels: podLabel, Namespace: "ns2"}}},
   261  			fits: false,
   262  		},
   263  		{
   264  			name: "Doesn't satisfy the PodAffinity because of unmatching labelSelector with the existing pod",
   265  			pod: &v1.Pod{
   266  				ObjectMeta: metav1.ObjectMeta{
   267  					Name:   "fakename",
   268  					Labels: podLabel,
   269  				},
   270  				Spec: v1.PodSpec{
   271  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   272  					Affinity: &v1.Affinity{
   273  						PodAffinity: &v1.PodAffinity{
   274  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   275  								{
   276  									LabelSelector: &metav1.LabelSelector{
   277  										MatchExpressions: []metav1.LabelSelectorRequirement{
   278  											{
   279  												Key:      "service",
   280  												Operator: metav1.LabelSelectorOpIn,
   281  												Values:   []string{"antivirusscan", "value2"},
   282  											},
   283  										},
   284  									},
   285  									TopologyKey: "region",
   286  								},
   287  							},
   288  						},
   289  					},
   290  				},
   291  			},
   292  			pods: []*v1.Pod{{Spec: v1.PodSpec{
   293  				Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   294  				NodeName:   "testnode-0"}, ObjectMeta: metav1.ObjectMeta{
   295  				Name:   "fakename2",
   296  				Labels: podLabel}}},
   297  			fits: false,
   298  		},
   299  		{
   300  			name: "validates that InterPodAffinity is respected if matching with multiple affinities in multiple RequiredDuringSchedulingIgnoredDuringExecution ",
   301  			pod: &v1.Pod{
   302  				ObjectMeta: metav1.ObjectMeta{
   303  					Name:   "fakename",
   304  					Labels: podLabel2,
   305  				},
   306  				Spec: v1.PodSpec{
   307  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   308  					Affinity: &v1.Affinity{
   309  						PodAffinity: &v1.PodAffinity{
   310  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   311  								{
   312  									LabelSelector: &metav1.LabelSelector{
   313  										MatchExpressions: []metav1.LabelSelectorRequirement{
   314  											{
   315  												Key:      "service",
   316  												Operator: metav1.LabelSelectorOpExists,
   317  											}, {
   318  												Key:      "wrongkey",
   319  												Operator: metav1.LabelSelectorOpDoesNotExist,
   320  											},
   321  										},
   322  									},
   323  									TopologyKey: "region",
   324  								}, {
   325  									LabelSelector: &metav1.LabelSelector{
   326  										MatchExpressions: []metav1.LabelSelectorRequirement{
   327  											{
   328  												Key:      "service",
   329  												Operator: metav1.LabelSelectorOpIn,
   330  												Values:   []string{"securityscan"},
   331  											}, {
   332  												Key:      "service",
   333  												Operator: metav1.LabelSelectorOpNotIn,
   334  												Values:   []string{"WrongValue"},
   335  											},
   336  										},
   337  									},
   338  									TopologyKey: "region",
   339  								},
   340  							},
   341  						},
   342  					},
   343  				},
   344  			},
   345  			pods: []*v1.Pod{{Spec: v1.PodSpec{
   346  				Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   347  				NodeName:   "testnode-0"}, ObjectMeta: metav1.ObjectMeta{
   348  				Name:   "fakename2",
   349  				Labels: podLabel}}},
   350  			fits: true,
   351  		},
   352  		{
   353  			name: "The labelSelector requirements(items of matchExpressions) are ANDed, the pod cannot schedule onto the node because one of the matchExpression items doesn't match.",
   354  			pod: &v1.Pod{
   355  				ObjectMeta: metav1.ObjectMeta{
   356  					Labels: podLabel2,
   357  					Name:   "fakename",
   358  				},
   359  				Spec: v1.PodSpec{
   360  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   361  					Affinity: &v1.Affinity{
   362  						PodAffinity: &v1.PodAffinity{
   363  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   364  								{
   365  									LabelSelector: &metav1.LabelSelector{
   366  										MatchExpressions: []metav1.LabelSelectorRequirement{
   367  											{
   368  												Key:      "service",
   369  												Operator: metav1.LabelSelectorOpExists,
   370  											}, {
   371  												Key:      "wrongkey",
   372  												Operator: metav1.LabelSelectorOpDoesNotExist,
   373  											},
   374  										},
   375  									},
   376  									TopologyKey: "region",
   377  								}, {
   378  									LabelSelector: &metav1.LabelSelector{
   379  										MatchExpressions: []metav1.LabelSelectorRequirement{
   380  											{
   381  												Key:      "service",
   382  												Operator: metav1.LabelSelectorOpIn,
   383  												Values:   []string{"securityscan2"},
   384  											}, {
   385  												Key:      "service",
   386  												Operator: metav1.LabelSelectorOpNotIn,
   387  												Values:   []string{"WrongValue"},
   388  											},
   389  										},
   390  									},
   391  									TopologyKey: "region",
   392  								},
   393  							},
   394  						},
   395  					},
   396  				},
   397  			},
   398  			pods: []*v1.Pod{{Spec: v1.PodSpec{
   399  				Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   400  				NodeName:   "testnode-0"}, ObjectMeta: metav1.ObjectMeta{
   401  				Name:   "fakename2",
   402  				Labels: podLabel}}},
   403  			fits: false,
   404  		},
   405  		{
   406  			name: "validates that InterPod Affinity and AntiAffinity is respected if matching",
   407  			pod: &v1.Pod{
   408  				ObjectMeta: metav1.ObjectMeta{
   409  					Name:   "fakename",
   410  					Labels: podLabel2,
   411  				},
   412  				Spec: v1.PodSpec{
   413  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   414  					Affinity: &v1.Affinity{
   415  						PodAffinity: &v1.PodAffinity{
   416  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   417  								{
   418  									LabelSelector: &metav1.LabelSelector{
   419  										MatchExpressions: []metav1.LabelSelectorRequirement{
   420  											{
   421  												Key:      "service",
   422  												Operator: metav1.LabelSelectorOpIn,
   423  												Values:   []string{"securityscan", "value2"},
   424  											},
   425  										},
   426  									},
   427  									TopologyKey: "region",
   428  								},
   429  							},
   430  						},
   431  						PodAntiAffinity: &v1.PodAntiAffinity{
   432  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   433  								{
   434  									LabelSelector: &metav1.LabelSelector{
   435  										MatchExpressions: []metav1.LabelSelectorRequirement{
   436  											{
   437  												Key:      "service",
   438  												Operator: metav1.LabelSelectorOpIn,
   439  												Values:   []string{"antivirusscan", "value2"},
   440  											},
   441  										},
   442  									},
   443  									TopologyKey: "node",
   444  								},
   445  							},
   446  						},
   447  					},
   448  				},
   449  			},
   450  			pods: []*v1.Pod{{Spec: v1.PodSpec{
   451  				Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   452  				NodeName:   "testnode-0"}, ObjectMeta: metav1.ObjectMeta{
   453  				Name:   "fakename2",
   454  				Labels: podLabel}}},
   455  			fits: true,
   456  		},
   457  		{
   458  			name: "satisfies the PodAffinity and PodAntiAffinity and PodAntiAffinity symmetry with the existing pod",
   459  			pod: &v1.Pod{
   460  				ObjectMeta: metav1.ObjectMeta{
   461  					Name:   "fakename",
   462  					Labels: podLabel2,
   463  				},
   464  				Spec: v1.PodSpec{
   465  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   466  					Affinity: &v1.Affinity{
   467  						PodAffinity: &v1.PodAffinity{
   468  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   469  								{
   470  									LabelSelector: &metav1.LabelSelector{
   471  										MatchExpressions: []metav1.LabelSelectorRequirement{
   472  											{
   473  												Key:      "service",
   474  												Operator: metav1.LabelSelectorOpIn,
   475  												Values:   []string{"securityscan", "value2"},
   476  											},
   477  										},
   478  									},
   479  									TopologyKey: "region",
   480  								},
   481  							},
   482  						},
   483  						PodAntiAffinity: &v1.PodAntiAffinity{
   484  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   485  								{
   486  									LabelSelector: &metav1.LabelSelector{
   487  										MatchExpressions: []metav1.LabelSelectorRequirement{
   488  											{
   489  												Key:      "service",
   490  												Operator: metav1.LabelSelectorOpIn,
   491  												Values:   []string{"antivirusscan", "value2"},
   492  											},
   493  										},
   494  									},
   495  									TopologyKey: "node",
   496  								},
   497  							},
   498  						},
   499  					},
   500  				},
   501  			},
   502  			pods: []*v1.Pod{
   503  				{
   504  					Spec: v1.PodSpec{
   505  						Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   506  						NodeName:   "testnode-0",
   507  						Affinity: &v1.Affinity{
   508  							PodAntiAffinity: &v1.PodAntiAffinity{
   509  								RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   510  									{
   511  										LabelSelector: &metav1.LabelSelector{
   512  											MatchExpressions: []metav1.LabelSelectorRequirement{
   513  												{
   514  													Key:      "service",
   515  													Operator: metav1.LabelSelectorOpIn,
   516  													Values:   []string{"antivirusscan", "value2"},
   517  												},
   518  											},
   519  										},
   520  										TopologyKey: "node",
   521  									},
   522  								},
   523  							},
   524  						},
   525  					},
   526  					ObjectMeta: metav1.ObjectMeta{
   527  						Name:   "fakename2",
   528  						Labels: podLabel},
   529  				},
   530  			},
   531  			fits: true,
   532  		},
   533  		{
   534  			name: "satisfies the PodAffinity but doesn't satisfies the PodAntiAffinity with the existing pod",
   535  			pod: &v1.Pod{
   536  				ObjectMeta: metav1.ObjectMeta{
   537  					Name:   "fakename",
   538  					Labels: podLabel2,
   539  				},
   540  				Spec: v1.PodSpec{
   541  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   542  					Affinity: &v1.Affinity{
   543  						PodAffinity: &v1.PodAffinity{
   544  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   545  								{
   546  									LabelSelector: &metav1.LabelSelector{
   547  										MatchExpressions: []metav1.LabelSelectorRequirement{
   548  											{
   549  												Key:      "service",
   550  												Operator: metav1.LabelSelectorOpIn,
   551  												Values:   []string{"securityscan", "value2"},
   552  											},
   553  										},
   554  									},
   555  									TopologyKey: "region",
   556  								},
   557  							},
   558  						},
   559  						PodAntiAffinity: &v1.PodAntiAffinity{
   560  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   561  								{
   562  									LabelSelector: &metav1.LabelSelector{
   563  										MatchExpressions: []metav1.LabelSelectorRequirement{
   564  											{
   565  												Key:      "service",
   566  												Operator: metav1.LabelSelectorOpIn,
   567  												Values:   []string{"securityscan", "value2"},
   568  											},
   569  										},
   570  									},
   571  									TopologyKey: "zone",
   572  								},
   573  							},
   574  						},
   575  					},
   576  				},
   577  			},
   578  			pods: []*v1.Pod{{Spec: v1.PodSpec{
   579  				Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   580  				NodeName:   "testnode-0"}, ObjectMeta: metav1.ObjectMeta{
   581  				Name:   "fakename2",
   582  				Labels: podLabel}}},
   583  			fits: false,
   584  		},
   585  		{
   586  			name: "satisfies the PodAffinity and PodAntiAffinity but doesn't satisfies PodAntiAffinity symmetry with the existing pod",
   587  			pod: &v1.Pod{
   588  				ObjectMeta: metav1.ObjectMeta{
   589  					Name:   "fakename",
   590  					Labels: podLabel,
   591  				},
   592  				Spec: v1.PodSpec{
   593  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   594  					Affinity: &v1.Affinity{
   595  						PodAffinity: &v1.PodAffinity{
   596  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   597  								{
   598  									LabelSelector: &metav1.LabelSelector{
   599  										MatchExpressions: []metav1.LabelSelectorRequirement{
   600  											{
   601  												Key:      "service",
   602  												Operator: metav1.LabelSelectorOpIn,
   603  												Values:   []string{"securityscan", "value2"},
   604  											},
   605  										},
   606  									},
   607  									TopologyKey: "region",
   608  								},
   609  							},
   610  						},
   611  						PodAntiAffinity: &v1.PodAntiAffinity{
   612  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   613  								{
   614  									LabelSelector: &metav1.LabelSelector{
   615  										MatchExpressions: []metav1.LabelSelectorRequirement{
   616  											{
   617  												Key:      "service",
   618  												Operator: metav1.LabelSelectorOpIn,
   619  												Values:   []string{"antivirusscan", "value2"},
   620  											},
   621  										},
   622  									},
   623  									TopologyKey: "node",
   624  								},
   625  							},
   626  						},
   627  					},
   628  				},
   629  			},
   630  			pods: []*v1.Pod{
   631  				{
   632  					Spec: v1.PodSpec{
   633  						NodeName:   "testnode-0",
   634  						Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   635  						Affinity: &v1.Affinity{
   636  							PodAntiAffinity: &v1.PodAntiAffinity{
   637  								RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   638  									{
   639  										LabelSelector: &metav1.LabelSelector{
   640  											MatchExpressions: []metav1.LabelSelectorRequirement{
   641  												{
   642  													Key:      "service",
   643  													Operator: metav1.LabelSelectorOpIn,
   644  													Values:   []string{"securityscan", "value3"},
   645  												},
   646  											},
   647  										},
   648  										TopologyKey: "zone",
   649  									},
   650  								},
   651  							},
   652  						},
   653  					},
   654  					ObjectMeta: metav1.ObjectMeta{
   655  						Name:   "fakename2",
   656  						Labels: podLabel},
   657  				},
   658  			},
   659  			fits: false,
   660  		},
   661  		{
   662  			name: "pod matches its own Label in PodAffinity and that matches the existing pod Labels",
   663  			pod: &v1.Pod{
   664  				ObjectMeta: metav1.ObjectMeta{
   665  					Name:   "fakename",
   666  					Labels: podLabel,
   667  				},
   668  				Spec: v1.PodSpec{
   669  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   670  					Affinity: &v1.Affinity{
   671  						PodAffinity: &v1.PodAffinity{
   672  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   673  								{
   674  									LabelSelector: &metav1.LabelSelector{
   675  										MatchExpressions: []metav1.LabelSelectorRequirement{
   676  											{
   677  												Key:      "service",
   678  												Operator: metav1.LabelSelectorOpNotIn,
   679  												Values:   []string{"securityscan", "value2"},
   680  											},
   681  										},
   682  									},
   683  									TopologyKey: "region",
   684  								},
   685  							},
   686  						},
   687  					},
   688  				},
   689  			},
   690  			pods: []*v1.Pod{{Spec: v1.PodSpec{
   691  				Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   692  				NodeName:   "machine2"}, ObjectMeta: metav1.ObjectMeta{
   693  				Name:   "fakename2",
   694  				Labels: podLabel}}},
   695  			fits: false,
   696  		},
   697  		{
   698  			name: "Verify that PodAntiAffinity of an existing pod is respected when PodAntiAffinity symmetry is not satisfied with the existing pod",
   699  			pod: &v1.Pod{
   700  				ObjectMeta: metav1.ObjectMeta{
   701  					Name:   "fakename",
   702  					Labels: podLabel,
   703  				},
   704  				Spec: v1.PodSpec{Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}}},
   705  			},
   706  			pods: []*v1.Pod{
   707  				{
   708  					Spec: v1.PodSpec{NodeName: "testnode-0",
   709  						Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   710  						Affinity: &v1.Affinity{
   711  							PodAntiAffinity: &v1.PodAntiAffinity{
   712  								RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   713  									{
   714  										LabelSelector: &metav1.LabelSelector{
   715  											MatchExpressions: []metav1.LabelSelectorRequirement{
   716  												{
   717  													Key:      "service",
   718  													Operator: metav1.LabelSelectorOpIn,
   719  													Values:   []string{"securityscan", "value2"},
   720  												},
   721  											},
   722  										},
   723  										TopologyKey: "zone",
   724  									},
   725  								},
   726  							},
   727  						},
   728  					},
   729  					ObjectMeta: metav1.ObjectMeta{
   730  						Name:   "fakename2",
   731  						Labels: podLabel},
   732  				},
   733  			},
   734  			fits: false,
   735  		},
   736  		{
   737  			name: "Verify that PodAntiAffinity from existing pod is respected when pod statisfies PodAntiAffinity symmetry with the existing pod",
   738  			pod: &v1.Pod{
   739  				ObjectMeta: metav1.ObjectMeta{
   740  					Name:   "fake-name",
   741  					Labels: podLabel,
   742  				},
   743  				Spec: v1.PodSpec{Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}}},
   744  			},
   745  			pods: []*v1.Pod{
   746  				{
   747  					Spec: v1.PodSpec{NodeName: "testnode-0",
   748  						Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   749  						Affinity: &v1.Affinity{
   750  							PodAntiAffinity: &v1.PodAntiAffinity{
   751  								RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   752  									{
   753  										LabelSelector: &metav1.LabelSelector{
   754  											MatchExpressions: []metav1.LabelSelectorRequirement{
   755  												{
   756  													Key:      "service",
   757  													Operator: metav1.LabelSelectorOpNotIn,
   758  													Values:   []string{"securityscan", "value2"},
   759  												},
   760  											},
   761  										},
   762  										TopologyKey: "zone",
   763  									},
   764  								},
   765  							},
   766  						},
   767  					},
   768  					ObjectMeta: metav1.ObjectMeta{
   769  						Name:   "fake-name2",
   770  						Labels: podLabel},
   771  				},
   772  			},
   773  			fits: true,
   774  		},
   775  		{
   776  			name: "nodes[0] and nodes[1] have same topologyKey and label value. nodes[0] has an existing pod that matches the inter pod affinity rule. The new pod can not be scheduled onto either of the two nodes.",
   777  			pod: &v1.Pod{
   778  				ObjectMeta: metav1.ObjectMeta{Name: "fake-name2"},
   779  				Spec: v1.PodSpec{
   780  					Containers:   []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   781  					NodeSelector: map[string]string{"region": "r1"},
   782  					Affinity: &v1.Affinity{
   783  						PodAntiAffinity: &v1.PodAntiAffinity{
   784  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   785  								{
   786  									LabelSelector: &metav1.LabelSelector{
   787  										MatchExpressions: []metav1.LabelSelectorRequirement{
   788  											{
   789  												Key:      "foo",
   790  												Operator: metav1.LabelSelectorOpIn,
   791  												Values:   []string{"abc"},
   792  											},
   793  										},
   794  									},
   795  									TopologyKey: "region",
   796  								},
   797  							},
   798  						},
   799  					},
   800  				},
   801  			},
   802  			pods: []*v1.Pod{
   803  				{Spec: v1.PodSpec{
   804  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   805  					NodeName:   "testnode-0"}, ObjectMeta: metav1.ObjectMeta{Name: "fakename", Labels: map[string]string{"foo": "abc"}}},
   806  			},
   807  			fits: false,
   808  		},
   809  		{
   810  			name: "anti affinity: matchLabelKeys is merged into LabelSelector with In operator (feature flag: enabled)",
   811  			pod: &v1.Pod{
   812  				Spec: v1.PodSpec{
   813  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   814  					Affinity: &v1.Affinity{
   815  						PodAntiAffinity: &v1.PodAntiAffinity{
   816  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   817  								{
   818  									TopologyKey: "zone",
   819  									LabelSelector: &metav1.LabelSelector{
   820  										MatchExpressions: []metav1.LabelSelectorRequirement{
   821  											{
   822  												Key:      "foo",
   823  												Operator: metav1.LabelSelectorOpExists,
   824  											},
   825  										},
   826  									},
   827  									MatchLabelKeys: []string{"bar"},
   828  								},
   829  							},
   830  						},
   831  					},
   832  				},
   833  				ObjectMeta: metav1.ObjectMeta{
   834  					Name:   "incoming",
   835  					Labels: map[string]string{"foo": "", "bar": "a"},
   836  				},
   837  			},
   838  			pods: []*v1.Pod{
   839  				// It matches the incoming Pod's anti affinity's labelSelector.
   840  				// BUT, the matchLabelKeys make the existing Pod's anti affinity's labelSelector not match with this label.
   841  				{
   842  					Spec: v1.PodSpec{
   843  						Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   844  						NodeName:   "testnode-0",
   845  					},
   846  					ObjectMeta: metav1.ObjectMeta{Name: "pod1", Labels: map[string]string{"foo": "", "bar": "fuga"}},
   847  				},
   848  				// It matches the incoming Pod's anti affinity's labelSelector.
   849  				// BUT, the matchLabelKeys make the existing Pod's anti affinity's labelSelector not match with this label.
   850  				{
   851  					Spec: v1.PodSpec{
   852  						Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   853  						NodeName:   "testnode-0",
   854  					},
   855  					ObjectMeta: metav1.ObjectMeta{Name: "pod2", Labels: map[string]string{"foo": "", "bar": "hoge"}},
   856  				},
   857  			},
   858  			enableMatchLabelKeysInAffinity: true,
   859  			fits:                           true,
   860  		},
   861  		{
   862  			name: "anti affinity: mismatchLabelKeys is merged into LabelSelector with NotIn operator (feature flag: enabled)",
   863  			pod: &v1.Pod{
   864  				Spec: v1.PodSpec{
   865  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   866  					Affinity: &v1.Affinity{
   867  						PodAntiAffinity: &v1.PodAntiAffinity{
   868  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   869  								{
   870  									TopologyKey: "zone",
   871  									LabelSelector: &metav1.LabelSelector{
   872  										MatchExpressions: []metav1.LabelSelectorRequirement{
   873  											{
   874  												Key:      "foo",
   875  												Operator: metav1.LabelSelectorOpExists,
   876  											},
   877  										},
   878  									},
   879  									MismatchLabelKeys: []string{"bar"},
   880  								},
   881  							},
   882  						},
   883  					},
   884  				},
   885  				ObjectMeta: metav1.ObjectMeta{
   886  					Name:   "incoming",
   887  					Labels: map[string]string{"foo": "", "bar": "a"},
   888  				},
   889  			},
   890  			pods: []*v1.Pod{
   891  				// It matches the incoming Pod's anti affinity's labelSelector.
   892  				// BUT, the mismatchLabelKeys make the existing Pod's anti affinity's labelSelector not match with this label.
   893  				{
   894  					Spec: v1.PodSpec{
   895  						Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   896  						NodeName:   "testnode-0",
   897  					},
   898  					ObjectMeta: metav1.ObjectMeta{Name: "pod1", Labels: map[string]string{"foo": "", "bar": "a"}},
   899  				},
   900  				// It matches the incoming Pod's anti affinity's labelSelector.
   901  				// BUT, the mismatchLabelKeys make the existing Pod's anti affinity's labelSelector not match with this label.
   902  				{
   903  					Spec: v1.PodSpec{
   904  						Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   905  						NodeName:   "testnode-0",
   906  					},
   907  					ObjectMeta: metav1.ObjectMeta{Name: "pod2", Labels: map[string]string{"foo": "", "bar": "a"}},
   908  				},
   909  			},
   910  			enableMatchLabelKeysInAffinity: true,
   911  			fits:                           true,
   912  		},
   913  		{
   914  			name: "affinity: matchLabelKeys is merged into LabelSelector with In operator (feature flag: enabled)",
   915  			pod: &v1.Pod{
   916  				Spec: v1.PodSpec{
   917  					Containers: []v1.Container{
   918  						{
   919  							Name:  "container",
   920  							Image: imageutils.GetPauseImageName(),
   921  							Resources: v1.ResourceRequirements{
   922  								Requests: v1.ResourceList{
   923  									v1.ResourceMemory: resource.MustParse("1G"),
   924  								},
   925  							},
   926  						},
   927  					},
   928  					Affinity: &v1.Affinity{
   929  						PodAffinity: &v1.PodAffinity{
   930  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
   931  								{
   932  									TopologyKey: "node",
   933  									LabelSelector: &metav1.LabelSelector{
   934  										MatchExpressions: []metav1.LabelSelectorRequirement{
   935  											{
   936  												Key:      "foo",
   937  												Operator: metav1.LabelSelectorOpExists,
   938  											},
   939  										},
   940  									},
   941  									MatchLabelKeys: []string{"bar"},
   942  								},
   943  							},
   944  						},
   945  					},
   946  				},
   947  				ObjectMeta: metav1.ObjectMeta{
   948  					Name:   "incoming",
   949  					Labels: map[string]string{"foo": "", "bar": "a"},
   950  				},
   951  			},
   952  			pods: []*v1.Pod{
   953  				{
   954  					// It matches the incoming affinity. But, it uses all resources on nodes[1].
   955  					// So, the incoming Pod can no longer get scheduled on nodes[1].
   956  					Spec: v1.PodSpec{
   957  						Containers: []v1.Container{
   958  							{
   959  								Name:  "container",
   960  								Image: imageutils.GetPauseImageName(),
   961  								Resources: v1.ResourceRequirements{
   962  									Requests: v1.ResourceList{
   963  										v1.ResourceMemory: resource.MustParse("1G"),
   964  									},
   965  								},
   966  							},
   967  						},
   968  						NodeName: "anothernode-0",
   969  					},
   970  					ObjectMeta: metav1.ObjectMeta{Name: "pod1", Labels: map[string]string{"foo": "", "bar": "a"}},
   971  				},
   972  				{
   973  					// It doesn't match the incoming affinity due to matchLabelKeys.
   974  					Spec: v1.PodSpec{
   975  						Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   976  						NodeName:   "testnode-0",
   977  					},
   978  					ObjectMeta: metav1.ObjectMeta{Name: "pod2", Labels: map[string]string{"foo": "", "bar": "hoge"}},
   979  				},
   980  				{
   981  					// It doesn't match the incoming affinity due to matchLabelKeys.
   982  					Spec: v1.PodSpec{
   983  						Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
   984  						NodeName:   "testnode-0",
   985  					},
   986  					ObjectMeta: metav1.ObjectMeta{Name: "pod3", Labels: map[string]string{"foo": "", "bar": "fuga"}},
   987  				},
   988  			},
   989  			enableMatchLabelKeysInAffinity: true,
   990  			fits:                           false,
   991  		},
   992  		{
   993  			name: "affinity: mismatchLabelKeys is merged into LabelSelector with NotIn operator (feature flag: enabled)",
   994  			pod: &v1.Pod{
   995  				Spec: v1.PodSpec{
   996  					Containers: []v1.Container{
   997  						{
   998  							Name:  "container",
   999  							Image: imageutils.GetPauseImageName(),
  1000  							Resources: v1.ResourceRequirements{
  1001  								Requests: v1.ResourceList{
  1002  									v1.ResourceMemory: resource.MustParse("1G"),
  1003  								},
  1004  							},
  1005  						},
  1006  					},
  1007  					Affinity: &v1.Affinity{
  1008  						PodAffinity: &v1.PodAffinity{
  1009  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
  1010  								{
  1011  									TopologyKey: "node",
  1012  									LabelSelector: &metav1.LabelSelector{
  1013  										MatchExpressions: []metav1.LabelSelectorRequirement{
  1014  											{
  1015  												Key:      "foo",
  1016  												Operator: metav1.LabelSelectorOpExists,
  1017  											},
  1018  										},
  1019  									},
  1020  									MismatchLabelKeys: []string{"bar"},
  1021  								},
  1022  							},
  1023  						},
  1024  					},
  1025  				},
  1026  				ObjectMeta: metav1.ObjectMeta{
  1027  					Name:   "incoming",
  1028  					Labels: map[string]string{"foo": "", "bar": "a"},
  1029  				},
  1030  			},
  1031  			pods: []*v1.Pod{
  1032  				{
  1033  					// It matches the incoming affinity. But, it uses all resources on nodes[1].
  1034  					// So, the incoming Pod can no longer get scheduled on nodes[1].
  1035  					Spec: v1.PodSpec{
  1036  						Containers: []v1.Container{
  1037  							{
  1038  								Name:  "container",
  1039  								Image: imageutils.GetPauseImageName(),
  1040  								Resources: v1.ResourceRequirements{
  1041  									Requests: v1.ResourceList{
  1042  										v1.ResourceMemory: resource.MustParse("1G"),
  1043  									},
  1044  								},
  1045  							},
  1046  						},
  1047  						NodeName: "anothernode-0",
  1048  					},
  1049  					ObjectMeta: metav1.ObjectMeta{Name: "pod1", Labels: map[string]string{"foo": "", "bar": "fuga"}},
  1050  				},
  1051  				{
  1052  					// It doesn't match the incoming affinity due to mismatchLabelKeys.
  1053  					Spec: v1.PodSpec{
  1054  						Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
  1055  						NodeName:   "testnode-0",
  1056  					},
  1057  					ObjectMeta: metav1.ObjectMeta{Name: "pod2", Labels: map[string]string{"foo": "", "bar": "a"}},
  1058  				},
  1059  				{
  1060  					// It doesn't match the incoming affinity due to mismatchLabelKeys.
  1061  					Spec: v1.PodSpec{
  1062  						Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
  1063  						NodeName:   "testnode-0",
  1064  					},
  1065  					ObjectMeta: metav1.ObjectMeta{Name: "pod3", Labels: map[string]string{"foo": "", "bar": "a"}},
  1066  				},
  1067  			},
  1068  			enableMatchLabelKeysInAffinity: true,
  1069  			fits:                           false,
  1070  		},
  1071  	}
  1072  
  1073  	for _, test := range tests {
  1074  		t.Run(test.name, func(t *testing.T) {
  1075  			featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MatchLabelKeysInPodAffinity, test.enableMatchLabelKeysInAffinity)
  1076  			_, ctx := ktesting.NewTestContext(t)
  1077  
  1078  			testCtx := initTest(t, "")
  1079  			cs := testCtx.ClientSet
  1080  
  1081  			if _, err := createNode(cs, st.MakeNode().Name("testnode-0").Label("region", "r1").Label("zone", "z11").Label("node", "n1").Capacity(
  1082  				map[v1.ResourceName]string{
  1083  					v1.ResourceMemory: "1G",
  1084  				},
  1085  			).Obj()); err != nil {
  1086  				t.Fatalf("failed to create node: %v", err)
  1087  			}
  1088  
  1089  			// another test node has the same "region" and "zone" labels as testnode, but has a different "node" label.
  1090  			if _, err := createNode(cs, st.MakeNode().Name("anothernode-0").Label("region", "r1").Label("zone", "z11").Label("node", "n2").Capacity(
  1091  				map[v1.ResourceName]string{
  1092  					v1.ResourceMemory: "1G",
  1093  				},
  1094  			).Obj()); err != nil {
  1095  				t.Fatalf("failed to create node: %v", err)
  1096  			}
  1097  
  1098  			if err := createNamespacesWithLabels(cs, []string{"ns1", "ns2"}, map[string]string{"team": "team1"}); err != nil {
  1099  				t.Fatal(err)
  1100  			}
  1101  			if err := createNamespacesWithLabels(cs, []string{"ns3"}, map[string]string{"team": "team2"}); err != nil {
  1102  				t.Fatal(err)
  1103  			}
  1104  
  1105  			for _, pod := range test.pods {
  1106  				if pod.Namespace == "" {
  1107  					pod.Namespace = defaultNS
  1108  				}
  1109  				createdPod, err := cs.CoreV1().Pods(pod.Namespace).Create(ctx, pod, metav1.CreateOptions{})
  1110  				if err != nil {
  1111  					t.Fatalf("Error while creating pod: %v", err)
  1112  				}
  1113  				err = wait.PollUntilContextTimeout(ctx, pollInterval, wait.ForeverTestTimeout, false,
  1114  					testutils.PodScheduled(cs, createdPod.Namespace, createdPod.Name))
  1115  				if err != nil {
  1116  					t.Errorf("Error while creating pod: %v", err)
  1117  				}
  1118  			}
  1119  			if test.pod.Namespace == "" {
  1120  				test.pod.Namespace = defaultNS
  1121  			}
  1122  
  1123  			testPod, err := cs.CoreV1().Pods(test.pod.Namespace).Create(ctx, test.pod, metav1.CreateOptions{})
  1124  			if err != nil {
  1125  				if !(test.errorType == "invalidPod" && apierrors.IsInvalid(err)) {
  1126  					t.Fatalf("Error while creating pod: %v", err)
  1127  				}
  1128  			}
  1129  
  1130  			if test.fits {
  1131  				err = wait.PollUntilContextTimeout(ctx, pollInterval, wait.ForeverTestTimeout, false,
  1132  					testutils.PodScheduled(cs, testPod.Namespace, testPod.Name))
  1133  			} else {
  1134  				err = wait.PollUntilContextTimeout(ctx, pollInterval, wait.ForeverTestTimeout, false,
  1135  					podUnschedulable(cs, testPod.Namespace, testPod.Name))
  1136  			}
  1137  			if err != nil {
  1138  				t.Errorf("Error while trying to fit a pod: %v", err)
  1139  				return
  1140  			}
  1141  
  1142  			err = cs.CoreV1().Pods(test.pod.Namespace).Delete(ctx, test.pod.Name, *metav1.NewDeleteOptions(0))
  1143  			if err != nil {
  1144  				t.Errorf("Error while deleting pod: %v", err)
  1145  			}
  1146  			err = wait.PollUntilContextTimeout(ctx, pollInterval, wait.ForeverTestTimeout, true,
  1147  				testutils.PodDeleted(ctx, cs, testCtx.NS.Name, test.pod.Name))
  1148  			if err != nil {
  1149  				t.Errorf("Error while waiting for pod to get deleted: %v", err)
  1150  			}
  1151  			for _, pod := range test.pods {
  1152  				err = cs.CoreV1().Pods(pod.Namespace).Delete(ctx, pod.Name, *metav1.NewDeleteOptions(0))
  1153  				if err != nil {
  1154  					t.Errorf("Error while deleting pod: %v", err)
  1155  				}
  1156  				err = wait.PollUntilContextTimeout(ctx, pollInterval, wait.ForeverTestTimeout, true,
  1157  					testutils.PodDeleted(ctx, cs, pod.Namespace, pod.Name))
  1158  				if err != nil {
  1159  					t.Errorf("Error while waiting for pod to get deleted: %v", err)
  1160  				}
  1161  			}
  1162  		})
  1163  	}
  1164  }
  1165  
  1166  // TestInterPodAffinityWithNamespaceSelector verifies that inter pod affinity with NamespaceSelector works as expected.
  1167  func TestInterPodAffinityWithNamespaceSelector(t *testing.T) {
  1168  	podLabel := map[string]string{"service": "securityscan"}
  1169  	tests := []struct {
  1170  		name        string
  1171  		pod         *v1.Pod
  1172  		existingPod *v1.Pod
  1173  		fits        bool
  1174  		errorType   string
  1175  	}{
  1176  		{
  1177  			name: "MatchingNamespaces",
  1178  			pod: &v1.Pod{
  1179  				ObjectMeta: metav1.ObjectMeta{
  1180  					Name: "pod-ns-selector",
  1181  				},
  1182  				Spec: v1.PodSpec{
  1183  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
  1184  					Affinity: &v1.Affinity{
  1185  						PodAffinity: &v1.PodAffinity{
  1186  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
  1187  								{
  1188  									LabelSelector: &metav1.LabelSelector{
  1189  										MatchExpressions: []metav1.LabelSelectorRequirement{
  1190  											{
  1191  												Key:      "service",
  1192  												Operator: metav1.LabelSelectorOpIn,
  1193  												Values:   []string{"securityscan"},
  1194  											},
  1195  										},
  1196  									},
  1197  									NamespaceSelector: &metav1.LabelSelector{
  1198  										MatchExpressions: []metav1.LabelSelectorRequirement{
  1199  											{
  1200  												Key:      "team",
  1201  												Operator: metav1.LabelSelectorOpIn,
  1202  												Values:   []string{"team1"},
  1203  											},
  1204  										},
  1205  									},
  1206  									TopologyKey: "region",
  1207  								},
  1208  							},
  1209  						},
  1210  					},
  1211  				},
  1212  			},
  1213  			existingPod: &v1.Pod{
  1214  				ObjectMeta: metav1.ObjectMeta{
  1215  					Name:      "fakename2",
  1216  					Labels:    podLabel,
  1217  					Namespace: "ns2",
  1218  				},
  1219  				Spec: v1.PodSpec{
  1220  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
  1221  				},
  1222  			},
  1223  			fits: true,
  1224  		},
  1225  		{
  1226  			name: "MismatchingNamespaces",
  1227  			pod: &v1.Pod{
  1228  				ObjectMeta: metav1.ObjectMeta{
  1229  					Name: "pod-ns-selector",
  1230  				},
  1231  				Spec: v1.PodSpec{
  1232  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
  1233  					Affinity: &v1.Affinity{
  1234  						PodAffinity: &v1.PodAffinity{
  1235  							RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
  1236  								{
  1237  									LabelSelector: &metav1.LabelSelector{
  1238  										MatchExpressions: []metav1.LabelSelectorRequirement{
  1239  											{
  1240  												Key:      "service",
  1241  												Operator: metav1.LabelSelectorOpIn,
  1242  												Values:   []string{"securityscan"},
  1243  											},
  1244  										},
  1245  									},
  1246  									NamespaceSelector: &metav1.LabelSelector{
  1247  										MatchExpressions: []metav1.LabelSelectorRequirement{
  1248  											{
  1249  												Key:      "team",
  1250  												Operator: metav1.LabelSelectorOpIn,
  1251  												Values:   []string{"team1"},
  1252  											},
  1253  										},
  1254  									},
  1255  									TopologyKey: "region",
  1256  								},
  1257  							},
  1258  						},
  1259  					},
  1260  				},
  1261  			},
  1262  			existingPod: &v1.Pod{
  1263  				ObjectMeta: metav1.ObjectMeta{
  1264  					Name:      "fakename2",
  1265  					Labels:    podLabel,
  1266  					Namespace: "ns3",
  1267  				},
  1268  				Spec: v1.PodSpec{
  1269  					Containers: []v1.Container{{Name: "container", Image: imageutils.GetPauseImageName()}},
  1270  				},
  1271  			},
  1272  			fits: false,
  1273  		},
  1274  	}
  1275  
  1276  	for _, test := range tests {
  1277  		t.Run(test.name, func(t *testing.T) {
  1278  			testCtx := initTest(t, "")
  1279  
  1280  			// Add a few nodes with labels
  1281  			nodes, err := createAndWaitForNodesInCache(testCtx, "testnode", st.MakeNode().Label("region", "r1").Label("zone", "z11"), 2)
  1282  			if err != nil {
  1283  				t.Fatal(err)
  1284  			}
  1285  			test.existingPod.Spec.NodeName = nodes[0].Name
  1286  
  1287  			cs := testCtx.ClientSet
  1288  
  1289  			if err := createNamespacesWithLabels(cs, []string{"ns1", "ns2"}, map[string]string{"team": "team1"}); err != nil {
  1290  				t.Fatal(err)
  1291  			}
  1292  			if err := createNamespacesWithLabels(cs, []string{"ns3"}, map[string]string{"team": "team2"}); err != nil {
  1293  				t.Fatal(err)
  1294  			}
  1295  			defaultNS := "ns1"
  1296  
  1297  			createdPod, err := cs.CoreV1().Pods(test.existingPod.Namespace).Create(testCtx.Ctx, test.existingPod, metav1.CreateOptions{})
  1298  			if err != nil {
  1299  				t.Fatalf("Error while creating pod: %v", err)
  1300  			}
  1301  			err = wait.PollUntilContextTimeout(testCtx.Ctx, pollInterval, wait.ForeverTestTimeout, false,
  1302  				testutils.PodScheduled(cs, createdPod.Namespace, createdPod.Name))
  1303  			if err != nil {
  1304  				t.Errorf("Error while creating pod: %v", err)
  1305  			}
  1306  
  1307  			if test.pod.Namespace == "" {
  1308  				test.pod.Namespace = defaultNS
  1309  			}
  1310  
  1311  			testPod, err := cs.CoreV1().Pods(test.pod.Namespace).Create(testCtx.Ctx, test.pod, metav1.CreateOptions{})
  1312  			if err != nil {
  1313  				if !(test.errorType == "invalidPod" && apierrors.IsInvalid(err)) {
  1314  					t.Fatalf("Error while creating pod: %v", err)
  1315  				}
  1316  			}
  1317  
  1318  			if test.fits {
  1319  				err = wait.PollUntilContextTimeout(testCtx.Ctx, pollInterval, wait.ForeverTestTimeout, false,
  1320  					testutils.PodScheduled(cs, testPod.Namespace, testPod.Name))
  1321  			} else {
  1322  				err = wait.PollUntilContextTimeout(testCtx.Ctx, pollInterval, wait.ForeverTestTimeout, false,
  1323  					podUnschedulable(cs, testPod.Namespace, testPod.Name))
  1324  			}
  1325  			if err != nil {
  1326  				t.Errorf("Error while trying to fit a pod: %v", err)
  1327  			}
  1328  			err = cs.CoreV1().Pods(test.pod.Namespace).Delete(testCtx.Ctx, test.pod.Name, *metav1.NewDeleteOptions(0))
  1329  			if err != nil {
  1330  				t.Errorf("Error while deleting pod: %v", err)
  1331  			}
  1332  			err = wait.PollUntilContextTimeout(testCtx.Ctx, pollInterval, wait.ForeverTestTimeout, true,
  1333  				testutils.PodDeleted(testCtx.Ctx, cs, testCtx.NS.Name, test.pod.Name))
  1334  			if err != nil {
  1335  				t.Errorf("Error while waiting for pod to get deleted: %v", err)
  1336  			}
  1337  			err = cs.CoreV1().Pods(test.existingPod.Namespace).Delete(testCtx.Ctx, test.existingPod.Name, *metav1.NewDeleteOptions(0))
  1338  			if err != nil {
  1339  				t.Errorf("Error while deleting pod: %v", err)
  1340  			}
  1341  			err = wait.PollUntilContextTimeout(testCtx.Ctx, pollInterval, wait.ForeverTestTimeout, true,
  1342  				testutils.PodDeleted(testCtx.Ctx, cs, test.existingPod.Namespace, test.existingPod.Name))
  1343  			if err != nil {
  1344  				t.Errorf("Error while waiting for pod to get deleted: %v", err)
  1345  			}
  1346  		})
  1347  	}
  1348  }
  1349  
  1350  // TestPodTopologySpreadFilter verifies that EvenPodsSpread predicate functions well.
  1351  func TestPodTopologySpreadFilter(t *testing.T) {
  1352  	pause := imageutils.GetPauseImageName()
  1353  	//  default nodes with labels "zone: zone-{0,1}" and "node: <node name>".
  1354  	defaultNodes := []*v1.Node{
  1355  		st.MakeNode().Name("node-0").Label("node", "node-0").Label("zone", "zone-0").Obj(),
  1356  		st.MakeNode().Name("node-1").Label("node", "node-1").Label("zone", "zone-0").Obj(),
  1357  		st.MakeNode().Name("node-2").Label("node", "node-2").Label("zone", "zone-1").Obj(),
  1358  		st.MakeNode().Name("node-3").Label("node", "node-3").Label("zone", "zone-1").Obj(),
  1359  	}
  1360  
  1361  	tests := []struct {
  1362  		name                      string
  1363  		incomingPod               *v1.Pod
  1364  		existingPods              []*v1.Pod
  1365  		fits                      bool
  1366  		nodes                     []*v1.Node
  1367  		candidateNodes            []string // nodes expected to schedule onto
  1368  		enableNodeInclusionPolicy bool
  1369  		enableMatchLabelKeys      bool
  1370  	}{
  1371  		// note: naming starts at index 0
  1372  		{
  1373  			name: "place pod on a 1/1/0/1 cluster with MaxSkew=1, node-2 is the only fit",
  1374  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
  1375  				SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
  1376  				Obj(),
  1377  			existingPods: []*v1.Pod{
  1378  				st.MakePod().Name("p0").Node("node-0").Label("foo", "").Container(pause).Obj(),
  1379  				st.MakePod().Name("p1").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1380  				st.MakePod().Name("p3").Node("node-3").Label("foo", "").Container(pause).Obj(),
  1381  			},
  1382  			fits:           true,
  1383  			nodes:          defaultNodes,
  1384  			candidateNodes: []string{"node-2"},
  1385  		},
  1386  		{
  1387  			name: "place pod on a 2/0/0/1 cluster with MaxSkew=2, node-{1,2,3} are good fits",
  1388  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
  1389  				SpreadConstraint(2, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
  1390  				Obj(),
  1391  			existingPods: []*v1.Pod{
  1392  				st.MakePod().Name("p0a").Node("node-0").Label("foo", "").Container(pause).Obj(),
  1393  				st.MakePod().Name("p0b").Node("node-0").Label("foo", "").Container(pause).Obj(),
  1394  				st.MakePod().Name("p3").Node("node-3").Label("foo", "").Container(pause).Obj(),
  1395  			},
  1396  			fits:           true,
  1397  			nodes:          defaultNodes,
  1398  			candidateNodes: []string{"node-1", "node-2", "node-3"},
  1399  		},
  1400  		{
  1401  			name: "pod is required to be placed on zone0, so only node-1 fits",
  1402  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
  1403  				NodeAffinityIn("zone", []string{"zone-0"}).
  1404  				SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
  1405  				Obj(),
  1406  			existingPods: []*v1.Pod{
  1407  				st.MakePod().Name("p0").Node("node-0").Label("foo", "").Container(pause).Obj(),
  1408  				st.MakePod().Name("p3").Node("node-3").Label("foo", "").Container(pause).Obj(),
  1409  			},
  1410  			fits:           true,
  1411  			nodes:          defaultNodes,
  1412  			candidateNodes: []string{"node-1"},
  1413  		},
  1414  		{
  1415  			name: "two constraints: pod can only be placed to zone-1/node-2",
  1416  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
  1417  				SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
  1418  				SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
  1419  				Obj(),
  1420  			existingPods: []*v1.Pod{
  1421  				st.MakePod().Name("p0").Node("node-0").Label("foo", "").Container(pause).Obj(),
  1422  				st.MakePod().Name("p1").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1423  				st.MakePod().Name("p3a").Node("node-3").Label("foo", "").Container(pause).Obj(),
  1424  				st.MakePod().Name("p3b").Node("node-3").Label("foo", "").Container(pause).Obj(),
  1425  			},
  1426  			fits:           true,
  1427  			nodes:          defaultNodes,
  1428  			candidateNodes: []string{"node-2"},
  1429  		},
  1430  		{
  1431  			name: "pod cannot be placed onto any node",
  1432  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
  1433  				NodeAffinityNotIn("node", []string{"node-0"}). // mock a 3-node cluster
  1434  				SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
  1435  				SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
  1436  				Obj(),
  1437  			existingPods: []*v1.Pod{
  1438  				st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1439  				st.MakePod().Name("p1b").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1440  				st.MakePod().Name("p2a").Node("node-2").Label("foo", "").Container(pause).Obj(),
  1441  				st.MakePod().Name("p2b").Node("node-2").Label("foo", "").Container(pause).Obj(),
  1442  				st.MakePod().Name("p3").Node("node-3").Label("foo", "").Container(pause).Obj(),
  1443  			},
  1444  			fits:  false,
  1445  			nodes: defaultNodes,
  1446  		},
  1447  		{
  1448  			name: "high priority pod can preempt others",
  1449  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).Priority(100).
  1450  				NodeAffinityNotIn("node", []string{"node-0"}). // mock a 3-node cluster
  1451  				SpreadConstraint(1, "zone", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
  1452  				SpreadConstraint(1, "node", hardSpread, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
  1453  				Obj(),
  1454  			existingPods: []*v1.Pod{
  1455  				st.MakePod().ZeroTerminationGracePeriod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1456  				st.MakePod().ZeroTerminationGracePeriod().Name("p1b").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1457  				st.MakePod().ZeroTerminationGracePeriod().Name("p2a").Node("node-2").Label("foo", "").Container(pause).Obj(),
  1458  				st.MakePod().ZeroTerminationGracePeriod().Name("p2b").Node("node-2").Label("foo", "").Container(pause).Obj(),
  1459  				st.MakePod().ZeroTerminationGracePeriod().Name("p3").Node("node-3").Label("foo", "").Container(pause).Obj(),
  1460  			},
  1461  			fits:           true,
  1462  			nodes:          defaultNodes,
  1463  			candidateNodes: []string{"node-1", "node-2", "node-3"},
  1464  		},
  1465  		{
  1466  			name: "pods spread across nodes as 2/2/1, maxSkew is 2, and the number of domains < minDomains, then the third node fits",
  1467  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
  1468  				NodeAffinityNotIn("node", []string{"node-0"}). // mock a 3-node cluster
  1469  				SpreadConstraint(
  1470  					2,
  1471  					"node",
  1472  					hardSpread,
  1473  					st.MakeLabelSelector().Exists("foo").Obj(),
  1474  					pointer.Int32(4), // larger than the number of domains (= 3)
  1475  					nil,
  1476  					nil,
  1477  					nil,
  1478  				).
  1479  				Obj(),
  1480  			existingPods: []*v1.Pod{
  1481  				st.MakePod().ZeroTerminationGracePeriod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1482  				st.MakePod().ZeroTerminationGracePeriod().Name("p1b").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1483  				st.MakePod().ZeroTerminationGracePeriod().Name("p2a").Node("node-2").Label("foo", "").Container(pause).Obj(),
  1484  				st.MakePod().ZeroTerminationGracePeriod().Name("p2b").Node("node-2").Label("foo", "").Container(pause).Obj(),
  1485  				st.MakePod().ZeroTerminationGracePeriod().Name("p3").Node("node-3").Label("foo", "").Container(pause).Obj(),
  1486  			},
  1487  			fits:           true,
  1488  			nodes:          defaultNodes,
  1489  			candidateNodes: []string{"node-3"},
  1490  		},
  1491  		{
  1492  			name: "pods spread across nodes as 2/2/1, maxSkew is 2, and the number of domains > minDomains, then the all nodes fit",
  1493  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
  1494  				NodeAffinityNotIn("node", []string{"node-0"}). // mock a 3-node cluster
  1495  				SpreadConstraint(
  1496  					2,
  1497  					"node",
  1498  					hardSpread,
  1499  					st.MakeLabelSelector().Exists("foo").Obj(),
  1500  					pointer.Int32(2), // smaller than the number of domains (= 3)
  1501  					nil,
  1502  					nil,
  1503  					nil,
  1504  				).
  1505  				Obj(),
  1506  			existingPods: []*v1.Pod{
  1507  				st.MakePod().ZeroTerminationGracePeriod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1508  				st.MakePod().ZeroTerminationGracePeriod().Name("p1b").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1509  				st.MakePod().ZeroTerminationGracePeriod().Name("p2a").Node("node-2").Label("foo", "").Container(pause).Obj(),
  1510  				st.MakePod().ZeroTerminationGracePeriod().Name("p2b").Node("node-2").Label("foo", "").Container(pause).Obj(),
  1511  				st.MakePod().ZeroTerminationGracePeriod().Name("p3").Node("node-3").Label("foo", "").Container(pause).Obj(),
  1512  			},
  1513  			fits:           true,
  1514  			nodes:          defaultNodes,
  1515  			candidateNodes: []string{"node-1", "node-2", "node-3"},
  1516  		},
  1517  		{
  1518  			name: "pods spread across zone as 2/1, maxSkew is 2 and the number of domains < minDomains, then the third and fourth nodes fit",
  1519  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
  1520  				SpreadConstraint(
  1521  					2,
  1522  					"zone",
  1523  					v1.DoNotSchedule,
  1524  					st.MakeLabelSelector().Exists("foo").Obj(),
  1525  					pointer.Int32(3), // larger than the number of domains(2)
  1526  					nil,
  1527  					nil,
  1528  					nil,
  1529  				).Obj(),
  1530  			existingPods: []*v1.Pod{
  1531  				st.MakePod().Name("p1a").Node("node-0").Label("foo", "").Container(pause).Obj(),
  1532  				st.MakePod().Name("p2a").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1533  				st.MakePod().Name("p3a").Node("node-2").Label("foo", "").Container(pause).Obj(),
  1534  			},
  1535  			fits:           true,
  1536  			nodes:          defaultNodes,
  1537  			candidateNodes: []string{"node-2", "node-3"},
  1538  		},
  1539  		{
  1540  			name: "pods spread across zone as 2/1, maxSkew is 2 and the number of domains < minDomains, then the all nodes fit",
  1541  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
  1542  				SpreadConstraint(
  1543  					2,
  1544  					"zone",
  1545  					v1.DoNotSchedule,
  1546  					st.MakeLabelSelector().Exists("foo").Obj(),
  1547  					pointer.Int32(1), // smaller than the number of domains(2)
  1548  					nil,
  1549  					nil,
  1550  					nil,
  1551  				).Obj(),
  1552  			existingPods: []*v1.Pod{
  1553  				st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1554  				st.MakePod().Name("p2a").Node("node-2").Label("foo", "").Container(pause).Obj(),
  1555  				st.MakePod().Name("p3a").Node("node-3").Label("foo", "").Container(pause).Obj(),
  1556  			},
  1557  			fits:           true,
  1558  			nodes:          defaultNodes,
  1559  			candidateNodes: []string{"node-0", "node-1", "node-2", "node-3"},
  1560  		},
  1561  		{
  1562  			name: "NodeAffinityPolicy honored with labelSelectors, pods spread across zone as 2/1",
  1563  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
  1564  				NodeSelector(map[string]string{"foo": ""}).
  1565  				SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
  1566  				Obj(),
  1567  			existingPods: []*v1.Pod{
  1568  				st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1569  				st.MakePod().Name("p2a").Node("node-2").Label("foo", "").Container(pause).Obj(),
  1570  				st.MakePod().Name("p3a").Node("node-3").Label("foo", "").Container(pause).Obj(),
  1571  				st.MakePod().Name("p4a").Node("node-4").Label("foo", "").Container(pause).Obj(),
  1572  			},
  1573  			fits: true,
  1574  			nodes: []*v1.Node{
  1575  				st.MakeNode().Name("node-1").Label("node", "node-1").Label("zone", "zone-1").Label("foo", "").Obj(),
  1576  				st.MakeNode().Name("node-2").Label("node", "node-2").Label("zone", "zone-1").Label("foo", "").Obj(),
  1577  				st.MakeNode().Name("node-3").Label("node", "node-3").Label("zone", "zone-2").Obj(),
  1578  				st.MakeNode().Name("node-4").Label("node", "node-4").Label("zone", "zone-2").Label("foo", "").Obj(),
  1579  			},
  1580  			candidateNodes:            []string{"node-4"}, // node-3 is filtered out by NodeAffinity plugin
  1581  			enableNodeInclusionPolicy: true,
  1582  		},
  1583  		{
  1584  			name: "NodeAffinityPolicy ignored with nodeAffinity, pods spread across zone as 1/~2~",
  1585  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
  1586  				NodeAffinityIn("foo", []string{""}).
  1587  				SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, &ignorePolicy, nil, nil).
  1588  				Obj(),
  1589  			existingPods: []*v1.Pod{
  1590  				st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1591  				st.MakePod().Name("p3a").Node("node-3").Label("foo", "").Container(pause).Obj(),
  1592  				st.MakePod().Name("p4a").Node("node-4").Label("foo", "").Container(pause).Obj(),
  1593  			},
  1594  			fits: true,
  1595  			nodes: []*v1.Node{
  1596  				st.MakeNode().Name("node-1").Label("node", "node-1").Label("zone", "zone-1").Label("foo", "").Obj(),
  1597  				st.MakeNode().Name("node-2").Label("node", "node-2").Label("zone", "zone-1").Label("foo", "").Obj(),
  1598  				st.MakeNode().Name("node-3").Label("node", "node-3").Label("zone", "zone-2").Obj(),
  1599  				st.MakeNode().Name("node-4").Label("node", "node-4").Label("zone", "zone-2").Label("foo", "").Obj(),
  1600  			},
  1601  			candidateNodes:            []string{"node-1", "node-2"},
  1602  			enableNodeInclusionPolicy: true,
  1603  		},
  1604  		{
  1605  			name: "NodeTaintsPolicy honored, pods spread across zone as 2/1",
  1606  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
  1607  				SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, &honorPolicy, nil).
  1608  				Obj(),
  1609  			existingPods: []*v1.Pod{
  1610  				st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1611  				st.MakePod().Name("p2a").Node("node-2").Label("foo", "").Container(pause).Obj(),
  1612  				st.MakePod().Name("p3a").Node("node-3").Label("foo", "").Container(pause).Obj(),
  1613  				st.MakePod().Name("p4a").Node("node-4").Label("foo", "").Container(pause).Obj(),
  1614  			},
  1615  			fits: true,
  1616  			nodes: []*v1.Node{
  1617  				st.MakeNode().Name("node-1").Label("node", "node-1").Label("zone", "zone-1").Label("foo", "").Obj(),
  1618  				st.MakeNode().Name("node-2").Label("node", "node-2").Label("zone", "zone-1").Label("foo", "").Obj(),
  1619  				st.MakeNode().Name("node-3").Label("node", "node-3").Label("zone", "zone-2").Taints(taints).Obj(),
  1620  				st.MakeNode().Name("node-4").Label("node", "node-4").Label("zone", "zone-2").Label("foo", "").Obj(),
  1621  			},
  1622  			candidateNodes:            []string{"node-4"}, // node-3 is filtered out by TaintToleration plugin
  1623  			enableNodeInclusionPolicy: true,
  1624  		},
  1625  		{
  1626  			name: "NodeTaintsPolicy ignored, pods spread across zone as 2/2",
  1627  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
  1628  				SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
  1629  				Obj(),
  1630  			existingPods: []*v1.Pod{
  1631  				st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1632  				st.MakePod().Name("p2a").Node("node-2").Label("foo", "").Container(pause).Obj(),
  1633  				st.MakePod().Name("p3a").Node("node-3").Label("foo", "").Container(pause).Obj(),
  1634  				st.MakePod().Name("p4a").Node("node-4").Label("foo", "").Container(pause).Obj(),
  1635  			},
  1636  			fits: true,
  1637  			nodes: []*v1.Node{
  1638  				st.MakeNode().Name("node-1").Label("node", "node-1").Label("zone", "zone-1").Label("foo", "").Obj(),
  1639  				st.MakeNode().Name("node-2").Label("node", "node-2").Label("zone", "zone-1").Label("foo", "").Obj(),
  1640  				st.MakeNode().Name("node-3").Label("node", "node-3").Label("zone", "zone-2").Taints(taints).Obj(),
  1641  				st.MakeNode().Name("node-4").Label("node", "node-4").Label("zone", "zone-2").Label("foo", "").Obj(),
  1642  			},
  1643  			candidateNodes:            []string{"node-1", "node-2", "node-4"}, // node-3 is filtered out by TaintToleration plugin
  1644  			enableNodeInclusionPolicy: true,
  1645  		},
  1646  		{
  1647  			// 1. to fulfil "zone" constraint, pods spread across zones as 2/1
  1648  			// 2. to fulfil "node" constraint, pods spread across zones as 1/1/~0~/1
  1649  			// intersection of (1) and (2) returns node-4 as node-3 is filtered out by NodeAffinity plugin.
  1650  			name: "two node inclusion Constraints, zone: honor/ignore, node: honor/ignore",
  1651  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
  1652  				NodeSelector(map[string]string{"foo": ""}).
  1653  				SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
  1654  				SpreadConstraint(1, "node", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
  1655  				Obj(),
  1656  			existingPods: []*v1.Pod{
  1657  				st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1658  				st.MakePod().Name("p2a").Node("node-2").Label("foo", "").Container(pause).Obj(),
  1659  				st.MakePod().Name("p3a").Node("node-3").Label("foo", "").Container(pause).Obj(),
  1660  				st.MakePod().Name("p4a").Node("node-4").Label("foo", "").Container(pause).Obj(),
  1661  			},
  1662  			fits: true,
  1663  			nodes: []*v1.Node{
  1664  				st.MakeNode().Name("node-1").Label("node", "node-1").Label("zone", "zone-1").Label("foo", "").Obj(),
  1665  				st.MakeNode().Name("node-2").Label("node", "node-2").Label("zone", "zone-1").Label("foo", "").Taints(taints).Obj(),
  1666  				st.MakeNode().Name("node-3").Label("node", "node-3").Label("zone", "zone-2").Obj(),
  1667  				st.MakeNode().Name("node-4").Label("node", "node-4").Label("zone", "zone-2").Label("foo", "").Obj(),
  1668  			},
  1669  			candidateNodes:            []string{"node-4"},
  1670  			enableNodeInclusionPolicy: true,
  1671  		},
  1672  		{
  1673  			// 1. to fulfil "zone" constraint, pods spread across zones as 2/1
  1674  			// 2. to fulfil "node" constraint, pods spread across zones as 1/1/~0~/1
  1675  			// intersection of (1) and (2) returns node-4 as node-3 is filtered out by NodeAffinity plugin
  1676  			name: "feature gate disabled, two node inclusion Constraints, zone: honor/ignore, node: honor/ignore",
  1677  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
  1678  				NodeSelector(map[string]string{"foo": ""}).
  1679  				SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
  1680  				SpreadConstraint(1, "node", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, nil).
  1681  				Obj(),
  1682  			existingPods: []*v1.Pod{
  1683  				st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1684  				st.MakePod().Name("p2a").Node("node-2").Label("foo", "").Container(pause).Obj(),
  1685  				st.MakePod().Name("p3a").Node("node-3").Label("foo", "").Container(pause).Obj(),
  1686  				st.MakePod().Name("p4a").Node("node-4").Label("foo", "").Container(pause).Obj(),
  1687  			},
  1688  			fits: true,
  1689  			nodes: []*v1.Node{
  1690  				st.MakeNode().Name("node-1").Label("node", "node-1").Label("zone", "zone-1").Label("foo", "").Obj(),
  1691  				st.MakeNode().Name("node-2").Label("node", "node-2").Label("zone", "zone-1").Label("foo", "").Taints(taints).Obj(),
  1692  				st.MakeNode().Name("node-3").Label("node", "node-3").Label("zone", "zone-2").Obj(),
  1693  				st.MakeNode().Name("node-4").Label("node", "node-4").Label("zone", "zone-2").Label("foo", "").Obj(),
  1694  			},
  1695  			candidateNodes:            []string{"node-4"},
  1696  			enableNodeInclusionPolicy: false,
  1697  		},
  1698  		{
  1699  			// 1. to fulfil "zone" constraint, pods spread across zones as 2/2
  1700  			// 2. to fulfil "node" constraint, pods spread across zones as 1/~0~/~0~/1
  1701  			// intersection of (1) and (2) returns node-1 and node-4 as node-2, node-3 are filtered out by plugins
  1702  			name: "two node inclusion Constraints, zone: ignore/ignore, node: honor/honor",
  1703  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
  1704  				NodeSelector(map[string]string{"foo": ""}).
  1705  				SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, &ignorePolicy, nil, nil).
  1706  				SpreadConstraint(1, "node", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, &honorPolicy, nil).
  1707  				Obj(),
  1708  			existingPods: []*v1.Pod{
  1709  				st.MakePod().Name("p1a").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1710  				st.MakePod().Name("p2a").Node("node-2").Label("foo", "").Container(pause).Obj(),
  1711  				st.MakePod().Name("p3a").Node("node-3").Label("foo", "").Container(pause).Obj(),
  1712  				st.MakePod().Name("p4a").Node("node-4").Label("foo", "").Container(pause).Obj(),
  1713  			},
  1714  			fits: true,
  1715  			nodes: []*v1.Node{
  1716  				st.MakeNode().Name("node-1").Label("node", "node-1").Label("zone", "zone-1").Label("foo", "").Obj(),
  1717  				st.MakeNode().Name("node-2").Label("node", "node-2").Label("zone", "zone-1").Label("foo", "").Taints(taints).Obj(),
  1718  				st.MakeNode().Name("node-3").Label("node", "node-3").Label("zone", "zone-2").Obj(),
  1719  				st.MakeNode().Name("node-4").Label("node", "node-4").Label("zone", "zone-2").Label("foo", "").Obj(),
  1720  			},
  1721  			candidateNodes:            []string{"node-1", "node-4"},
  1722  			enableNodeInclusionPolicy: true,
  1723  		},
  1724  		{
  1725  			name: "matchLabelKeys ignored when feature gate disabled, pods spread across zone as 2/1",
  1726  			incomingPod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").Container(pause).
  1727  				SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, []string{"bar"}).
  1728  				Obj(),
  1729  			existingPods: []*v1.Pod{
  1730  				st.MakePod().Name("p1a").Node("node-0").Label("foo", "").Container(pause).Obj(),
  1731  				st.MakePod().Name("p2a").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1732  				st.MakePod().Name("p3a").Node("node-2").Label("foo", "").Label("bar", "").Container(pause).Obj(),
  1733  			},
  1734  			fits:                 true,
  1735  			nodes:                defaultNodes,
  1736  			candidateNodes:       []string{"node-2", "node-3"},
  1737  			enableMatchLabelKeys: false,
  1738  		},
  1739  		{
  1740  			name: "matchLabelKeys ANDed with LabelSelector when LabelSelector isn't empty, pods spread across zone as 0/1",
  1741  			incomingPod: st.MakePod().Name("p").Label("foo", "").Label("bar", "").Container(pause).
  1742  				SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Exists("foo").Obj(), nil, nil, nil, []string{"bar"}).
  1743  				Obj(),
  1744  			existingPods: []*v1.Pod{
  1745  				st.MakePod().Name("p1a").Node("node-0").Label("foo", "").Container(pause).Obj(),
  1746  				st.MakePod().Name("p2a").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1747  				st.MakePod().Name("p3a").Node("node-2").Label("foo", "").Label("bar", "").Container(pause).Obj(),
  1748  			},
  1749  			fits:                 true,
  1750  			nodes:                defaultNodes,
  1751  			candidateNodes:       []string{"node-0", "node-1"},
  1752  			enableMatchLabelKeys: true,
  1753  		},
  1754  		{
  1755  			name: "matchLabelKeys ANDed with LabelSelector when LabelSelector is empty, pods spread across zone as 2/1",
  1756  			incomingPod: st.MakePod().Name("p").Label("foo", "").Container(pause).
  1757  				SpreadConstraint(1, "zone", v1.DoNotSchedule, st.MakeLabelSelector().Obj(), nil, nil, nil, []string{"foo"}).
  1758  				Obj(),
  1759  			existingPods: []*v1.Pod{
  1760  				st.MakePod().Name("p1a").Node("node-0").Label("foo", "").Container(pause).Obj(),
  1761  				st.MakePod().Name("p2a").Node("node-1").Label("foo", "").Container(pause).Obj(),
  1762  				st.MakePod().Name("p3a").Node("node-2").Label("foo", "").Container(pause).Obj(),
  1763  			},
  1764  			fits:                 true,
  1765  			nodes:                defaultNodes,
  1766  			candidateNodes:       []string{"node-2", "node-3"},
  1767  			enableMatchLabelKeys: true,
  1768  		},
  1769  	}
  1770  	for _, tt := range tests {
  1771  		t.Run(tt.name, func(t *testing.T) {
  1772  			featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeInclusionPolicyInPodTopologySpread, tt.enableNodeInclusionPolicy)
  1773  			featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MatchLabelKeysInPodTopologySpread, tt.enableMatchLabelKeys)
  1774  
  1775  			testCtx := initTest(t, "pts-predicate")
  1776  			cs := testCtx.ClientSet
  1777  			ns := testCtx.NS.Name
  1778  
  1779  			for i := range tt.nodes {
  1780  				if _, err := createNode(cs, tt.nodes[i]); err != nil {
  1781  					t.Fatalf("Cannot create node: %v", err)
  1782  				}
  1783  			}
  1784  
  1785  			// set namespace to pods
  1786  			for i := range tt.existingPods {
  1787  				tt.existingPods[i].SetNamespace(ns)
  1788  			}
  1789  			tt.incomingPod.SetNamespace(ns)
  1790  
  1791  			allPods := append(tt.existingPods, tt.incomingPod)
  1792  			defer testutils.CleanupPods(testCtx.Ctx, cs, t, allPods)
  1793  
  1794  			for _, pod := range tt.existingPods {
  1795  				createdPod, err := cs.CoreV1().Pods(pod.Namespace).Create(testCtx.Ctx, pod, metav1.CreateOptions{})
  1796  				if err != nil {
  1797  					t.Fatalf("Error while creating pod during test: %v", err)
  1798  				}
  1799  				err = wait.PollUntilContextTimeout(testCtx.Ctx, pollInterval, wait.ForeverTestTimeout, false,
  1800  					testutils.PodScheduled(cs, createdPod.Namespace, createdPod.Name))
  1801  				if err != nil {
  1802  					t.Errorf("Error while waiting for pod during test: %v", err)
  1803  				}
  1804  			}
  1805  			testPod, err := cs.CoreV1().Pods(tt.incomingPod.Namespace).Create(testCtx.Ctx, tt.incomingPod, metav1.CreateOptions{})
  1806  			if err != nil {
  1807  				t.Fatalf("Error while creating pod during test: %v", err)
  1808  			}
  1809  
  1810  			if tt.fits {
  1811  				err = wait.PollUntilContextTimeout(testCtx.Ctx, pollInterval, wait.ForeverTestTimeout, false,
  1812  					podScheduledIn(cs, testPod.Namespace, testPod.Name, tt.candidateNodes))
  1813  			} else {
  1814  				err = wait.PollUntilContextTimeout(testCtx.Ctx, pollInterval, wait.ForeverTestTimeout, false,
  1815  					podUnschedulable(cs, testPod.Namespace, testPod.Name))
  1816  			}
  1817  			if err != nil {
  1818  				t.Errorf("Test Failed: %v", err)
  1819  			}
  1820  		})
  1821  	}
  1822  }
  1823  
  1824  var (
  1825  	hardSpread = v1.DoNotSchedule
  1826  )
  1827  
  1828  func TestUnschedulablePodBecomesSchedulable(t *testing.T) {
  1829  	tests := []struct {
  1830  		name   string
  1831  		init   func(kubernetes.Interface, string) error
  1832  		pod    *testutils.PausePodConfig
  1833  		update func(kubernetes.Interface, string) error
  1834  	}{
  1835  		{
  1836  			name: "node gets added",
  1837  			pod: &testutils.PausePodConfig{
  1838  				Name: "pod-1",
  1839  			},
  1840  			update: func(cs kubernetes.Interface, _ string) error {
  1841  				_, err := createNode(cs, st.MakeNode().Name("node-added").Obj())
  1842  				if err != nil {
  1843  					return fmt.Errorf("cannot create node: %v", err)
  1844  				}
  1845  				return nil
  1846  			},
  1847  		},
  1848  		{
  1849  			name: "node gets taint removed",
  1850  			init: func(cs kubernetes.Interface, _ string) error {
  1851  				node, err := createNode(cs, st.MakeNode().Name("node-tainted").Obj())
  1852  				if err != nil {
  1853  					return fmt.Errorf("cannot create node: %v", err)
  1854  				}
  1855  				taint := v1.Taint{Key: "test", Value: "test", Effect: v1.TaintEffectNoSchedule}
  1856  				if err := testutils.AddTaintToNode(cs, node.Name, taint); err != nil {
  1857  					return fmt.Errorf("cannot add taint to node: %v", err)
  1858  				}
  1859  				return nil
  1860  			},
  1861  			pod: &testutils.PausePodConfig{
  1862  				Name: "pod-1",
  1863  			},
  1864  			update: func(cs kubernetes.Interface, _ string) error {
  1865  				taint := v1.Taint{Key: "test", Value: "test", Effect: v1.TaintEffectNoSchedule}
  1866  				if err := testutils.RemoveTaintOffNode(cs, "node-tainted", taint); err != nil {
  1867  					return fmt.Errorf("cannot remove taint off node: %v", err)
  1868  				}
  1869  				return nil
  1870  			},
  1871  		},
  1872  		{
  1873  			name: "other pod gets deleted",
  1874  			init: func(cs kubernetes.Interface, ns string) error {
  1875  				nodeObject := st.MakeNode().Name("node-scheduler-integration-test").Capacity(map[v1.ResourceName]string{v1.ResourcePods: "1"}).Obj()
  1876  				_, err := createNode(cs, nodeObject)
  1877  				if err != nil {
  1878  					return fmt.Errorf("cannot create node: %v", err)
  1879  				}
  1880  				_, err = createPausePod(cs, initPausePod(&testutils.PausePodConfig{Name: "pod-to-be-deleted", Namespace: ns}))
  1881  				if err != nil {
  1882  					return fmt.Errorf("cannot create pod: %v", err)
  1883  				}
  1884  				return nil
  1885  			},
  1886  			pod: &testutils.PausePodConfig{
  1887  				Name: "pod-1",
  1888  			},
  1889  			update: func(cs kubernetes.Interface, ns string) error {
  1890  				if err := deletePod(cs, "pod-to-be-deleted", ns); err != nil {
  1891  					return fmt.Errorf("cannot delete pod: %v", err)
  1892  				}
  1893  				return nil
  1894  			},
  1895  		},
  1896  		{
  1897  			name: "pod with pod-affinity gets added",
  1898  			init: func(cs kubernetes.Interface, _ string) error {
  1899  				_, err := createNode(cs, st.MakeNode().Name("node-1").Label("region", "test").Obj())
  1900  				if err != nil {
  1901  					return fmt.Errorf("cannot create node: %v", err)
  1902  				}
  1903  				return nil
  1904  			},
  1905  			pod: &testutils.PausePodConfig{
  1906  				Name: "pod-1",
  1907  				Affinity: &v1.Affinity{
  1908  					PodAffinity: &v1.PodAffinity{
  1909  						RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
  1910  							{
  1911  								LabelSelector: &metav1.LabelSelector{
  1912  									MatchLabels: map[string]string{
  1913  										"pod-with-affinity": "true",
  1914  									},
  1915  								},
  1916  								TopologyKey: "region",
  1917  							},
  1918  						},
  1919  					},
  1920  				},
  1921  			},
  1922  			update: func(cs kubernetes.Interface, ns string) error {
  1923  				podConfig := &testutils.PausePodConfig{
  1924  					Name:      "pod-with-affinity",
  1925  					Namespace: ns,
  1926  					Labels: map[string]string{
  1927  						"pod-with-affinity": "true",
  1928  					},
  1929  				}
  1930  				if _, err := createPausePod(cs, initPausePod(podConfig)); err != nil {
  1931  					return fmt.Errorf("cannot create pod: %v", err)
  1932  				}
  1933  				return nil
  1934  			},
  1935  		},
  1936  		{
  1937  			name: "scheduled pod gets updated to match affinity",
  1938  			init: func(cs kubernetes.Interface, ns string) error {
  1939  				_, err := createNode(cs, st.MakeNode().Name("node-1").Label("region", "test").Obj())
  1940  				if err != nil {
  1941  					return fmt.Errorf("cannot create node: %v", err)
  1942  				}
  1943  				if _, err := createPausePod(cs, initPausePod(&testutils.PausePodConfig{Name: "pod-to-be-updated", Namespace: ns})); err != nil {
  1944  					return fmt.Errorf("cannot create pod: %v", err)
  1945  				}
  1946  				return nil
  1947  			},
  1948  			pod: &testutils.PausePodConfig{
  1949  				Name: "pod-1",
  1950  				Affinity: &v1.Affinity{
  1951  					PodAffinity: &v1.PodAffinity{
  1952  						RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
  1953  							{
  1954  								LabelSelector: &metav1.LabelSelector{
  1955  									MatchLabels: map[string]string{
  1956  										"pod-with-affinity": "true",
  1957  									},
  1958  								},
  1959  								TopologyKey: "region",
  1960  							},
  1961  						},
  1962  					},
  1963  				},
  1964  			},
  1965  			update: func(cs kubernetes.Interface, ns string) error {
  1966  				pod, err := getPod(cs, "pod-to-be-updated", ns)
  1967  				if err != nil {
  1968  					return fmt.Errorf("cannot get pod: %v", err)
  1969  				}
  1970  				pod.Labels = map[string]string{"pod-with-affinity": "true"}
  1971  				if _, err := cs.CoreV1().Pods(pod.Namespace).Update(context.TODO(), pod, metav1.UpdateOptions{}); err != nil {
  1972  					return fmt.Errorf("cannot update pod: %v", err)
  1973  				}
  1974  				return nil
  1975  			},
  1976  		},
  1977  		{
  1978  			name: "scheduled pod uses read-write-once-pod pvc",
  1979  			init: func(cs kubernetes.Interface, ns string) error {
  1980  				_, err := createNode(cs, st.MakeNode().Name("node").Obj())
  1981  				if err != nil {
  1982  					return fmt.Errorf("cannot create node: %v", err)
  1983  				}
  1984  
  1985  				storage := v1.VolumeResourceRequirements{Requests: v1.ResourceList{v1.ResourceStorage: resource.MustParse("1Mi")}}
  1986  				volType := v1.HostPathDirectoryOrCreate
  1987  				pv, err := testutils.CreatePV(cs, st.MakePersistentVolume().
  1988  					Name("pv-with-read-write-once-pod").
  1989  					AccessModes([]v1.PersistentVolumeAccessMode{v1.ReadWriteOncePod}).
  1990  					Capacity(storage.Requests).
  1991  					HostPathVolumeSource(&v1.HostPathVolumeSource{Path: "/mnt", Type: &volType}).
  1992  					Obj())
  1993  				if err != nil {
  1994  					return fmt.Errorf("cannot create pv: %v", err)
  1995  				}
  1996  				pvc, err := testutils.CreatePVC(cs, st.MakePersistentVolumeClaim().
  1997  					Name("pvc-with-read-write-once-pod").
  1998  					Namespace(ns).
  1999  					// Annotation and volume name required for PVC to be considered bound.
  2000  					Annotation(volume.AnnBindCompleted, "true").
  2001  					VolumeName(pv.Name).
  2002  					AccessModes([]v1.PersistentVolumeAccessMode{v1.ReadWriteOncePod}).
  2003  					Resources(storage).
  2004  					Obj())
  2005  				if err != nil {
  2006  					return fmt.Errorf("cannot create pvc: %v", err)
  2007  				}
  2008  
  2009  				pod := initPausePod(&testutils.PausePodConfig{
  2010  					Name:      "pod-to-be-deleted",
  2011  					Namespace: ns,
  2012  					Volumes: []v1.Volume{{
  2013  						Name: "volume",
  2014  						VolumeSource: v1.VolumeSource{
  2015  							PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
  2016  								ClaimName: pvc.Name,
  2017  							},
  2018  						},
  2019  					}},
  2020  				})
  2021  				if _, err := createPausePod(cs, pod); err != nil {
  2022  					return fmt.Errorf("cannot create pod: %v", err)
  2023  				}
  2024  				return nil
  2025  			},
  2026  			pod: &testutils.PausePodConfig{
  2027  				Name: "pod-to-take-over-pvc",
  2028  				Volumes: []v1.Volume{{
  2029  					Name: "volume",
  2030  					VolumeSource: v1.VolumeSource{
  2031  						PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
  2032  							ClaimName: "pvc-with-read-write-once-pod",
  2033  						},
  2034  					},
  2035  				}},
  2036  			},
  2037  			update: func(cs kubernetes.Interface, ns string) error {
  2038  				return deletePod(cs, "pod-to-be-deleted", ns)
  2039  			},
  2040  		},
  2041  		{
  2042  			name: "pod with pvc has node-affinity to non-existent/illegal nodes",
  2043  			init: func(cs kubernetes.Interface, ns string) error {
  2044  				storage := v1.VolumeResourceRequirements{Requests: v1.ResourceList{v1.ResourceStorage: resource.MustParse("1Mi")}}
  2045  				volType := v1.HostPathDirectoryOrCreate
  2046  				pv, err := testutils.CreatePV(cs, st.MakePersistentVolume().
  2047  					Name("pv-has-non-existent-nodes").
  2048  					AccessModes([]v1.PersistentVolumeAccessMode{v1.ReadWriteOncePod}).
  2049  					Capacity(storage.Requests).
  2050  					HostPathVolumeSource(&v1.HostPathVolumeSource{Path: "/tmp", Type: &volType}).
  2051  					NodeAffinityIn("kubernetes.io/hostname", []string{"node-available", "non-existing"}). // one node exist, one doesn't
  2052  					Obj())
  2053  				if err != nil {
  2054  					return fmt.Errorf("cannot create pv: %w", err)
  2055  				}
  2056  				_, err = testutils.CreatePVC(cs, st.MakePersistentVolumeClaim().
  2057  					Name("pvc-has-non-existent-nodes").
  2058  					Namespace(ns).
  2059  					Annotation(volume.AnnBindCompleted, "true").
  2060  					VolumeName(pv.Name).
  2061  					AccessModes([]v1.PersistentVolumeAccessMode{v1.ReadWriteOncePod}).
  2062  					Resources(storage).
  2063  					Obj())
  2064  				if err != nil {
  2065  					return fmt.Errorf("cannot create pvc: %w", err)
  2066  				}
  2067  				return nil
  2068  			},
  2069  			pod: &testutils.PausePodConfig{
  2070  				Name: "pod-with-pvc-has-non-existent-nodes",
  2071  				Volumes: []v1.Volume{{
  2072  					Name: "volume",
  2073  					VolumeSource: v1.VolumeSource{
  2074  						PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
  2075  							ClaimName: "pvc-has-non-existent-nodes",
  2076  						},
  2077  					},
  2078  				}},
  2079  			},
  2080  			update: func(cs kubernetes.Interface, ns string) error {
  2081  				_, err := createNode(cs, st.MakeNode().Label("kubernetes.io/hostname", "node-available").Name("node-available").Obj())
  2082  				if err != nil {
  2083  					return fmt.Errorf("cannot create node: %w", err)
  2084  				}
  2085  				return nil
  2086  			},
  2087  		},
  2088  		{
  2089  			name: "pod with pvc got scheduled after node updated it's label",
  2090  			init: func(cs kubernetes.Interface, ns string) error {
  2091  				_, err := createNode(cs, st.MakeNode().Label("foo", "foo").Name("node-foo").Obj())
  2092  				if err != nil {
  2093  					return fmt.Errorf("cannot create node: %w", err)
  2094  				}
  2095  				storage := v1.VolumeResourceRequirements{Requests: v1.ResourceList{v1.ResourceStorage: resource.MustParse("1Mi")}}
  2096  				volType := v1.HostPathDirectoryOrCreate
  2097  				pv, err := testutils.CreatePV(cs, st.MakePersistentVolume().
  2098  					Name("pv-foo").
  2099  					AccessModes([]v1.PersistentVolumeAccessMode{v1.ReadWriteOncePod}).
  2100  					Capacity(storage.Requests).
  2101  					HostPathVolumeSource(&v1.HostPathVolumeSource{Path: "/tmp", Type: &volType}).
  2102  					NodeAffinityIn("foo", []string{"bar"}).
  2103  					Obj())
  2104  				if err != nil {
  2105  					return fmt.Errorf("cannot create pv: %w", err)
  2106  				}
  2107  				_, err = testutils.CreatePVC(cs, st.MakePersistentVolumeClaim().
  2108  					Name("pvc-foo").
  2109  					Namespace(ns).
  2110  					Annotation(volume.AnnBindCompleted, "true").
  2111  					VolumeName(pv.Name).
  2112  					AccessModes([]v1.PersistentVolumeAccessMode{v1.ReadWriteOncePod}).
  2113  					Resources(storage).
  2114  					Obj())
  2115  				if err != nil {
  2116  					return fmt.Errorf("cannot create pvc: %w", err)
  2117  				}
  2118  				return nil
  2119  			},
  2120  			pod: &testutils.PausePodConfig{
  2121  				Name: "pod-with-pvc-foo",
  2122  				Volumes: []v1.Volume{{
  2123  					Name: "volume",
  2124  					VolumeSource: v1.VolumeSource{
  2125  						PersistentVolumeClaim: &v1.PersistentVolumeClaimVolumeSource{
  2126  							ClaimName: "pvc-foo",
  2127  						},
  2128  					},
  2129  				}},
  2130  			},
  2131  			update: func(cs kubernetes.Interface, ns string) error {
  2132  				_, err := updateNode(cs, &v1.Node{
  2133  					ObjectMeta: metav1.ObjectMeta{
  2134  						Name: "node-foo",
  2135  						Labels: map[string]string{
  2136  							"foo": "bar",
  2137  						},
  2138  					},
  2139  				})
  2140  				if err != nil {
  2141  					return fmt.Errorf("cannot update node: %w", err)
  2142  				}
  2143  				return nil
  2144  			},
  2145  		},
  2146  	}
  2147  	for _, tt := range tests {
  2148  		t.Run(tt.name, func(t *testing.T) {
  2149  			testCtx := initTest(t, "scheduler-informer")
  2150  
  2151  			if tt.init != nil {
  2152  				if err := tt.init(testCtx.ClientSet, testCtx.NS.Name); err != nil {
  2153  					t.Fatal(err)
  2154  				}
  2155  			}
  2156  			tt.pod.Namespace = testCtx.NS.Name
  2157  			pod, err := createPausePod(testCtx.ClientSet, initPausePod(tt.pod))
  2158  			if err != nil {
  2159  				t.Fatal(err)
  2160  			}
  2161  			if err := waitForPodUnschedulable(testCtx.ClientSet, pod); err != nil {
  2162  				t.Errorf("Pod %v got scheduled: %v", pod.Name, err)
  2163  			}
  2164  			if err := tt.update(testCtx.ClientSet, testCtx.NS.Name); err != nil {
  2165  				t.Fatal(err)
  2166  			}
  2167  			if err := testutils.WaitForPodToSchedule(testCtx.ClientSet, pod); err != nil {
  2168  				t.Errorf("Pod %v was not scheduled: %v", pod.Name, err)
  2169  			}
  2170  			// Make sure pending queue is empty.
  2171  			pendingPods, s := testCtx.Scheduler.SchedulingQueue.PendingPods()
  2172  			if len(pendingPods) != 0 {
  2173  				t.Errorf("pending pods queue is not empty, size is: %d, summary is: %s", len(pendingPods), s)
  2174  			}
  2175  		})
  2176  	}
  2177  }
  2178  
  2179  // TestPodAffinityMatchLabelKeyEnablement tests the Pod is correctly mutated by MatchLabelKeysInPodAffinity feature,
  2180  // even if turing the feature gate enabled or disabled.
  2181  func TestPodAffinityMatchLabelKeyEnablement(t *testing.T) {
  2182  	// enable the feature gate
  2183  	featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MatchLabelKeysInPodAffinity, true)
  2184  	testCtx := initTest(t, "matchlabelkey")
  2185  
  2186  	pod := &v1.Pod{
  2187  		ObjectMeta: metav1.ObjectMeta{
  2188  			GenerateName: "test",
  2189  			Namespace:    testCtx.NS.Name,
  2190  			Labels:       map[string]string{"foo": "", "bar": "a"},
  2191  		},
  2192  		Spec: v1.PodSpec{
  2193  			Containers: []v1.Container{
  2194  				{
  2195  					Name:  "container",
  2196  					Image: imageutils.GetPauseImageName(),
  2197  					Resources: v1.ResourceRequirements{
  2198  						Requests: v1.ResourceList{
  2199  							v1.ResourceMemory: resource.MustParse("1G"),
  2200  						},
  2201  					},
  2202  				},
  2203  			},
  2204  			Affinity: &v1.Affinity{
  2205  				PodAffinity: &v1.PodAffinity{
  2206  					RequiredDuringSchedulingIgnoredDuringExecution: []v1.PodAffinityTerm{
  2207  						{
  2208  							TopologyKey: "node",
  2209  							LabelSelector: &metav1.LabelSelector{
  2210  								MatchExpressions: []metav1.LabelSelectorRequirement{
  2211  									{
  2212  										Key:      "foo",
  2213  										Operator: metav1.LabelSelectorOpExists,
  2214  									},
  2215  								},
  2216  							},
  2217  							MatchLabelKeys: []string{"bar"},
  2218  						},
  2219  					},
  2220  				},
  2221  			},
  2222  		},
  2223  	}
  2224  	expectedLabelSelector := &metav1.LabelSelector{
  2225  		MatchExpressions: []metav1.LabelSelectorRequirement{
  2226  			{
  2227  				Key:      "foo",
  2228  				Operator: metav1.LabelSelectorOpExists,
  2229  			},
  2230  			{
  2231  				Key:      "bar",
  2232  				Operator: metav1.LabelSelectorOpIn,
  2233  				Values:   []string{"a"},
  2234  			},
  2235  		},
  2236  	}
  2237  
  2238  	p1, err := testCtx.ClientSet.CoreV1().Pods(testCtx.NS.Name).Create(testCtx.Ctx, pod, metav1.CreateOptions{})
  2239  	if err != nil {
  2240  		t.Fatalf("Error while creating pod during test: %v", err)
  2241  	}
  2242  
  2243  	// check the pod has the expected label selector.
  2244  	gotpod, err := testCtx.ClientSet.CoreV1().Pods(testCtx.NS.Name).Get(testCtx.Ctx, p1.Name, metav1.GetOptions{})
  2245  	if err != nil {
  2246  		t.Fatalf("Error while getting pod during test: %v", err)
  2247  	}
  2248  
  2249  	// the label selector should be changed from the original one because the feature gate is enabled.
  2250  	if d := cmp.Diff(gotpod.Spec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector, expectedLabelSelector); d != "" {
  2251  		t.Fatalf("Pod %v has wrong label selector: diff = \n%v", p1.Name, d)
  2252  	}
  2253  
  2254  	// disable the feature gate.
  2255  	featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MatchLabelKeysInPodAffinity, false)
  2256  
  2257  	p2, err := testCtx.ClientSet.CoreV1().Pods(testCtx.NS.Name).Create(testCtx.Ctx, pod, metav1.CreateOptions{})
  2258  	if err != nil {
  2259  		t.Fatalf("Error while creating pod during test: %v", err)
  2260  	}
  2261  
  2262  	// check the pod has the expected label selector.
  2263  	gotpod, err = testCtx.ClientSet.CoreV1().Pods(testCtx.NS.Name).Get(testCtx.Ctx, p2.Name, metav1.GetOptions{})
  2264  	if err != nil {
  2265  		t.Fatalf("Error while getting pod during test: %v", err)
  2266  	}
  2267  
  2268  	// the label selector should be the same as the original one because the feature gate is disabled.
  2269  	if d := cmp.Diff(gotpod.Spec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector, pod.Spec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector); d != "" {
  2270  		t.Fatalf("Pod %v has wrong label selector: diff = \n%v", p2.Name, d)
  2271  	}
  2272  
  2273  	// check the pod, which was created when the feature gate is enabled, still has the expected label selector.
  2274  	gotpod, err = testCtx.ClientSet.CoreV1().Pods(testCtx.NS.Name).Get(testCtx.Ctx, p1.Name, metav1.GetOptions{})
  2275  	if err != nil {
  2276  		t.Fatalf("Error while getting pod during test: %v", err)
  2277  	}
  2278  
  2279  	// the label selector should be changed from the original one because the feature gate is enabled.
  2280  	if d := cmp.Diff(gotpod.Spec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector, expectedLabelSelector); d != "" {
  2281  		t.Fatalf("Pod %v has wrong label selector: diff = \n%v", p1.Name, d)
  2282  	}
  2283  
  2284  	// Again, enable the feature gate.
  2285  	featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MatchLabelKeysInPodAffinity, true)
  2286  
  2287  	p3, err := testCtx.ClientSet.CoreV1().Pods(testCtx.NS.Name).Create(testCtx.Ctx, pod, metav1.CreateOptions{})
  2288  	if err != nil {
  2289  		t.Fatalf("Error while creating pod during test: %v", err)
  2290  	}
  2291  
  2292  	// check the pod has the expected label selector.
  2293  	gotpod, err = testCtx.ClientSet.CoreV1().Pods(testCtx.NS.Name).Get(testCtx.Ctx, p3.Name, metav1.GetOptions{})
  2294  	if err != nil {
  2295  		t.Fatalf("Error while getting pod during test: %v", err)
  2296  	}
  2297  
  2298  	// the label selector should be changed from the original one because the feature gate is enabled.
  2299  	if d := cmp.Diff(gotpod.Spec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector, expectedLabelSelector); d != "" {
  2300  		t.Fatalf("Pod %v has wrong label selector: diff = \n%v", p1.Name, d)
  2301  	}
  2302  
  2303  	// check the pod has the expected label selector.
  2304  	gotpod, err = testCtx.ClientSet.CoreV1().Pods(testCtx.NS.Name).Get(testCtx.Ctx, p2.Name, metav1.GetOptions{})
  2305  	if err != nil {
  2306  		t.Fatalf("Error while getting pod during test: %v", err)
  2307  	}
  2308  
  2309  	// the label selector shouldn't get changed because the feature gate was disabled at its creation.
  2310  	// Even if the feature gate is enabled now, matchLabelKeys don't get applied to the pod.
  2311  	// (it's only handled when the pod is created)
  2312  	if d := cmp.Diff(gotpod.Spec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector, pod.Spec.Affinity.PodAffinity.RequiredDuringSchedulingIgnoredDuringExecution[0].LabelSelector); d != "" {
  2313  		t.Fatalf("Pod %v has wrong label selector: diff = \n%v", p2.Name, d)
  2314  	}
  2315  
  2316  }