k8s.io/kubernetes@v1.29.3/pkg/api/pod/util_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 pod
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"strings"
    23  	"testing"
    24  
    25  	"github.com/google/go-cmp/cmp"
    26  
    27  	v1 "k8s.io/api/core/v1"
    28  	"k8s.io/apimachinery/pkg/api/resource"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/util/sets"
    31  	"k8s.io/apimachinery/pkg/util/validation/field"
    32  	utilfeature "k8s.io/apiserver/pkg/util/feature"
    33  	featuregatetesting "k8s.io/component-base/featuregate/testing"
    34  	api "k8s.io/kubernetes/pkg/apis/core"
    35  	"k8s.io/kubernetes/pkg/features"
    36  	"k8s.io/utils/pointer"
    37  )
    38  
    39  func TestVisitContainers(t *testing.T) {
    40  	setAllFeatureEnabledContainersDuringTest := ContainerType(0)
    41  	testCases := []struct {
    42  		desc           string
    43  		spec           *api.PodSpec
    44  		wantContainers []string
    45  		mask           ContainerType
    46  	}{
    47  		{
    48  			desc:           "empty podspec",
    49  			spec:           &api.PodSpec{},
    50  			wantContainers: []string{},
    51  			mask:           AllContainers,
    52  		},
    53  		{
    54  			desc: "regular containers",
    55  			spec: &api.PodSpec{
    56  				Containers: []api.Container{
    57  					{Name: "c1"},
    58  					{Name: "c2"},
    59  				},
    60  				InitContainers: []api.Container{
    61  					{Name: "i1"},
    62  					{Name: "i2"},
    63  				},
    64  				EphemeralContainers: []api.EphemeralContainer{
    65  					{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e1"}},
    66  					{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e2"}},
    67  				},
    68  			},
    69  			wantContainers: []string{"c1", "c2"},
    70  			mask:           Containers,
    71  		},
    72  		{
    73  			desc: "init containers",
    74  			spec: &api.PodSpec{
    75  				Containers: []api.Container{
    76  					{Name: "c1"},
    77  					{Name: "c2"},
    78  				},
    79  				InitContainers: []api.Container{
    80  					{Name: "i1"},
    81  					{Name: "i2"},
    82  				},
    83  				EphemeralContainers: []api.EphemeralContainer{
    84  					{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e1"}},
    85  					{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e2"}},
    86  				},
    87  			},
    88  			wantContainers: []string{"i1", "i2"},
    89  			mask:           InitContainers,
    90  		},
    91  		{
    92  			desc: "ephemeral containers",
    93  			spec: &api.PodSpec{
    94  				Containers: []api.Container{
    95  					{Name: "c1"},
    96  					{Name: "c2"},
    97  				},
    98  				InitContainers: []api.Container{
    99  					{Name: "i1"},
   100  					{Name: "i2"},
   101  				},
   102  				EphemeralContainers: []api.EphemeralContainer{
   103  					{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e1"}},
   104  					{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e2"}},
   105  				},
   106  			},
   107  			wantContainers: []string{"e1", "e2"},
   108  			mask:           EphemeralContainers,
   109  		},
   110  		{
   111  			desc: "all container types",
   112  			spec: &api.PodSpec{
   113  				Containers: []api.Container{
   114  					{Name: "c1"},
   115  					{Name: "c2"},
   116  				},
   117  				InitContainers: []api.Container{
   118  					{Name: "i1"},
   119  					{Name: "i2"},
   120  				},
   121  				EphemeralContainers: []api.EphemeralContainer{
   122  					{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e1"}},
   123  					{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e2"}},
   124  				},
   125  			},
   126  			wantContainers: []string{"i1", "i2", "c1", "c2", "e1", "e2"},
   127  			mask:           AllContainers,
   128  		},
   129  		{
   130  			desc: "all feature enabled container types with ephemeral containers enabled",
   131  			spec: &api.PodSpec{
   132  				Containers: []api.Container{
   133  					{Name: "c1"},
   134  					{Name: "c2", SecurityContext: &api.SecurityContext{}},
   135  				},
   136  				InitContainers: []api.Container{
   137  					{Name: "i1"},
   138  					{Name: "i2", SecurityContext: &api.SecurityContext{}},
   139  				},
   140  				EphemeralContainers: []api.EphemeralContainer{
   141  					{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e1"}},
   142  					{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e2"}},
   143  				},
   144  			},
   145  			wantContainers: []string{"i1", "i2", "c1", "c2", "e1", "e2"},
   146  			mask:           setAllFeatureEnabledContainersDuringTest,
   147  		},
   148  		{
   149  			desc: "dropping fields",
   150  			spec: &api.PodSpec{
   151  				Containers: []api.Container{
   152  					{Name: "c1"},
   153  					{Name: "c2", SecurityContext: &api.SecurityContext{}},
   154  				},
   155  				InitContainers: []api.Container{
   156  					{Name: "i1"},
   157  					{Name: "i2", SecurityContext: &api.SecurityContext{}},
   158  				},
   159  				EphemeralContainers: []api.EphemeralContainer{
   160  					{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e1"}},
   161  					{EphemeralContainerCommon: api.EphemeralContainerCommon{Name: "e2", SecurityContext: &api.SecurityContext{}}},
   162  				},
   163  			},
   164  			wantContainers: []string{"i1", "i2", "c1", "c2", "e1", "e2"},
   165  			mask:           AllContainers,
   166  		},
   167  	}
   168  
   169  	for _, tc := range testCases {
   170  		t.Run(tc.desc, func(t *testing.T) {
   171  			if tc.mask == setAllFeatureEnabledContainersDuringTest {
   172  				tc.mask = AllFeatureEnabledContainers()
   173  			}
   174  
   175  			gotContainers := []string{}
   176  			VisitContainers(tc.spec, tc.mask, func(c *api.Container, containerType ContainerType) bool {
   177  				gotContainers = append(gotContainers, c.Name)
   178  				if c.SecurityContext != nil {
   179  					c.SecurityContext = nil
   180  				}
   181  				return true
   182  			})
   183  			if !cmp.Equal(gotContainers, tc.wantContainers) {
   184  				t.Errorf("VisitContainers() = %+v, want %+v", gotContainers, tc.wantContainers)
   185  			}
   186  			for _, c := range tc.spec.Containers {
   187  				if c.SecurityContext != nil {
   188  					t.Errorf("VisitContainers() did not drop SecurityContext for container %q", c.Name)
   189  				}
   190  			}
   191  			for _, c := range tc.spec.InitContainers {
   192  				if c.SecurityContext != nil {
   193  					t.Errorf("VisitContainers() did not drop SecurityContext for init container %q", c.Name)
   194  				}
   195  			}
   196  			for _, c := range tc.spec.EphemeralContainers {
   197  				if c.SecurityContext != nil {
   198  					t.Errorf("VisitContainers() did not drop SecurityContext for ephemeral container %q", c.Name)
   199  				}
   200  			}
   201  		})
   202  	}
   203  }
   204  
   205  func TestPodSecrets(t *testing.T) {
   206  	// Stub containing all possible secret references in a pod.
   207  	// The names of the referenced secrets match struct paths detected by reflection.
   208  	pod := &api.Pod{
   209  		Spec: api.PodSpec{
   210  			Containers: []api.Container{{
   211  				EnvFrom: []api.EnvFromSource{{
   212  					SecretRef: &api.SecretEnvSource{
   213  						LocalObjectReference: api.LocalObjectReference{
   214  							Name: "Spec.Containers[*].EnvFrom[*].SecretRef"}}}},
   215  				Env: []api.EnvVar{{
   216  					ValueFrom: &api.EnvVarSource{
   217  						SecretKeyRef: &api.SecretKeySelector{
   218  							LocalObjectReference: api.LocalObjectReference{
   219  								Name: "Spec.Containers[*].Env[*].ValueFrom.SecretKeyRef"}}}}}}},
   220  			ImagePullSecrets: []api.LocalObjectReference{{
   221  				Name: "Spec.ImagePullSecrets"}},
   222  			InitContainers: []api.Container{{
   223  				EnvFrom: []api.EnvFromSource{{
   224  					SecretRef: &api.SecretEnvSource{
   225  						LocalObjectReference: api.LocalObjectReference{
   226  							Name: "Spec.InitContainers[*].EnvFrom[*].SecretRef"}}}},
   227  				Env: []api.EnvVar{{
   228  					ValueFrom: &api.EnvVarSource{
   229  						SecretKeyRef: &api.SecretKeySelector{
   230  							LocalObjectReference: api.LocalObjectReference{
   231  								Name: "Spec.InitContainers[*].Env[*].ValueFrom.SecretKeyRef"}}}}}}},
   232  			Volumes: []api.Volume{{
   233  				VolumeSource: api.VolumeSource{
   234  					AzureFile: &api.AzureFileVolumeSource{
   235  						SecretName: "Spec.Volumes[*].VolumeSource.AzureFile.SecretName"}}}, {
   236  				VolumeSource: api.VolumeSource{
   237  					CephFS: &api.CephFSVolumeSource{
   238  						SecretRef: &api.LocalObjectReference{
   239  							Name: "Spec.Volumes[*].VolumeSource.CephFS.SecretRef"}}}}, {
   240  				VolumeSource: api.VolumeSource{
   241  					Cinder: &api.CinderVolumeSource{
   242  						SecretRef: &api.LocalObjectReference{
   243  							Name: "Spec.Volumes[*].VolumeSource.Cinder.SecretRef"}}}}, {
   244  				VolumeSource: api.VolumeSource{
   245  					FlexVolume: &api.FlexVolumeSource{
   246  						SecretRef: &api.LocalObjectReference{
   247  							Name: "Spec.Volumes[*].VolumeSource.FlexVolume.SecretRef"}}}}, {
   248  				VolumeSource: api.VolumeSource{
   249  					Projected: &api.ProjectedVolumeSource{
   250  						Sources: []api.VolumeProjection{{
   251  							Secret: &api.SecretProjection{
   252  								LocalObjectReference: api.LocalObjectReference{
   253  									Name: "Spec.Volumes[*].VolumeSource.Projected.Sources[*].Secret"}}}}}}}, {
   254  				VolumeSource: api.VolumeSource{
   255  					RBD: &api.RBDVolumeSource{
   256  						SecretRef: &api.LocalObjectReference{
   257  							Name: "Spec.Volumes[*].VolumeSource.RBD.SecretRef"}}}}, {
   258  				VolumeSource: api.VolumeSource{
   259  					Secret: &api.SecretVolumeSource{
   260  						SecretName: "Spec.Volumes[*].VolumeSource.Secret.SecretName"}}}, {
   261  				VolumeSource: api.VolumeSource{
   262  					Secret: &api.SecretVolumeSource{
   263  						SecretName: "Spec.Volumes[*].VolumeSource.Secret"}}}, {
   264  				VolumeSource: api.VolumeSource{
   265  					ScaleIO: &api.ScaleIOVolumeSource{
   266  						SecretRef: &api.LocalObjectReference{
   267  							Name: "Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef"}}}}, {
   268  				VolumeSource: api.VolumeSource{
   269  					ISCSI: &api.ISCSIVolumeSource{
   270  						SecretRef: &api.LocalObjectReference{
   271  							Name: "Spec.Volumes[*].VolumeSource.ISCSI.SecretRef"}}}}, {
   272  				VolumeSource: api.VolumeSource{
   273  					StorageOS: &api.StorageOSVolumeSource{
   274  						SecretRef: &api.LocalObjectReference{
   275  							Name: "Spec.Volumes[*].VolumeSource.StorageOS.SecretRef"}}}}, {
   276  				VolumeSource: api.VolumeSource{
   277  					CSI: &api.CSIVolumeSource{
   278  						NodePublishSecretRef: &api.LocalObjectReference{
   279  							Name: "Spec.Volumes[*].VolumeSource.CSI.NodePublishSecretRef"}}}}},
   280  			EphemeralContainers: []api.EphemeralContainer{{
   281  				EphemeralContainerCommon: api.EphemeralContainerCommon{
   282  					EnvFrom: []api.EnvFromSource{{
   283  						SecretRef: &api.SecretEnvSource{
   284  							LocalObjectReference: api.LocalObjectReference{
   285  								Name: "Spec.EphemeralContainers[*].EphemeralContainerCommon.EnvFrom[*].SecretRef"}}}},
   286  					Env: []api.EnvVar{{
   287  						ValueFrom: &api.EnvVarSource{
   288  							SecretKeyRef: &api.SecretKeySelector{
   289  								LocalObjectReference: api.LocalObjectReference{
   290  									Name: "Spec.EphemeralContainers[*].EphemeralContainerCommon.Env[*].ValueFrom.SecretKeyRef"}}}}}}}},
   291  		},
   292  	}
   293  	extractedNames := sets.NewString()
   294  	VisitPodSecretNames(pod, func(name string) bool {
   295  		extractedNames.Insert(name)
   296  		return true
   297  	}, AllContainers)
   298  
   299  	// excludedSecretPaths holds struct paths to fields with "secret" in the name that are not actually references to secret API objects
   300  	excludedSecretPaths := sets.NewString(
   301  		"Spec.Volumes[*].VolumeSource.CephFS.SecretFile",
   302  	)
   303  	// expectedSecretPaths holds struct paths to fields with "secret" in the name that are references to secret API objects.
   304  	// every path here should be represented as an example in the Pod stub above, with the secret name set to the path.
   305  	expectedSecretPaths := sets.NewString(
   306  		"Spec.Containers[*].EnvFrom[*].SecretRef",
   307  		"Spec.Containers[*].Env[*].ValueFrom.SecretKeyRef",
   308  		"Spec.EphemeralContainers[*].EphemeralContainerCommon.EnvFrom[*].SecretRef",
   309  		"Spec.EphemeralContainers[*].EphemeralContainerCommon.Env[*].ValueFrom.SecretKeyRef",
   310  		"Spec.ImagePullSecrets",
   311  		"Spec.InitContainers[*].EnvFrom[*].SecretRef",
   312  		"Spec.InitContainers[*].Env[*].ValueFrom.SecretKeyRef",
   313  		"Spec.Volumes[*].VolumeSource.AzureFile.SecretName",
   314  		"Spec.Volumes[*].VolumeSource.CephFS.SecretRef",
   315  		"Spec.Volumes[*].VolumeSource.Cinder.SecretRef",
   316  		"Spec.Volumes[*].VolumeSource.FlexVolume.SecretRef",
   317  		"Spec.Volumes[*].VolumeSource.Projected.Sources[*].Secret",
   318  		"Spec.Volumes[*].VolumeSource.RBD.SecretRef",
   319  		"Spec.Volumes[*].VolumeSource.Secret",
   320  		"Spec.Volumes[*].VolumeSource.Secret.SecretName",
   321  		"Spec.Volumes[*].VolumeSource.ScaleIO.SecretRef",
   322  		"Spec.Volumes[*].VolumeSource.ISCSI.SecretRef",
   323  		"Spec.Volumes[*].VolumeSource.StorageOS.SecretRef",
   324  		"Spec.Volumes[*].VolumeSource.CSI.NodePublishSecretRef",
   325  	)
   326  	secretPaths := collectResourcePaths(t, "secret", nil, "", reflect.TypeOf(&api.Pod{}))
   327  	secretPaths = secretPaths.Difference(excludedSecretPaths)
   328  	if missingPaths := expectedSecretPaths.Difference(secretPaths); len(missingPaths) > 0 {
   329  		t.Logf("Missing expected secret paths:\n%s", strings.Join(missingPaths.List(), "\n"))
   330  		t.Error("Missing expected secret paths. Verify VisitPodSecretNames() is correctly finding the missing paths, then correct expectedSecretPaths")
   331  	}
   332  	if extraPaths := secretPaths.Difference(expectedSecretPaths); len(extraPaths) > 0 {
   333  		t.Logf("Extra secret paths:\n%s", strings.Join(extraPaths.List(), "\n"))
   334  		t.Error("Extra fields with 'secret' in the name found. Verify VisitPodSecretNames() is including these fields if appropriate, then correct expectedSecretPaths")
   335  	}
   336  
   337  	if missingNames := expectedSecretPaths.Difference(extractedNames); len(missingNames) > 0 {
   338  		t.Logf("Missing expected secret names:\n%s", strings.Join(missingNames.List(), "\n"))
   339  		t.Error("Missing expected secret names. Verify the pod stub above includes these references, then verify VisitPodSecretNames() is correctly finding the missing names")
   340  	}
   341  	if extraNames := extractedNames.Difference(expectedSecretPaths); len(extraNames) > 0 {
   342  		t.Logf("Extra secret names:\n%s", strings.Join(extraNames.List(), "\n"))
   343  		t.Error("Extra secret names extracted. Verify VisitPodSecretNames() is correctly extracting secret names")
   344  	}
   345  
   346  	// emptyPod is a stub containing empty object names
   347  	emptyPod := &api.Pod{
   348  		Spec: api.PodSpec{
   349  			Containers: []api.Container{{
   350  				EnvFrom: []api.EnvFromSource{{
   351  					SecretRef: &api.SecretEnvSource{
   352  						LocalObjectReference: api.LocalObjectReference{
   353  							Name: ""}}}}}},
   354  		},
   355  	}
   356  	VisitPodSecretNames(emptyPod, func(name string) bool {
   357  		t.Fatalf("expected no empty names collected, got %q", name)
   358  		return false
   359  	}, AllContainers)
   360  }
   361  
   362  // collectResourcePaths traverses the object, computing all the struct paths that lead to fields with resourcename in the name.
   363  func collectResourcePaths(t *testing.T, resourcename string, path *field.Path, name string, tp reflect.Type) sets.String {
   364  	resourcename = strings.ToLower(resourcename)
   365  	resourcePaths := sets.NewString()
   366  
   367  	if tp.Kind() == reflect.Pointer {
   368  		resourcePaths.Insert(collectResourcePaths(t, resourcename, path, name, tp.Elem()).List()...)
   369  		return resourcePaths
   370  	}
   371  
   372  	if strings.Contains(strings.ToLower(name), resourcename) {
   373  		resourcePaths.Insert(path.String())
   374  	}
   375  
   376  	switch tp.Kind() {
   377  	case reflect.Pointer:
   378  		resourcePaths.Insert(collectResourcePaths(t, resourcename, path, name, tp.Elem()).List()...)
   379  	case reflect.Struct:
   380  		// ObjectMeta is generic and therefore should never have a field with a specific resource's name;
   381  		// it contains cycles so it's easiest to just skip it.
   382  		if name == "ObjectMeta" {
   383  			break
   384  		}
   385  		for i := 0; i < tp.NumField(); i++ {
   386  			field := tp.Field(i)
   387  			resourcePaths.Insert(collectResourcePaths(t, resourcename, path.Child(field.Name), field.Name, field.Type).List()...)
   388  		}
   389  	case reflect.Interface:
   390  		t.Errorf("cannot find %s fields in interface{} field %s", resourcename, path.String())
   391  	case reflect.Map:
   392  		resourcePaths.Insert(collectResourcePaths(t, resourcename, path.Key("*"), "", tp.Elem()).List()...)
   393  	case reflect.Slice:
   394  		resourcePaths.Insert(collectResourcePaths(t, resourcename, path.Key("*"), "", tp.Elem()).List()...)
   395  	default:
   396  		// all primitive types
   397  	}
   398  
   399  	return resourcePaths
   400  }
   401  
   402  func TestPodConfigmaps(t *testing.T) {
   403  	// Stub containing all possible ConfigMap references in a pod.
   404  	// The names of the referenced ConfigMaps match struct paths detected by reflection.
   405  	pod := &api.Pod{
   406  		Spec: api.PodSpec{
   407  			Containers: []api.Container{{
   408  				EnvFrom: []api.EnvFromSource{{
   409  					ConfigMapRef: &api.ConfigMapEnvSource{
   410  						LocalObjectReference: api.LocalObjectReference{
   411  							Name: "Spec.Containers[*].EnvFrom[*].ConfigMapRef"}}}},
   412  				Env: []api.EnvVar{{
   413  					ValueFrom: &api.EnvVarSource{
   414  						ConfigMapKeyRef: &api.ConfigMapKeySelector{
   415  							LocalObjectReference: api.LocalObjectReference{
   416  								Name: "Spec.Containers[*].Env[*].ValueFrom.ConfigMapKeyRef"}}}}}}},
   417  			EphemeralContainers: []api.EphemeralContainer{{
   418  				EphemeralContainerCommon: api.EphemeralContainerCommon{
   419  					EnvFrom: []api.EnvFromSource{{
   420  						ConfigMapRef: &api.ConfigMapEnvSource{
   421  							LocalObjectReference: api.LocalObjectReference{
   422  								Name: "Spec.EphemeralContainers[*].EphemeralContainerCommon.EnvFrom[*].ConfigMapRef"}}}},
   423  					Env: []api.EnvVar{{
   424  						ValueFrom: &api.EnvVarSource{
   425  							ConfigMapKeyRef: &api.ConfigMapKeySelector{
   426  								LocalObjectReference: api.LocalObjectReference{
   427  									Name: "Spec.EphemeralContainers[*].EphemeralContainerCommon.Env[*].ValueFrom.ConfigMapKeyRef"}}}}}}}},
   428  			InitContainers: []api.Container{{
   429  				EnvFrom: []api.EnvFromSource{{
   430  					ConfigMapRef: &api.ConfigMapEnvSource{
   431  						LocalObjectReference: api.LocalObjectReference{
   432  							Name: "Spec.InitContainers[*].EnvFrom[*].ConfigMapRef"}}}},
   433  				Env: []api.EnvVar{{
   434  					ValueFrom: &api.EnvVarSource{
   435  						ConfigMapKeyRef: &api.ConfigMapKeySelector{
   436  							LocalObjectReference: api.LocalObjectReference{
   437  								Name: "Spec.InitContainers[*].Env[*].ValueFrom.ConfigMapKeyRef"}}}}}}},
   438  			Volumes: []api.Volume{{
   439  				VolumeSource: api.VolumeSource{
   440  					Projected: &api.ProjectedVolumeSource{
   441  						Sources: []api.VolumeProjection{{
   442  							ConfigMap: &api.ConfigMapProjection{
   443  								LocalObjectReference: api.LocalObjectReference{
   444  									Name: "Spec.Volumes[*].VolumeSource.Projected.Sources[*].ConfigMap"}}}}}}}, {
   445  				VolumeSource: api.VolumeSource{
   446  					ConfigMap: &api.ConfigMapVolumeSource{
   447  						LocalObjectReference: api.LocalObjectReference{
   448  							Name: "Spec.Volumes[*].VolumeSource.ConfigMap"}}}}},
   449  		},
   450  	}
   451  	extractedNames := sets.NewString()
   452  	VisitPodConfigmapNames(pod, func(name string) bool {
   453  		extractedNames.Insert(name)
   454  		return true
   455  	}, AllContainers)
   456  
   457  	// expectedPaths holds struct paths to fields with "ConfigMap" in the name that are references to ConfigMap API objects.
   458  	// every path here should be represented as an example in the Pod stub above, with the ConfigMap name set to the path.
   459  	expectedPaths := sets.NewString(
   460  		"Spec.Containers[*].EnvFrom[*].ConfigMapRef",
   461  		"Spec.Containers[*].Env[*].ValueFrom.ConfigMapKeyRef",
   462  		"Spec.EphemeralContainers[*].EphemeralContainerCommon.EnvFrom[*].ConfigMapRef",
   463  		"Spec.EphemeralContainers[*].EphemeralContainerCommon.Env[*].ValueFrom.ConfigMapKeyRef",
   464  		"Spec.InitContainers[*].EnvFrom[*].ConfigMapRef",
   465  		"Spec.InitContainers[*].Env[*].ValueFrom.ConfigMapKeyRef",
   466  		"Spec.Volumes[*].VolumeSource.Projected.Sources[*].ConfigMap",
   467  		"Spec.Volumes[*].VolumeSource.ConfigMap",
   468  	)
   469  	collectPaths := collectResourcePaths(t, "ConfigMap", nil, "", reflect.TypeOf(&api.Pod{}))
   470  	if missingPaths := expectedPaths.Difference(collectPaths); len(missingPaths) > 0 {
   471  		t.Logf("Missing expected paths:\n%s", strings.Join(missingPaths.List(), "\n"))
   472  		t.Error("Missing expected paths. Verify VisitPodConfigmapNames() is correctly finding the missing paths, then correct expectedPaths")
   473  	}
   474  	if extraPaths := collectPaths.Difference(expectedPaths); len(extraPaths) > 0 {
   475  		t.Logf("Extra paths:\n%s", strings.Join(extraPaths.List(), "\n"))
   476  		t.Error("Extra fields with resource in the name found. Verify VisitPodConfigmapNames() is including these fields if appropriate, then correct expectedPaths")
   477  	}
   478  
   479  	if missingNames := expectedPaths.Difference(extractedNames); len(missingNames) > 0 {
   480  		t.Logf("Missing expected names:\n%s", strings.Join(missingNames.List(), "\n"))
   481  		t.Error("Missing expected names. Verify the pod stub above includes these references, then verify VisitPodConfigmapNames() is correctly finding the missing names")
   482  	}
   483  	if extraNames := extractedNames.Difference(expectedPaths); len(extraNames) > 0 {
   484  		t.Logf("Extra names:\n%s", strings.Join(extraNames.List(), "\n"))
   485  		t.Error("Extra names extracted. Verify VisitPodConfigmapNames() is correctly extracting resource names")
   486  	}
   487  
   488  	// emptyPod is a stub containing empty object names
   489  	emptyPod := &api.Pod{
   490  		Spec: api.PodSpec{
   491  			Containers: []api.Container{{
   492  				EnvFrom: []api.EnvFromSource{{
   493  					ConfigMapRef: &api.ConfigMapEnvSource{
   494  						LocalObjectReference: api.LocalObjectReference{
   495  							Name: ""}}}}}},
   496  		},
   497  	}
   498  	VisitPodConfigmapNames(emptyPod, func(name string) bool {
   499  		t.Fatalf("expected no empty names collected, got %q", name)
   500  		return false
   501  	}, AllContainers)
   502  }
   503  
   504  func TestDropFSGroupFields(t *testing.T) {
   505  	nofsGroupPod := func() *api.Pod {
   506  		return &api.Pod{
   507  			Spec: api.PodSpec{
   508  				Containers: []api.Container{
   509  					{
   510  						Name:  "container1",
   511  						Image: "testimage",
   512  					},
   513  				},
   514  			},
   515  		}
   516  	}
   517  
   518  	var podFSGroup int64 = 100
   519  	changePolicy := api.FSGroupChangeAlways
   520  
   521  	fsGroupPod := func() *api.Pod {
   522  		return &api.Pod{
   523  			Spec: api.PodSpec{
   524  				Containers: []api.Container{
   525  					{
   526  						Name:  "container1",
   527  						Image: "testimage",
   528  					},
   529  				},
   530  				SecurityContext: &api.PodSecurityContext{
   531  					FSGroup:             &podFSGroup,
   532  					FSGroupChangePolicy: &changePolicy,
   533  				},
   534  			},
   535  		}
   536  	}
   537  	podInfos := []struct {
   538  		description                  string
   539  		newPodHasFSGroupChangePolicy bool
   540  		pod                          func() *api.Pod
   541  		expectPolicyInPod            bool
   542  	}{
   543  		{
   544  			description:                  "oldPod.FSGroupChangePolicy=nil, feature=true, newPod.FSGroupChangePolicy=true",
   545  			pod:                          nofsGroupPod,
   546  			newPodHasFSGroupChangePolicy: true,
   547  			expectPolicyInPod:            true,
   548  		},
   549  		{
   550  			description:                  "oldPod=nil, feature=true, newPod.FSGroupChangePolicy=true",
   551  			pod:                          func() *api.Pod { return nil },
   552  			newPodHasFSGroupChangePolicy: true,
   553  			expectPolicyInPod:            true,
   554  		},
   555  		{
   556  			description:                  "oldPod.FSGroupChangePolicy=true, feature=true, newPod.FSGroupChangePolicy=false",
   557  			pod:                          fsGroupPod,
   558  			newPodHasFSGroupChangePolicy: false,
   559  			expectPolicyInPod:            false,
   560  		},
   561  	}
   562  	for _, podInfo := range podInfos {
   563  		t.Run(podInfo.description, func(t *testing.T) {
   564  			oldPod := podInfo.pod()
   565  			newPod := oldPod.DeepCopy()
   566  			if oldPod == nil && podInfo.newPodHasFSGroupChangePolicy {
   567  				newPod = fsGroupPod()
   568  			}
   569  
   570  			if oldPod != nil {
   571  				if podInfo.newPodHasFSGroupChangePolicy {
   572  					newPod.Spec.SecurityContext = &api.PodSecurityContext{
   573  						FSGroup:             &podFSGroup,
   574  						FSGroupChangePolicy: &changePolicy,
   575  					}
   576  				} else {
   577  					newPod.Spec.SecurityContext = &api.PodSecurityContext{}
   578  				}
   579  			}
   580  			DropDisabledPodFields(newPod, oldPod)
   581  
   582  			if podInfo.expectPolicyInPod {
   583  				secContext := newPod.Spec.SecurityContext
   584  				if secContext == nil || secContext.FSGroupChangePolicy == nil {
   585  					t.Errorf("for %s, expected fsGroupChangepolicy found none", podInfo.description)
   586  				}
   587  			} else {
   588  				secContext := newPod.Spec.SecurityContext
   589  				if secContext != nil && secContext.FSGroupChangePolicy != nil {
   590  					t.Errorf("for %s, unexpected fsGroupChangepolicy set", podInfo.description)
   591  				}
   592  			}
   593  		})
   594  	}
   595  
   596  }
   597  
   598  func TestDropProcMount(t *testing.T) {
   599  	procMount := api.UnmaskedProcMount
   600  	defaultProcMount := api.DefaultProcMount
   601  	podWithProcMount := func() *api.Pod {
   602  		return &api.Pod{
   603  			Spec: api.PodSpec{
   604  				RestartPolicy:  api.RestartPolicyNever,
   605  				Containers:     []api.Container{{Name: "container1", Image: "testimage", SecurityContext: &api.SecurityContext{ProcMount: &procMount}}},
   606  				InitContainers: []api.Container{{Name: "container1", Image: "testimage", SecurityContext: &api.SecurityContext{ProcMount: &procMount}}},
   607  			},
   608  		}
   609  	}
   610  	podWithDefaultProcMount := func() *api.Pod {
   611  		return &api.Pod{
   612  			Spec: api.PodSpec{
   613  				RestartPolicy:  api.RestartPolicyNever,
   614  				Containers:     []api.Container{{Name: "container1", Image: "testimage", SecurityContext: &api.SecurityContext{ProcMount: &defaultProcMount}}},
   615  				InitContainers: []api.Container{{Name: "container1", Image: "testimage", SecurityContext: &api.SecurityContext{ProcMount: &defaultProcMount}}},
   616  			},
   617  		}
   618  	}
   619  	podWithoutProcMount := func() *api.Pod {
   620  		return &api.Pod{
   621  			Spec: api.PodSpec{
   622  				RestartPolicy:  api.RestartPolicyNever,
   623  				Containers:     []api.Container{{Name: "container1", Image: "testimage", SecurityContext: &api.SecurityContext{ProcMount: nil}}},
   624  				InitContainers: []api.Container{{Name: "container1", Image: "testimage", SecurityContext: &api.SecurityContext{ProcMount: nil}}},
   625  			},
   626  		}
   627  	}
   628  
   629  	podInfo := []struct {
   630  		description  string
   631  		hasProcMount bool
   632  		pod          func() *api.Pod
   633  	}{
   634  		{
   635  			description:  "has ProcMount",
   636  			hasProcMount: true,
   637  			pod:          podWithProcMount,
   638  		},
   639  		{
   640  			description:  "has default ProcMount",
   641  			hasProcMount: false,
   642  			pod:          podWithDefaultProcMount,
   643  		},
   644  		{
   645  			description:  "does not have ProcMount",
   646  			hasProcMount: false,
   647  			pod:          podWithoutProcMount,
   648  		},
   649  		{
   650  			description:  "is nil",
   651  			hasProcMount: false,
   652  			pod:          func() *api.Pod { return nil },
   653  		},
   654  	}
   655  
   656  	for _, enabled := range []bool{true, false} {
   657  		for _, oldPodInfo := range podInfo {
   658  			for _, newPodInfo := range podInfo {
   659  				oldPodHasProcMount, oldPod := oldPodInfo.hasProcMount, oldPodInfo.pod()
   660  				newPodHasProcMount, newPod := newPodInfo.hasProcMount, newPodInfo.pod()
   661  				if newPod == nil {
   662  					continue
   663  				}
   664  
   665  				t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
   666  					defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ProcMountType, enabled)()
   667  
   668  					var oldPodSpec *api.PodSpec
   669  					if oldPod != nil {
   670  						oldPodSpec = &oldPod.Spec
   671  					}
   672  					dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
   673  
   674  					// old pod should never be changed
   675  					if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
   676  						t.Errorf("old pod changed: %v", cmp.Diff(oldPod, oldPodInfo.pod()))
   677  					}
   678  
   679  					switch {
   680  					case enabled || oldPodHasProcMount:
   681  						// new pod should not be changed if the feature is enabled, or if the old pod had ProcMount
   682  						if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
   683  							t.Errorf("new pod changed: %v", cmp.Diff(newPod, newPodInfo.pod()))
   684  						}
   685  					case newPodHasProcMount:
   686  						// new pod should be changed
   687  						if reflect.DeepEqual(newPod, newPodInfo.pod()) {
   688  							t.Errorf("new pod was not changed")
   689  						}
   690  						// new pod should not have ProcMount
   691  						if procMountInUse(&newPod.Spec) {
   692  							t.Errorf("new pod had ProcMount: %#v", &newPod.Spec)
   693  						}
   694  					default:
   695  						// new pod should not need to be changed
   696  						if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
   697  							t.Errorf("new pod changed: %v", cmp.Diff(newPod, newPodInfo.pod()))
   698  						}
   699  					}
   700  				})
   701  			}
   702  		}
   703  	}
   704  }
   705  
   706  func TestDropAppArmor(t *testing.T) {
   707  	podWithAppArmor := func() *api.Pod {
   708  		return &api.Pod{
   709  			ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"a": "1", v1.AppArmorBetaContainerAnnotationKeyPrefix + "foo": "default"}},
   710  			Spec:       api.PodSpec{},
   711  		}
   712  	}
   713  	podWithoutAppArmor := func() *api.Pod {
   714  		return &api.Pod{
   715  			ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{"a": "1"}},
   716  			Spec:       api.PodSpec{},
   717  		}
   718  	}
   719  
   720  	podInfo := []struct {
   721  		description string
   722  		hasAppArmor bool
   723  		pod         func() *api.Pod
   724  	}{
   725  		{
   726  			description: "has AppArmor",
   727  			hasAppArmor: true,
   728  			pod:         podWithAppArmor,
   729  		},
   730  		{
   731  			description: "does not have AppArmor",
   732  			hasAppArmor: false,
   733  			pod:         podWithoutAppArmor,
   734  		},
   735  		{
   736  			description: "is nil",
   737  			hasAppArmor: false,
   738  			pod:         func() *api.Pod { return nil },
   739  		},
   740  	}
   741  
   742  	for _, enabled := range []bool{true, false} {
   743  		for _, oldPodInfo := range podInfo {
   744  			for _, newPodInfo := range podInfo {
   745  				oldPodHasAppArmor, oldPod := oldPodInfo.hasAppArmor, oldPodInfo.pod()
   746  				newPodHasAppArmor, newPod := newPodInfo.hasAppArmor, newPodInfo.pod()
   747  				if newPod == nil {
   748  					continue
   749  				}
   750  
   751  				t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
   752  					defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.AppArmor, enabled)()
   753  
   754  					DropDisabledPodFields(newPod, oldPod)
   755  
   756  					// old pod should never be changed
   757  					if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
   758  						t.Errorf("old pod changed: %v", cmp.Diff(oldPod, oldPodInfo.pod()))
   759  					}
   760  
   761  					switch {
   762  					case enabled || oldPodHasAppArmor:
   763  						// new pod should not be changed if the feature is enabled, or if the old pod had AppArmor
   764  						if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
   765  							t.Errorf("new pod changed: %v", cmp.Diff(newPod, newPodInfo.pod()))
   766  						}
   767  					case newPodHasAppArmor:
   768  						// new pod should be changed
   769  						if reflect.DeepEqual(newPod, newPodInfo.pod()) {
   770  							t.Errorf("new pod was not changed")
   771  						}
   772  						// new pod should not have AppArmor
   773  						if !reflect.DeepEqual(newPod, podWithoutAppArmor()) {
   774  							t.Errorf("new pod had EmptyDir SizeLimit: %v", cmp.Diff(newPod, podWithoutAppArmor()))
   775  						}
   776  					default:
   777  						// new pod should not need to be changed
   778  						if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
   779  							t.Errorf("new pod changed: %v", cmp.Diff(newPod, newPodInfo.pod()))
   780  						}
   781  					}
   782  				})
   783  			}
   784  		}
   785  	}
   786  }
   787  
   788  func TestDropDynamicResourceAllocation(t *testing.T) {
   789  	resourceClaimName := "external-claim"
   790  
   791  	podWithClaims := &api.Pod{
   792  		Spec: api.PodSpec{
   793  			Containers: []api.Container{
   794  				{
   795  					Resources: api.ResourceRequirements{
   796  						Claims: []api.ResourceClaim{{Name: "my-claim"}},
   797  					},
   798  				},
   799  			},
   800  			InitContainers: []api.Container{
   801  				{
   802  					Resources: api.ResourceRequirements{
   803  						Claims: []api.ResourceClaim{{Name: "my-claim"}},
   804  					},
   805  				},
   806  			},
   807  			EphemeralContainers: []api.EphemeralContainer{
   808  				{
   809  					EphemeralContainerCommon: api.EphemeralContainerCommon{
   810  						Resources: api.ResourceRequirements{
   811  							Claims: []api.ResourceClaim{{Name: "my-claim"}},
   812  						},
   813  					},
   814  				},
   815  			},
   816  			ResourceClaims: []api.PodResourceClaim{
   817  				{
   818  					Name: "my-claim",
   819  					Source: api.ClaimSource{
   820  						ResourceClaimName: &resourceClaimName,
   821  					},
   822  				},
   823  			},
   824  		},
   825  		Status: api.PodStatus{
   826  			ResourceClaimStatuses: []api.PodResourceClaimStatus{
   827  				{Name: "my-claim", ResourceClaimName: pointer.String("pod-my-claim")},
   828  			},
   829  		},
   830  	}
   831  	podWithoutClaims := &api.Pod{
   832  		Spec: api.PodSpec{
   833  			Containers:          []api.Container{{}},
   834  			InitContainers:      []api.Container{{}},
   835  			EphemeralContainers: []api.EphemeralContainer{{}},
   836  		},
   837  	}
   838  
   839  	var noPod *api.Pod
   840  
   841  	testcases := []struct {
   842  		description string
   843  		enabled     bool
   844  		oldPod      *api.Pod
   845  		newPod      *api.Pod
   846  		wantPod     *api.Pod
   847  	}{
   848  		{
   849  			description: "old with claims / new with claims / disabled",
   850  			oldPod:      podWithClaims,
   851  			newPod:      podWithClaims,
   852  			wantPod:     podWithClaims,
   853  		},
   854  		{
   855  			description: "old without claims / new with claims / disabled",
   856  			oldPod:      podWithoutClaims,
   857  			newPod:      podWithClaims,
   858  			wantPod:     podWithoutClaims,
   859  		},
   860  		{
   861  			description: "no old pod/ new with claims / disabled",
   862  			oldPod:      noPod,
   863  			newPod:      podWithClaims,
   864  			wantPod:     podWithoutClaims,
   865  		},
   866  
   867  		{
   868  			description: "old with claims / new without claims / disabled",
   869  			oldPod:      podWithClaims,
   870  			newPod:      podWithoutClaims,
   871  			wantPod:     podWithoutClaims,
   872  		},
   873  		{
   874  			description: "old without claims / new without claims / disabled",
   875  			oldPod:      podWithoutClaims,
   876  			newPod:      podWithoutClaims,
   877  			wantPod:     podWithoutClaims,
   878  		},
   879  		{
   880  			description: "no old pod/ new without claims / disabled",
   881  			oldPod:      noPod,
   882  			newPod:      podWithoutClaims,
   883  			wantPod:     podWithoutClaims,
   884  		},
   885  
   886  		{
   887  			description: "old with claims / new with claims / enabled",
   888  			enabled:     true,
   889  			oldPod:      podWithClaims,
   890  			newPod:      podWithClaims,
   891  			wantPod:     podWithClaims,
   892  		},
   893  		{
   894  			description: "old without claims / new with claims / enabled",
   895  			enabled:     true,
   896  			oldPod:      podWithoutClaims,
   897  			newPod:      podWithClaims,
   898  			wantPod:     podWithClaims,
   899  		},
   900  		{
   901  			description: "no old pod/ new with claims / enabled",
   902  			enabled:     true,
   903  			oldPod:      noPod,
   904  			newPod:      podWithClaims,
   905  			wantPod:     podWithClaims,
   906  		},
   907  
   908  		{
   909  			description: "old with claims / new without claims / enabled",
   910  			enabled:     true,
   911  			oldPod:      podWithClaims,
   912  			newPod:      podWithoutClaims,
   913  			wantPod:     podWithoutClaims,
   914  		},
   915  		{
   916  			description: "old without claims / new without claims / enabled",
   917  			enabled:     true,
   918  			oldPod:      podWithoutClaims,
   919  			newPod:      podWithoutClaims,
   920  			wantPod:     podWithoutClaims,
   921  		},
   922  		{
   923  			description: "no old pod/ new without claims / enabled",
   924  			enabled:     true,
   925  			oldPod:      noPod,
   926  			newPod:      podWithoutClaims,
   927  			wantPod:     podWithoutClaims,
   928  		},
   929  	}
   930  
   931  	for _, tc := range testcases {
   932  		t.Run(tc.description, func(t *testing.T) {
   933  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DynamicResourceAllocation, tc.enabled)()
   934  
   935  			oldPod := tc.oldPod.DeepCopy()
   936  			newPod := tc.newPod.DeepCopy()
   937  			wantPod := tc.wantPod
   938  			DropDisabledPodFields(newPod, oldPod)
   939  
   940  			// old pod should never be changed
   941  			if diff := cmp.Diff(oldPod, tc.oldPod); diff != "" {
   942  				t.Errorf("old pod changed: %s", diff)
   943  			}
   944  
   945  			if diff := cmp.Diff(wantPod, newPod); diff != "" {
   946  				t.Errorf("new pod changed (- want, + got): %s", diff)
   947  			}
   948  		})
   949  	}
   950  }
   951  
   952  func TestValidatePodDeletionCostOption(t *testing.T) {
   953  	testCases := []struct {
   954  		name                            string
   955  		oldPodMeta                      *metav1.ObjectMeta
   956  		featureEnabled                  bool
   957  		wantAllowInvalidPodDeletionCost bool
   958  	}{
   959  		{
   960  			name:                            "CreateFeatureEnabled",
   961  			featureEnabled:                  true,
   962  			wantAllowInvalidPodDeletionCost: false,
   963  		},
   964  		{
   965  			name:                            "CreateFeatureDisabled",
   966  			featureEnabled:                  false,
   967  			wantAllowInvalidPodDeletionCost: true,
   968  		},
   969  		{
   970  			name:                            "UpdateFeatureDisabled",
   971  			oldPodMeta:                      &metav1.ObjectMeta{Annotations: map[string]string{api.PodDeletionCost: "100"}},
   972  			featureEnabled:                  false,
   973  			wantAllowInvalidPodDeletionCost: true,
   974  		},
   975  		{
   976  			name:                            "UpdateFeatureEnabledValidOldValue",
   977  			oldPodMeta:                      &metav1.ObjectMeta{Annotations: map[string]string{api.PodDeletionCost: "100"}},
   978  			featureEnabled:                  true,
   979  			wantAllowInvalidPodDeletionCost: false,
   980  		},
   981  		{
   982  			name:                            "UpdateFeatureEnabledValidOldValue",
   983  			oldPodMeta:                      &metav1.ObjectMeta{Annotations: map[string]string{api.PodDeletionCost: "invalid-value"}},
   984  			featureEnabled:                  true,
   985  			wantAllowInvalidPodDeletionCost: true,
   986  		},
   987  	}
   988  
   989  	for _, tc := range testCases {
   990  		t.Run(tc.name, func(t *testing.T) {
   991  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodDeletionCost, tc.featureEnabled)()
   992  			// The new pod doesn't impact the outcome.
   993  			gotOptions := GetValidationOptionsFromPodSpecAndMeta(nil, nil, nil, tc.oldPodMeta)
   994  			if tc.wantAllowInvalidPodDeletionCost != gotOptions.AllowInvalidPodDeletionCost {
   995  				t.Errorf("unexpected diff, want: %v, got: %v", tc.wantAllowInvalidPodDeletionCost, gotOptions.AllowInvalidPodDeletionCost)
   996  			}
   997  		})
   998  	}
   999  }
  1000  
  1001  func TestDropDisabledTopologySpreadConstraintsFields(t *testing.T) {
  1002  	testCases := []struct {
  1003  		name        string
  1004  		enabled     bool
  1005  		podSpec     *api.PodSpec
  1006  		oldPodSpec  *api.PodSpec
  1007  		wantPodSpec *api.PodSpec
  1008  	}{
  1009  		{
  1010  			name:        "TopologySpreadConstraints is nil",
  1011  			podSpec:     &api.PodSpec{},
  1012  			oldPodSpec:  &api.PodSpec{},
  1013  			wantPodSpec: &api.PodSpec{},
  1014  		},
  1015  		{
  1016  			name:        "TopologySpreadConstraints is empty",
  1017  			podSpec:     &api.PodSpec{TopologySpreadConstraints: []api.TopologySpreadConstraint{}},
  1018  			oldPodSpec:  &api.PodSpec{TopologySpreadConstraints: []api.TopologySpreadConstraint{}},
  1019  			wantPodSpec: &api.PodSpec{TopologySpreadConstraints: []api.TopologySpreadConstraint{}},
  1020  		},
  1021  		{
  1022  			name: "TopologySpreadConstraints is not empty, but all constraints don't have minDomains",
  1023  			podSpec: &api.PodSpec{TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1024  				{
  1025  					MinDomains: nil,
  1026  				},
  1027  				{
  1028  					MinDomains: nil,
  1029  				},
  1030  			}},
  1031  			oldPodSpec: &api.PodSpec{TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1032  				{
  1033  					MinDomains: nil,
  1034  				},
  1035  				{
  1036  					MinDomains: nil,
  1037  				},
  1038  			}},
  1039  			wantPodSpec: &api.PodSpec{TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1040  				{
  1041  					MinDomains: nil,
  1042  				},
  1043  				{
  1044  					MinDomains: nil,
  1045  				},
  1046  			}},
  1047  		},
  1048  		{
  1049  			name: "one constraint in podSpec has non-empty minDomains, feature gate is disabled " +
  1050  				"and all constraint in oldPodSpec doesn't have minDomains",
  1051  			enabled: false,
  1052  			podSpec: &api.PodSpec{
  1053  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1054  					{
  1055  						MinDomains: pointer.Int32(2),
  1056  					},
  1057  					{
  1058  						MinDomains: nil,
  1059  					},
  1060  				},
  1061  			},
  1062  			oldPodSpec: &api.PodSpec{
  1063  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1064  					{
  1065  						MinDomains: nil,
  1066  					},
  1067  					{
  1068  						MinDomains: nil,
  1069  					},
  1070  				},
  1071  			},
  1072  			wantPodSpec: &api.PodSpec{
  1073  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1074  					{
  1075  						// cleared.
  1076  						MinDomains: nil,
  1077  					},
  1078  					{
  1079  						MinDomains: nil,
  1080  					},
  1081  				},
  1082  			},
  1083  		},
  1084  		{
  1085  			name: "one constraint in podSpec has non-empty minDomains, feature gate is disabled " +
  1086  				"and one constraint in oldPodSpec has minDomains",
  1087  			enabled: false,
  1088  			podSpec: &api.PodSpec{
  1089  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1090  					{
  1091  						MinDomains: pointer.Int32(2),
  1092  					},
  1093  					{
  1094  						MinDomains: nil,
  1095  					},
  1096  				},
  1097  			},
  1098  			oldPodSpec: &api.PodSpec{
  1099  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1100  					{
  1101  						MinDomains: pointer.Int32(2),
  1102  					},
  1103  					{
  1104  						MinDomains: nil,
  1105  					},
  1106  				},
  1107  			},
  1108  			wantPodSpec: &api.PodSpec{
  1109  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1110  					{
  1111  						// not cleared.
  1112  						MinDomains: pointer.Int32(2),
  1113  					},
  1114  					{
  1115  						MinDomains: nil,
  1116  					},
  1117  				},
  1118  			},
  1119  		},
  1120  		{
  1121  			name: "one constraint in podSpec has non-empty minDomains, feature gate is enabled" +
  1122  				"and all constraint in oldPodSpec doesn't have minDomains",
  1123  			enabled: true,
  1124  			podSpec: &api.PodSpec{
  1125  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1126  					{
  1127  						MinDomains: pointer.Int32(2),
  1128  					},
  1129  					{
  1130  						MinDomains: nil,
  1131  					},
  1132  				},
  1133  			},
  1134  			oldPodSpec: &api.PodSpec{
  1135  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1136  					{
  1137  						MinDomains: nil,
  1138  					},
  1139  					{
  1140  						MinDomains: nil,
  1141  					},
  1142  				},
  1143  			},
  1144  			wantPodSpec: &api.PodSpec{
  1145  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1146  					{
  1147  						// not cleared.
  1148  						MinDomains: pointer.Int32(2),
  1149  					},
  1150  					{
  1151  						MinDomains: nil,
  1152  					},
  1153  				},
  1154  			},
  1155  		},
  1156  	}
  1157  	for _, tc := range testCases {
  1158  		t.Run(tc.name, func(t *testing.T) {
  1159  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MinDomainsInPodTopologySpread, tc.enabled)()
  1160  			dropDisabledFields(tc.podSpec, nil, tc.oldPodSpec, nil)
  1161  			if diff := cmp.Diff(tc.wantPodSpec, tc.podSpec); diff != "" {
  1162  				t.Errorf("unexpected pod spec (-want, +got):\n%s", diff)
  1163  			}
  1164  		})
  1165  	}
  1166  }
  1167  
  1168  func TestDropDisabledPodStatusFields(t *testing.T) {
  1169  	podWithHostIPs := func() *api.PodStatus {
  1170  		return &api.PodStatus{
  1171  			HostIPs: makeHostIPs("10.0.0.1", "fd00:10::1"),
  1172  		}
  1173  	}
  1174  
  1175  	podWithoutHostIPs := func() *api.PodStatus {
  1176  		return &api.PodStatus{
  1177  			HostIPs: nil,
  1178  		}
  1179  	}
  1180  
  1181  	tests := []struct {
  1182  		name           string
  1183  		podStatus      *api.PodStatus
  1184  		oldPodStatus   *api.PodStatus
  1185  		wantPodStatus  *api.PodStatus
  1186  		featureEnabled bool
  1187  	}{
  1188  		{
  1189  			name:           "gate off, old=without, new=without",
  1190  			oldPodStatus:   podWithoutHostIPs(),
  1191  			podStatus:      podWithoutHostIPs(),
  1192  			featureEnabled: false,
  1193  
  1194  			wantPodStatus: podWithoutHostIPs(),
  1195  		},
  1196  		{
  1197  			name:           "gate off, old=without, new=with",
  1198  			oldPodStatus:   podWithoutHostIPs(),
  1199  			podStatus:      podWithHostIPs(),
  1200  			featureEnabled: false,
  1201  
  1202  			wantPodStatus: podWithoutHostIPs(),
  1203  		},
  1204  		{
  1205  			name:           "gate off, old=with, new=without",
  1206  			oldPodStatus:   podWithHostIPs(),
  1207  			podStatus:      podWithoutHostIPs(),
  1208  			featureEnabled: false,
  1209  
  1210  			wantPodStatus: podWithoutHostIPs(),
  1211  		},
  1212  		{
  1213  			name:           "gate off, old=with, new=with",
  1214  			oldPodStatus:   podWithHostIPs(),
  1215  			podStatus:      podWithHostIPs(),
  1216  			featureEnabled: false,
  1217  
  1218  			wantPodStatus: podWithHostIPs(),
  1219  		},
  1220  		{
  1221  			name:           "gate on, old=without, new=without",
  1222  			oldPodStatus:   podWithoutHostIPs(),
  1223  			podStatus:      podWithoutHostIPs(),
  1224  			featureEnabled: true,
  1225  
  1226  			wantPodStatus: podWithoutHostIPs(),
  1227  		},
  1228  		{
  1229  			name:           "gate on, old=without, new=with",
  1230  			oldPodStatus:   podWithoutHostIPs(),
  1231  			podStatus:      podWithHostIPs(),
  1232  			featureEnabled: true,
  1233  
  1234  			wantPodStatus: podWithHostIPs(),
  1235  		},
  1236  		{
  1237  			name:           "gate on, old=with, new=without",
  1238  			oldPodStatus:   podWithHostIPs(),
  1239  			podStatus:      podWithoutHostIPs(),
  1240  			featureEnabled: true,
  1241  
  1242  			wantPodStatus: podWithoutHostIPs(),
  1243  		},
  1244  		{
  1245  			name:           "gate on, old=with, new=with",
  1246  			oldPodStatus:   podWithHostIPs(),
  1247  			podStatus:      podWithHostIPs(),
  1248  			featureEnabled: true,
  1249  
  1250  			wantPodStatus: podWithHostIPs(),
  1251  		},
  1252  	}
  1253  	for _, tt := range tests {
  1254  		t.Run(tt.name, func(t *testing.T) {
  1255  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodHostIPs, tt.featureEnabled)()
  1256  
  1257  			dropDisabledPodStatusFields(tt.podStatus, tt.oldPodStatus, &api.PodSpec{}, &api.PodSpec{})
  1258  
  1259  			if !reflect.DeepEqual(tt.podStatus, tt.wantPodStatus) {
  1260  				t.Errorf("dropDisabledStatusFields() = %v, want %v", tt.podStatus, tt.wantPodStatus)
  1261  			}
  1262  		})
  1263  	}
  1264  }
  1265  
  1266  func makeHostIPs(ips ...string) []api.HostIP {
  1267  	ret := []api.HostIP{}
  1268  	for _, ip := range ips {
  1269  		ret = append(ret, api.HostIP{IP: ip})
  1270  	}
  1271  	return ret
  1272  }
  1273  
  1274  func TestDropNodeInclusionPolicyFields(t *testing.T) {
  1275  	ignore := api.NodeInclusionPolicyIgnore
  1276  	honor := api.NodeInclusionPolicyHonor
  1277  
  1278  	tests := []struct {
  1279  		name        string
  1280  		enabled     bool
  1281  		podSpec     *api.PodSpec
  1282  		oldPodSpec  *api.PodSpec
  1283  		wantPodSpec *api.PodSpec
  1284  	}{
  1285  		{
  1286  			name:    "feature disabled, both pods don't use the fields",
  1287  			enabled: false,
  1288  			oldPodSpec: &api.PodSpec{
  1289  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  1290  			},
  1291  			podSpec: &api.PodSpec{
  1292  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  1293  			},
  1294  			wantPodSpec: &api.PodSpec{
  1295  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  1296  			},
  1297  		},
  1298  		{
  1299  			name:    "feature disabled, only old pod use NodeAffinityPolicy field",
  1300  			enabled: false,
  1301  			oldPodSpec: &api.PodSpec{
  1302  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1303  					{NodeAffinityPolicy: &honor},
  1304  				},
  1305  			},
  1306  			podSpec: &api.PodSpec{
  1307  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  1308  			},
  1309  			wantPodSpec: &api.PodSpec{
  1310  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  1311  			},
  1312  		},
  1313  		{
  1314  			name:    "feature disabled, only old pod use NodeTaintsPolicy field",
  1315  			enabled: false,
  1316  			oldPodSpec: &api.PodSpec{
  1317  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1318  					{NodeTaintsPolicy: &ignore},
  1319  				},
  1320  			},
  1321  			podSpec: &api.PodSpec{
  1322  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  1323  			},
  1324  			wantPodSpec: &api.PodSpec{
  1325  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  1326  			},
  1327  		},
  1328  		{
  1329  			name:    "feature disabled, only current pod use NodeAffinityPolicy field",
  1330  			enabled: false,
  1331  			oldPodSpec: &api.PodSpec{
  1332  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  1333  			},
  1334  			podSpec: &api.PodSpec{
  1335  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1336  					{NodeAffinityPolicy: &honor},
  1337  				},
  1338  			},
  1339  			wantPodSpec: &api.PodSpec{
  1340  				TopologySpreadConstraints: []api.TopologySpreadConstraint{{
  1341  					NodeAffinityPolicy: nil,
  1342  				}},
  1343  			},
  1344  		},
  1345  		{
  1346  			name:    "feature disabled, only current pod use NodeTaintsPolicy field",
  1347  			enabled: false,
  1348  			oldPodSpec: &api.PodSpec{
  1349  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  1350  			},
  1351  			podSpec: &api.PodSpec{
  1352  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1353  					{NodeTaintsPolicy: &ignore},
  1354  				},
  1355  			},
  1356  			wantPodSpec: &api.PodSpec{
  1357  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1358  					{NodeTaintsPolicy: nil},
  1359  				},
  1360  			},
  1361  		},
  1362  		{
  1363  			name:    "feature disabled, both pods use NodeAffinityPolicy fields",
  1364  			enabled: false,
  1365  			oldPodSpec: &api.PodSpec{
  1366  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1367  					{NodeAffinityPolicy: &honor},
  1368  				},
  1369  			},
  1370  			podSpec: &api.PodSpec{
  1371  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1372  					{NodeAffinityPolicy: &ignore},
  1373  				},
  1374  			},
  1375  			wantPodSpec: &api.PodSpec{
  1376  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1377  					{NodeAffinityPolicy: &ignore},
  1378  				},
  1379  			},
  1380  		},
  1381  		{
  1382  			name:    "feature disabled, both pods use NodeTaintsPolicy fields",
  1383  			enabled: false,
  1384  			oldPodSpec: &api.PodSpec{
  1385  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1386  					{NodeTaintsPolicy: &ignore},
  1387  				},
  1388  			},
  1389  			podSpec: &api.PodSpec{
  1390  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1391  					{NodeTaintsPolicy: &honor},
  1392  				},
  1393  			},
  1394  			wantPodSpec: &api.PodSpec{
  1395  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1396  					{NodeTaintsPolicy: &honor},
  1397  				},
  1398  			},
  1399  		},
  1400  		{
  1401  			name:    "feature enabled, both pods use the fields",
  1402  			enabled: true,
  1403  			oldPodSpec: &api.PodSpec{
  1404  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1405  					{
  1406  						NodeAffinityPolicy: &ignore,
  1407  						NodeTaintsPolicy:   &honor,
  1408  					},
  1409  				},
  1410  			},
  1411  			podSpec: &api.PodSpec{
  1412  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1413  					{
  1414  						NodeAffinityPolicy: &honor,
  1415  						NodeTaintsPolicy:   &ignore,
  1416  					},
  1417  				},
  1418  			},
  1419  			wantPodSpec: &api.PodSpec{
  1420  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1421  					{
  1422  						NodeAffinityPolicy: &honor,
  1423  						NodeTaintsPolicy:   &ignore,
  1424  					},
  1425  				},
  1426  			},
  1427  		},
  1428  		{
  1429  			name:    "feature enabled, only old pod use NodeAffinityPolicy field",
  1430  			enabled: true,
  1431  			oldPodSpec: &api.PodSpec{
  1432  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1433  					{
  1434  						NodeAffinityPolicy: &honor,
  1435  					},
  1436  				},
  1437  			},
  1438  			podSpec: &api.PodSpec{
  1439  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  1440  			},
  1441  			wantPodSpec: &api.PodSpec{
  1442  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  1443  			},
  1444  		},
  1445  		{
  1446  			name:    "feature enabled, only old pod use NodeTaintsPolicy field",
  1447  			enabled: true,
  1448  			oldPodSpec: &api.PodSpec{
  1449  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1450  					{
  1451  						NodeTaintsPolicy: &ignore,
  1452  					},
  1453  				},
  1454  			},
  1455  			podSpec: &api.PodSpec{
  1456  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  1457  			},
  1458  			wantPodSpec: &api.PodSpec{
  1459  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  1460  			},
  1461  		},
  1462  		{
  1463  			name:    "feature enabled, only current pod use NodeAffinityPolicy field",
  1464  			enabled: true,
  1465  			oldPodSpec: &api.PodSpec{
  1466  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  1467  			},
  1468  			podSpec: &api.PodSpec{
  1469  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1470  					{
  1471  						NodeAffinityPolicy: &ignore,
  1472  					},
  1473  				},
  1474  			},
  1475  			wantPodSpec: &api.PodSpec{
  1476  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1477  					{
  1478  						NodeAffinityPolicy: &ignore,
  1479  					},
  1480  				},
  1481  			},
  1482  		},
  1483  		{
  1484  			name:    "feature enabled, only current pod use NodeTaintsPolicy field",
  1485  			enabled: true,
  1486  			oldPodSpec: &api.PodSpec{
  1487  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  1488  			},
  1489  			podSpec: &api.PodSpec{
  1490  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1491  					{
  1492  						NodeTaintsPolicy: &honor,
  1493  					},
  1494  				},
  1495  			},
  1496  			wantPodSpec: &api.PodSpec{
  1497  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  1498  					{
  1499  						NodeTaintsPolicy: &honor,
  1500  					},
  1501  				},
  1502  			},
  1503  		},
  1504  	}
  1505  
  1506  	for _, test := range tests {
  1507  		t.Run(test.name, func(t *testing.T) {
  1508  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.NodeInclusionPolicyInPodTopologySpread, test.enabled)()
  1509  
  1510  			dropDisabledFields(test.podSpec, nil, test.oldPodSpec, nil)
  1511  			if diff := cmp.Diff(test.wantPodSpec, test.podSpec); diff != "" {
  1512  				t.Errorf("unexpected pod spec (-want, +got):\n%s", diff)
  1513  			}
  1514  		})
  1515  	}
  1516  }
  1517  
  1518  func Test_dropDisabledMatchLabelKeysFieldInPodAffinity(t *testing.T) {
  1519  	tests := []struct {
  1520  		name        string
  1521  		enabled     bool
  1522  		podSpec     *api.PodSpec
  1523  		oldPodSpec  *api.PodSpec
  1524  		wantPodSpec *api.PodSpec
  1525  	}{
  1526  		{
  1527  			name:    "[PodAffinity/required] feature disabled, both pods don't use MatchLabelKeys/MismatchLabelKeys fields",
  1528  			enabled: false,
  1529  			oldPodSpec: &api.PodSpec{
  1530  				Affinity: &api.Affinity{
  1531  					PodAffinity: &api.PodAffinity{
  1532  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  1533  					},
  1534  				},
  1535  			},
  1536  			podSpec: &api.PodSpec{
  1537  				Affinity: &api.Affinity{
  1538  					PodAffinity: &api.PodAffinity{
  1539  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  1540  					},
  1541  				},
  1542  			},
  1543  			wantPodSpec: &api.PodSpec{
  1544  				Affinity: &api.Affinity{
  1545  					PodAffinity: &api.PodAffinity{
  1546  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  1547  					},
  1548  				},
  1549  			},
  1550  		},
  1551  		{
  1552  			name:    "[PodAffinity/required] feature disabled, only old pod uses MatchLabelKeys/MismatchLabelKeys field",
  1553  			enabled: false,
  1554  			oldPodSpec: &api.PodSpec{
  1555  				Affinity: &api.Affinity{
  1556  					PodAffinity: &api.PodAffinity{
  1557  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  1558  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1559  						},
  1560  					},
  1561  				},
  1562  			},
  1563  			podSpec: &api.PodSpec{
  1564  				Affinity: &api.Affinity{
  1565  					PodAffinity: &api.PodAffinity{
  1566  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  1567  					},
  1568  				},
  1569  			},
  1570  			wantPodSpec: &api.PodSpec{
  1571  				Affinity: &api.Affinity{
  1572  					PodAffinity: &api.PodAffinity{
  1573  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  1574  					},
  1575  				},
  1576  			},
  1577  		},
  1578  		{
  1579  			name:    "[PodAffinity/required] feature disabled, only current pod uses MatchLabelKeys/MismatchLabelKeys field",
  1580  			enabled: false,
  1581  			oldPodSpec: &api.PodSpec{
  1582  				Affinity: &api.Affinity{
  1583  					PodAffinity: &api.PodAffinity{
  1584  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  1585  					},
  1586  				},
  1587  			},
  1588  			podSpec: &api.PodSpec{
  1589  				Affinity: &api.Affinity{
  1590  					PodAffinity: &api.PodAffinity{
  1591  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  1592  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1593  						},
  1594  					},
  1595  				},
  1596  			},
  1597  			wantPodSpec: &api.PodSpec{
  1598  				Affinity: &api.Affinity{
  1599  					PodAffinity: &api.PodAffinity{
  1600  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{{}},
  1601  					},
  1602  				},
  1603  			},
  1604  		},
  1605  		{
  1606  			name:    "[PodAffinity/required] feature disabled, both pods use MatchLabelKeys/MismatchLabelKeys fields",
  1607  			enabled: false,
  1608  			oldPodSpec: &api.PodSpec{
  1609  				Affinity: &api.Affinity{
  1610  					PodAffinity: &api.PodAffinity{
  1611  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  1612  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1613  						},
  1614  					},
  1615  				},
  1616  			},
  1617  			podSpec: &api.PodSpec{
  1618  				Affinity: &api.Affinity{
  1619  					PodAffinity: &api.PodAffinity{
  1620  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  1621  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1622  						},
  1623  					},
  1624  				},
  1625  			},
  1626  			wantPodSpec: &api.PodSpec{
  1627  				Affinity: &api.Affinity{
  1628  					PodAffinity: &api.PodAffinity{
  1629  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  1630  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1631  						},
  1632  					},
  1633  				},
  1634  			},
  1635  		},
  1636  		{
  1637  			name:    "[PodAffinity/required] feature enabled, only old pod uses MatchLabelKeys/MismatchLabelKeys field",
  1638  			enabled: true,
  1639  			oldPodSpec: &api.PodSpec{
  1640  				Affinity: &api.Affinity{
  1641  					PodAffinity: &api.PodAffinity{
  1642  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  1643  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1644  						},
  1645  					},
  1646  				},
  1647  			},
  1648  			podSpec: &api.PodSpec{
  1649  				Affinity: &api.Affinity{
  1650  					PodAffinity: &api.PodAffinity{
  1651  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  1652  					},
  1653  				},
  1654  			},
  1655  			wantPodSpec: &api.PodSpec{
  1656  				Affinity: &api.Affinity{
  1657  					PodAffinity: &api.PodAffinity{
  1658  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  1659  					},
  1660  				},
  1661  			},
  1662  		},
  1663  		{
  1664  			name:    "[PodAffinity/required] feature enabled, only current pod uses MatchLabelKeys/MismatchLabelKeys field",
  1665  			enabled: true,
  1666  			oldPodSpec: &api.PodSpec{
  1667  				Affinity: &api.Affinity{
  1668  					PodAffinity: &api.PodAffinity{
  1669  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  1670  					},
  1671  				},
  1672  			},
  1673  			podSpec: &api.PodSpec{
  1674  				Affinity: &api.Affinity{
  1675  					PodAffinity: &api.PodAffinity{
  1676  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  1677  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1678  						},
  1679  					},
  1680  				},
  1681  			},
  1682  			wantPodSpec: &api.PodSpec{
  1683  				Affinity: &api.Affinity{
  1684  					PodAffinity: &api.PodAffinity{
  1685  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  1686  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1687  						},
  1688  					},
  1689  				},
  1690  			},
  1691  		},
  1692  		{
  1693  			name:    "[PodAffinity/required] feature enabled, both pods use MatchLabelKeys/MismatchLabelKeys fields",
  1694  			enabled: false,
  1695  			oldPodSpec: &api.PodSpec{
  1696  				Affinity: &api.Affinity{
  1697  					PodAffinity: &api.PodAffinity{
  1698  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  1699  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1700  						},
  1701  					},
  1702  				},
  1703  			},
  1704  			podSpec: &api.PodSpec{
  1705  				Affinity: &api.Affinity{
  1706  					PodAffinity: &api.PodAffinity{
  1707  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  1708  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1709  						},
  1710  					},
  1711  				},
  1712  			},
  1713  			wantPodSpec: &api.PodSpec{
  1714  				Affinity: &api.Affinity{
  1715  					PodAffinity: &api.PodAffinity{
  1716  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  1717  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1718  						},
  1719  					},
  1720  				},
  1721  			},
  1722  		},
  1723  		{
  1724  			name:    "[PodAffinity/preferred] feature disabled, both pods don't use MatchLabelKeys/MismatchLabelKeys fields",
  1725  			enabled: false,
  1726  			oldPodSpec: &api.PodSpec{
  1727  				Affinity: &api.Affinity{
  1728  					PodAffinity: &api.PodAffinity{
  1729  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  1730  					},
  1731  				},
  1732  			},
  1733  			podSpec: &api.PodSpec{
  1734  				Affinity: &api.Affinity{
  1735  					PodAffinity: &api.PodAffinity{
  1736  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  1737  					},
  1738  				},
  1739  			},
  1740  			wantPodSpec: &api.PodSpec{
  1741  				Affinity: &api.Affinity{
  1742  					PodAffinity: &api.PodAffinity{
  1743  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  1744  					},
  1745  				},
  1746  			},
  1747  		},
  1748  		{
  1749  			name:    "[PodAffinity/preferred] feature disabled, only old pod uses MatchLabelKeys/MismatchLabelKeys field",
  1750  			enabled: false,
  1751  			oldPodSpec: &api.PodSpec{
  1752  				Affinity: &api.Affinity{
  1753  					PodAffinity: &api.PodAffinity{
  1754  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  1755  							{
  1756  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1757  							},
  1758  						},
  1759  					},
  1760  				},
  1761  			},
  1762  			podSpec: &api.PodSpec{
  1763  				Affinity: &api.Affinity{
  1764  					PodAffinity: &api.PodAffinity{
  1765  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  1766  					},
  1767  				},
  1768  			},
  1769  			wantPodSpec: &api.PodSpec{
  1770  				Affinity: &api.Affinity{
  1771  					PodAffinity: &api.PodAffinity{
  1772  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  1773  					},
  1774  				},
  1775  			},
  1776  		},
  1777  		{
  1778  			name:    "[PodAffinity/preferred] feature disabled, only current pod uses MatchLabelKeys/MismatchLabelKeys field",
  1779  			enabled: false,
  1780  			oldPodSpec: &api.PodSpec{
  1781  				Affinity: &api.Affinity{
  1782  					PodAffinity: &api.PodAffinity{
  1783  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  1784  					},
  1785  				},
  1786  			},
  1787  			podSpec: &api.PodSpec{
  1788  				Affinity: &api.Affinity{
  1789  					PodAffinity: &api.PodAffinity{
  1790  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  1791  							{
  1792  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1793  							},
  1794  						},
  1795  					},
  1796  				},
  1797  			},
  1798  			wantPodSpec: &api.PodSpec{
  1799  				Affinity: &api.Affinity{
  1800  					PodAffinity: &api.PodAffinity{
  1801  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{{}},
  1802  					},
  1803  				},
  1804  			},
  1805  		},
  1806  		{
  1807  			name:    "[PodAffinity/preferred] feature disabled, both pods use MatchLabelKeys/MismatchLabelKeys fields",
  1808  			enabled: false,
  1809  			oldPodSpec: &api.PodSpec{
  1810  				Affinity: &api.Affinity{
  1811  					PodAffinity: &api.PodAffinity{
  1812  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  1813  							{
  1814  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1815  							},
  1816  						},
  1817  					},
  1818  				},
  1819  			},
  1820  			podSpec: &api.PodSpec{
  1821  				Affinity: &api.Affinity{
  1822  					PodAffinity: &api.PodAffinity{
  1823  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  1824  							{
  1825  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1826  							},
  1827  						},
  1828  					},
  1829  				},
  1830  			},
  1831  			wantPodSpec: &api.PodSpec{
  1832  				Affinity: &api.Affinity{
  1833  					PodAffinity: &api.PodAffinity{
  1834  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  1835  							{
  1836  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1837  							},
  1838  						},
  1839  					},
  1840  				},
  1841  			},
  1842  		},
  1843  		{
  1844  			name:    "[PodAffinity/preferred] feature enabled, only old pod uses MatchLabelKeys/MismatchLabelKeys field",
  1845  			enabled: true,
  1846  			oldPodSpec: &api.PodSpec{
  1847  				Affinity: &api.Affinity{
  1848  					PodAffinity: &api.PodAffinity{
  1849  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  1850  							{
  1851  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1852  							},
  1853  						},
  1854  					},
  1855  				},
  1856  			},
  1857  			podSpec: &api.PodSpec{
  1858  				Affinity: &api.Affinity{
  1859  					PodAffinity: &api.PodAffinity{
  1860  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  1861  					},
  1862  				},
  1863  			},
  1864  			wantPodSpec: &api.PodSpec{
  1865  				Affinity: &api.Affinity{
  1866  					PodAffinity: &api.PodAffinity{
  1867  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  1868  					},
  1869  				},
  1870  			},
  1871  		},
  1872  		{
  1873  			name:    "[PodAffinity/preferred] feature enabled, only current pod uses MatchLabelKeys/MismatchLabelKeys field",
  1874  			enabled: true,
  1875  			oldPodSpec: &api.PodSpec{
  1876  				Affinity: &api.Affinity{
  1877  					PodAffinity: &api.PodAffinity{
  1878  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  1879  					},
  1880  				},
  1881  			},
  1882  			podSpec: &api.PodSpec{
  1883  				Affinity: &api.Affinity{
  1884  					PodAffinity: &api.PodAffinity{
  1885  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  1886  							{
  1887  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1888  							},
  1889  						},
  1890  					},
  1891  				},
  1892  			},
  1893  			wantPodSpec: &api.PodSpec{
  1894  				Affinity: &api.Affinity{
  1895  					PodAffinity: &api.PodAffinity{
  1896  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  1897  							{
  1898  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1899  							},
  1900  						},
  1901  					},
  1902  				},
  1903  			},
  1904  		},
  1905  		{
  1906  			name:    "[PodAffinity/preferred] feature enabled, both pods use MatchLabelKeys/MismatchLabelKeys fields",
  1907  			enabled: false,
  1908  			oldPodSpec: &api.PodSpec{
  1909  				Affinity: &api.Affinity{
  1910  					PodAffinity: &api.PodAffinity{
  1911  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  1912  							{
  1913  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1914  							},
  1915  						},
  1916  					},
  1917  				},
  1918  			},
  1919  			podSpec: &api.PodSpec{
  1920  				Affinity: &api.Affinity{
  1921  					PodAffinity: &api.PodAffinity{
  1922  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  1923  							{
  1924  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1925  							},
  1926  						},
  1927  					},
  1928  				},
  1929  			},
  1930  			wantPodSpec: &api.PodSpec{
  1931  				Affinity: &api.Affinity{
  1932  					PodAffinity: &api.PodAffinity{
  1933  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  1934  							{
  1935  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1936  							},
  1937  						},
  1938  					},
  1939  				},
  1940  			},
  1941  		},
  1942  		{
  1943  			name:    "[PodAntiAffinity/required] feature disabled, both pods don't use MatchLabelKeys/MismatchLabelKeys fields",
  1944  			enabled: false,
  1945  			oldPodSpec: &api.PodSpec{
  1946  				Affinity: &api.Affinity{
  1947  					PodAntiAffinity: &api.PodAntiAffinity{
  1948  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  1949  					},
  1950  				},
  1951  			},
  1952  			podSpec: &api.PodSpec{
  1953  				Affinity: &api.Affinity{
  1954  					PodAntiAffinity: &api.PodAntiAffinity{
  1955  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  1956  					},
  1957  				},
  1958  			},
  1959  			wantPodSpec: &api.PodSpec{
  1960  				Affinity: &api.Affinity{
  1961  					PodAntiAffinity: &api.PodAntiAffinity{
  1962  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  1963  					},
  1964  				},
  1965  			},
  1966  		},
  1967  		{
  1968  			name:    "[PodAntiAffinity/required] feature disabled, only old pod uses MatchLabelKeys/MismatchLabelKeys field",
  1969  			enabled: false,
  1970  			oldPodSpec: &api.PodSpec{
  1971  				Affinity: &api.Affinity{
  1972  					PodAntiAffinity: &api.PodAntiAffinity{
  1973  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  1974  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  1975  						},
  1976  					},
  1977  				},
  1978  			},
  1979  			podSpec: &api.PodSpec{
  1980  				Affinity: &api.Affinity{
  1981  					PodAntiAffinity: &api.PodAntiAffinity{
  1982  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  1983  					},
  1984  				},
  1985  			},
  1986  			wantPodSpec: &api.PodSpec{
  1987  				Affinity: &api.Affinity{
  1988  					PodAntiAffinity: &api.PodAntiAffinity{
  1989  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  1990  					},
  1991  				},
  1992  			},
  1993  		},
  1994  		{
  1995  			name:    "[PodAntiAffinity/required] feature disabled, only current pod uses MatchLabelKeys/MismatchLabelKeys field",
  1996  			enabled: false,
  1997  			oldPodSpec: &api.PodSpec{
  1998  				Affinity: &api.Affinity{
  1999  					PodAntiAffinity: &api.PodAntiAffinity{
  2000  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  2001  					},
  2002  				},
  2003  			},
  2004  			podSpec: &api.PodSpec{
  2005  				Affinity: &api.Affinity{
  2006  					PodAntiAffinity: &api.PodAntiAffinity{
  2007  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  2008  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2009  						},
  2010  					},
  2011  				},
  2012  			},
  2013  			wantPodSpec: &api.PodSpec{
  2014  				Affinity: &api.Affinity{
  2015  					PodAntiAffinity: &api.PodAntiAffinity{
  2016  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{{}},
  2017  					},
  2018  				},
  2019  			},
  2020  		},
  2021  		{
  2022  			name:    "[PodAntiAffinity/required] feature disabled, both pods use MatchLabelKeys/MismatchLabelKeys fields",
  2023  			enabled: false,
  2024  			oldPodSpec: &api.PodSpec{
  2025  				Affinity: &api.Affinity{
  2026  					PodAntiAffinity: &api.PodAntiAffinity{
  2027  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  2028  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2029  						},
  2030  					},
  2031  				},
  2032  			},
  2033  			podSpec: &api.PodSpec{
  2034  				Affinity: &api.Affinity{
  2035  					PodAntiAffinity: &api.PodAntiAffinity{
  2036  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  2037  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2038  						},
  2039  					},
  2040  				},
  2041  			},
  2042  			wantPodSpec: &api.PodSpec{
  2043  				Affinity: &api.Affinity{
  2044  					PodAntiAffinity: &api.PodAntiAffinity{
  2045  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  2046  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2047  						},
  2048  					},
  2049  				},
  2050  			},
  2051  		},
  2052  		{
  2053  			name:    "[PodAntiAffinity/required] feature enabled, only old pod uses MatchLabelKeys/MismatchLabelKeys field",
  2054  			enabled: true,
  2055  			oldPodSpec: &api.PodSpec{
  2056  				Affinity: &api.Affinity{
  2057  					PodAntiAffinity: &api.PodAntiAffinity{
  2058  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  2059  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2060  						},
  2061  					},
  2062  				},
  2063  			},
  2064  			podSpec: &api.PodSpec{
  2065  				Affinity: &api.Affinity{
  2066  					PodAntiAffinity: &api.PodAntiAffinity{
  2067  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  2068  					},
  2069  				},
  2070  			},
  2071  			wantPodSpec: &api.PodSpec{
  2072  				Affinity: &api.Affinity{
  2073  					PodAntiAffinity: &api.PodAntiAffinity{
  2074  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  2075  					},
  2076  				},
  2077  			},
  2078  		},
  2079  		{
  2080  			name:    "[PodAntiAffinity/required] feature enabled, only current pod uses MatchLabelKeys/MismatchLabelKeys field",
  2081  			enabled: true,
  2082  			oldPodSpec: &api.PodSpec{
  2083  				Affinity: &api.Affinity{
  2084  					PodAntiAffinity: &api.PodAntiAffinity{
  2085  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{},
  2086  					},
  2087  				},
  2088  			},
  2089  			podSpec: &api.PodSpec{
  2090  				Affinity: &api.Affinity{
  2091  					PodAntiAffinity: &api.PodAntiAffinity{
  2092  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  2093  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2094  						},
  2095  					},
  2096  				},
  2097  			},
  2098  			wantPodSpec: &api.PodSpec{
  2099  				Affinity: &api.Affinity{
  2100  					PodAntiAffinity: &api.PodAntiAffinity{
  2101  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  2102  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2103  						},
  2104  					},
  2105  				},
  2106  			},
  2107  		},
  2108  		{
  2109  			name:    "[PodAntiAffinity/required] feature enabled, both pods use MatchLabelKeys/MismatchLabelKeys fields",
  2110  			enabled: false,
  2111  			oldPodSpec: &api.PodSpec{
  2112  				Affinity: &api.Affinity{
  2113  					PodAntiAffinity: &api.PodAntiAffinity{
  2114  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  2115  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2116  						},
  2117  					},
  2118  				},
  2119  			},
  2120  			podSpec: &api.PodSpec{
  2121  				Affinity: &api.Affinity{
  2122  					PodAntiAffinity: &api.PodAntiAffinity{
  2123  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  2124  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2125  						},
  2126  					},
  2127  				},
  2128  			},
  2129  			wantPodSpec: &api.PodSpec{
  2130  				Affinity: &api.Affinity{
  2131  					PodAntiAffinity: &api.PodAntiAffinity{
  2132  						RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{
  2133  							{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2134  						},
  2135  					},
  2136  				},
  2137  			},
  2138  		},
  2139  
  2140  		{
  2141  			name:    "[PodAntiAffinity/preferred] feature disabled, both pods don't use MatchLabelKeys/MismatchLabelKeys fields",
  2142  			enabled: false,
  2143  			oldPodSpec: &api.PodSpec{
  2144  				Affinity: &api.Affinity{
  2145  					PodAntiAffinity: &api.PodAntiAffinity{
  2146  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  2147  					},
  2148  				},
  2149  			},
  2150  			podSpec: &api.PodSpec{
  2151  				Affinity: &api.Affinity{
  2152  					PodAntiAffinity: &api.PodAntiAffinity{
  2153  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  2154  					},
  2155  				},
  2156  			},
  2157  			wantPodSpec: &api.PodSpec{
  2158  				Affinity: &api.Affinity{
  2159  					PodAntiAffinity: &api.PodAntiAffinity{
  2160  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  2161  					},
  2162  				},
  2163  			},
  2164  		},
  2165  		{
  2166  			name:    "[PodAntiAffinity/preferred] feature disabled, only old pod uses MatchLabelKeys/MismatchLabelKeys field",
  2167  			enabled: false,
  2168  			oldPodSpec: &api.PodSpec{
  2169  				Affinity: &api.Affinity{
  2170  					PodAntiAffinity: &api.PodAntiAffinity{
  2171  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  2172  							{
  2173  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2174  							},
  2175  						},
  2176  					},
  2177  				},
  2178  			},
  2179  			podSpec: &api.PodSpec{
  2180  				Affinity: &api.Affinity{
  2181  					PodAntiAffinity: &api.PodAntiAffinity{
  2182  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  2183  					},
  2184  				},
  2185  			},
  2186  			wantPodSpec: &api.PodSpec{
  2187  				Affinity: &api.Affinity{
  2188  					PodAntiAffinity: &api.PodAntiAffinity{
  2189  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  2190  					},
  2191  				},
  2192  			},
  2193  		},
  2194  		{
  2195  			name:    "[PodAntiAffinity/preferred] feature disabled, only current pod uses MatchLabelKeys/MismatchLabelKeys field",
  2196  			enabled: false,
  2197  			oldPodSpec: &api.PodSpec{
  2198  				Affinity: &api.Affinity{
  2199  					PodAntiAffinity: &api.PodAntiAffinity{
  2200  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  2201  					},
  2202  				},
  2203  			},
  2204  			podSpec: &api.PodSpec{
  2205  				Affinity: &api.Affinity{
  2206  					PodAntiAffinity: &api.PodAntiAffinity{
  2207  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  2208  							{
  2209  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2210  							},
  2211  						},
  2212  					},
  2213  				},
  2214  			},
  2215  			wantPodSpec: &api.PodSpec{
  2216  				Affinity: &api.Affinity{
  2217  					PodAntiAffinity: &api.PodAntiAffinity{
  2218  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{{}},
  2219  					},
  2220  				},
  2221  			},
  2222  		},
  2223  		{
  2224  			name:    "[PodAntiAffinity/preferred] feature disabled, both pods use MatchLabelKeys/MismatchLabelKeys fields",
  2225  			enabled: false,
  2226  			oldPodSpec: &api.PodSpec{
  2227  				Affinity: &api.Affinity{
  2228  					PodAntiAffinity: &api.PodAntiAffinity{
  2229  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  2230  							{
  2231  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2232  							},
  2233  						},
  2234  					},
  2235  				},
  2236  			},
  2237  			podSpec: &api.PodSpec{
  2238  				Affinity: &api.Affinity{
  2239  					PodAntiAffinity: &api.PodAntiAffinity{
  2240  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  2241  							{
  2242  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2243  							},
  2244  						},
  2245  					},
  2246  				},
  2247  			},
  2248  			wantPodSpec: &api.PodSpec{
  2249  				Affinity: &api.Affinity{
  2250  					PodAntiAffinity: &api.PodAntiAffinity{
  2251  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  2252  							{
  2253  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2254  							},
  2255  						},
  2256  					},
  2257  				},
  2258  			},
  2259  		},
  2260  		{
  2261  			name:    "[PodAntiAffinity/preferred] feature enabled, only old pod uses MatchLabelKeys/MismatchLabelKeys field",
  2262  			enabled: true,
  2263  			oldPodSpec: &api.PodSpec{
  2264  				Affinity: &api.Affinity{
  2265  					PodAntiAffinity: &api.PodAntiAffinity{
  2266  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  2267  							{
  2268  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2269  							},
  2270  						},
  2271  					},
  2272  				},
  2273  			},
  2274  			podSpec: &api.PodSpec{
  2275  				Affinity: &api.Affinity{
  2276  					PodAntiAffinity: &api.PodAntiAffinity{
  2277  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  2278  					},
  2279  				},
  2280  			},
  2281  			wantPodSpec: &api.PodSpec{
  2282  				Affinity: &api.Affinity{
  2283  					PodAntiAffinity: &api.PodAntiAffinity{
  2284  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  2285  					},
  2286  				},
  2287  			},
  2288  		},
  2289  		{
  2290  			name:    "[PodAntiAffinity/preferred] feature enabled, only current pod uses MatchLabelKeys/MismatchLabelKeys field",
  2291  			enabled: true,
  2292  			oldPodSpec: &api.PodSpec{
  2293  				Affinity: &api.Affinity{
  2294  					PodAntiAffinity: &api.PodAntiAffinity{
  2295  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{},
  2296  					},
  2297  				},
  2298  			},
  2299  			podSpec: &api.PodSpec{
  2300  				Affinity: &api.Affinity{
  2301  					PodAntiAffinity: &api.PodAntiAffinity{
  2302  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  2303  							{
  2304  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2305  							},
  2306  						},
  2307  					},
  2308  				},
  2309  			},
  2310  			wantPodSpec: &api.PodSpec{
  2311  				Affinity: &api.Affinity{
  2312  					PodAntiAffinity: &api.PodAntiAffinity{
  2313  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  2314  							{
  2315  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2316  							},
  2317  						},
  2318  					},
  2319  				},
  2320  			},
  2321  		},
  2322  		{
  2323  			name:    "[PodAntiAffinity/preferred] feature enabled, both pods use MatchLabelKeys/MismatchLabelKeys fields",
  2324  			enabled: false,
  2325  			oldPodSpec: &api.PodSpec{
  2326  				Affinity: &api.Affinity{
  2327  					PodAntiAffinity: &api.PodAntiAffinity{
  2328  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  2329  							{
  2330  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2331  							},
  2332  						},
  2333  					},
  2334  				},
  2335  			},
  2336  			podSpec: &api.PodSpec{
  2337  				Affinity: &api.Affinity{
  2338  					PodAntiAffinity: &api.PodAntiAffinity{
  2339  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  2340  							{
  2341  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2342  							},
  2343  						},
  2344  					},
  2345  				},
  2346  			},
  2347  			wantPodSpec: &api.PodSpec{
  2348  				Affinity: &api.Affinity{
  2349  					PodAntiAffinity: &api.PodAntiAffinity{
  2350  						PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{
  2351  							{
  2352  								PodAffinityTerm: api.PodAffinityTerm{MatchLabelKeys: []string{"foo"}, MismatchLabelKeys: []string{"foo"}},
  2353  							},
  2354  						},
  2355  					},
  2356  				},
  2357  			},
  2358  		},
  2359  	}
  2360  
  2361  	for _, test := range tests {
  2362  		t.Run(test.name, func(t *testing.T) {
  2363  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MatchLabelKeysInPodAffinity, test.enabled)()
  2364  
  2365  			dropDisabledFields(test.podSpec, nil, test.oldPodSpec, nil)
  2366  			if diff := cmp.Diff(test.wantPodSpec, test.podSpec); diff != "" {
  2367  				t.Errorf("unexpected pod spec (-want, +got):\n%s", diff)
  2368  			}
  2369  		})
  2370  	}
  2371  }
  2372  
  2373  func Test_dropDisabledMatchLabelKeysFieldInTopologySpread(t *testing.T) {
  2374  	tests := []struct {
  2375  		name        string
  2376  		enabled     bool
  2377  		podSpec     *api.PodSpec
  2378  		oldPodSpec  *api.PodSpec
  2379  		wantPodSpec *api.PodSpec
  2380  	}{
  2381  		{
  2382  			name:    "feature disabled, both pods don't use MatchLabelKeys fields",
  2383  			enabled: false,
  2384  			oldPodSpec: &api.PodSpec{
  2385  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  2386  			},
  2387  			podSpec: &api.PodSpec{
  2388  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  2389  			},
  2390  			wantPodSpec: &api.PodSpec{
  2391  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  2392  			},
  2393  		},
  2394  		{
  2395  			name:    "feature disabled, only old pod uses MatchLabelKeys field",
  2396  			enabled: false,
  2397  			oldPodSpec: &api.PodSpec{
  2398  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  2399  					{MatchLabelKeys: []string{"foo"}},
  2400  				},
  2401  			},
  2402  			podSpec: &api.PodSpec{
  2403  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  2404  			},
  2405  			wantPodSpec: &api.PodSpec{
  2406  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  2407  			},
  2408  		},
  2409  		{
  2410  			name:    "feature disabled, only current pod uses MatchLabelKeys field",
  2411  			enabled: false,
  2412  			oldPodSpec: &api.PodSpec{
  2413  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  2414  			},
  2415  			podSpec: &api.PodSpec{
  2416  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  2417  					{MatchLabelKeys: []string{"foo"}},
  2418  				},
  2419  			},
  2420  			wantPodSpec: &api.PodSpec{
  2421  				TopologySpreadConstraints: []api.TopologySpreadConstraint{{}},
  2422  			},
  2423  		},
  2424  		{
  2425  			name:    "feature disabled, both pods use MatchLabelKeys fields",
  2426  			enabled: false,
  2427  			oldPodSpec: &api.PodSpec{
  2428  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  2429  					{MatchLabelKeys: []string{"foo"}},
  2430  				},
  2431  			},
  2432  			podSpec: &api.PodSpec{
  2433  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  2434  					{MatchLabelKeys: []string{"foo"}},
  2435  				},
  2436  			},
  2437  			wantPodSpec: &api.PodSpec{
  2438  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  2439  					{MatchLabelKeys: []string{"foo"}},
  2440  				},
  2441  			},
  2442  		},
  2443  		{
  2444  			name:    "feature enabled, only old pod uses MatchLabelKeys field",
  2445  			enabled: true,
  2446  			oldPodSpec: &api.PodSpec{
  2447  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  2448  					{MatchLabelKeys: []string{"foo"}},
  2449  				},
  2450  			},
  2451  			podSpec: &api.PodSpec{
  2452  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  2453  			},
  2454  			wantPodSpec: &api.PodSpec{
  2455  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  2456  			},
  2457  		},
  2458  		{
  2459  			name:    "feature enabled, only current pod uses MatchLabelKeys field",
  2460  			enabled: true,
  2461  			oldPodSpec: &api.PodSpec{
  2462  				TopologySpreadConstraints: []api.TopologySpreadConstraint{},
  2463  			},
  2464  			podSpec: &api.PodSpec{
  2465  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  2466  					{MatchLabelKeys: []string{"foo"}},
  2467  				},
  2468  			},
  2469  			wantPodSpec: &api.PodSpec{
  2470  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  2471  					{MatchLabelKeys: []string{"foo"}},
  2472  				},
  2473  			},
  2474  		},
  2475  		{
  2476  			name:    "feature enabled, both pods use MatchLabelKeys fields",
  2477  			enabled: false,
  2478  			oldPodSpec: &api.PodSpec{
  2479  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  2480  					{MatchLabelKeys: []string{"foo"}},
  2481  				},
  2482  			},
  2483  			podSpec: &api.PodSpec{
  2484  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  2485  					{MatchLabelKeys: []string{"foo"}},
  2486  				},
  2487  			},
  2488  			wantPodSpec: &api.PodSpec{
  2489  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  2490  					{MatchLabelKeys: []string{"foo"}},
  2491  				},
  2492  			},
  2493  		},
  2494  	}
  2495  
  2496  	for _, test := range tests {
  2497  		t.Run(test.name, func(t *testing.T) {
  2498  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.MatchLabelKeysInPodTopologySpread, test.enabled)()
  2499  
  2500  			dropDisabledFields(test.podSpec, nil, test.oldPodSpec, nil)
  2501  			if diff := cmp.Diff(test.wantPodSpec, test.podSpec); diff != "" {
  2502  				t.Errorf("unexpected pod spec (-want, +got):\n%s", diff)
  2503  			}
  2504  		})
  2505  	}
  2506  }
  2507  
  2508  func TestDropHostUsers(t *testing.T) {
  2509  	falseVar := false
  2510  	trueVar := true
  2511  
  2512  	podWithoutHostUsers := func() *api.Pod {
  2513  		return &api.Pod{
  2514  			Spec: api.PodSpec{
  2515  				SecurityContext: &api.PodSecurityContext{}},
  2516  		}
  2517  	}
  2518  	podWithHostUsersFalse := func() *api.Pod {
  2519  		return &api.Pod{
  2520  			Spec: api.PodSpec{
  2521  				SecurityContext: &api.PodSecurityContext{
  2522  					HostUsers: &falseVar,
  2523  				},
  2524  			},
  2525  		}
  2526  	}
  2527  	podWithHostUsersTrue := func() *api.Pod {
  2528  		return &api.Pod{
  2529  			Spec: api.PodSpec{
  2530  				SecurityContext: &api.PodSecurityContext{
  2531  					HostUsers: &trueVar,
  2532  				},
  2533  			},
  2534  		}
  2535  	}
  2536  
  2537  	podInfo := []struct {
  2538  		description  string
  2539  		hasHostUsers bool
  2540  		pod          func() *api.Pod
  2541  	}{
  2542  		{
  2543  			description:  "with hostUsers=true",
  2544  			hasHostUsers: true,
  2545  			pod:          podWithHostUsersTrue,
  2546  		},
  2547  		{
  2548  			description:  "with hostUsers=false",
  2549  			hasHostUsers: true,
  2550  			pod:          podWithHostUsersFalse,
  2551  		},
  2552  		{
  2553  			description: "with hostUsers=nil",
  2554  			pod:         func() *api.Pod { return nil },
  2555  		},
  2556  	}
  2557  
  2558  	for _, enabled := range []bool{true, false} {
  2559  		for _, oldPodInfo := range podInfo {
  2560  			for _, newPodInfo := range podInfo {
  2561  				oldPodHasHostUsers, oldPod := oldPodInfo.hasHostUsers, oldPodInfo.pod()
  2562  				newPodHasHostUsers, newPod := newPodInfo.hasHostUsers, newPodInfo.pod()
  2563  				if newPod == nil {
  2564  					continue
  2565  				}
  2566  
  2567  				t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
  2568  					defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.UserNamespacesSupport, enabled)()
  2569  
  2570  					DropDisabledPodFields(newPod, oldPod)
  2571  
  2572  					// old pod should never be changed
  2573  					if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
  2574  						t.Errorf("old pod changed: %v", cmp.Diff(oldPod, oldPodInfo.pod()))
  2575  					}
  2576  
  2577  					switch {
  2578  					case enabled || oldPodHasHostUsers:
  2579  						// new pod should not be changed if the feature is enabled, or if the old pod had hostUsers
  2580  						if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  2581  							t.Errorf("new pod changed: %v", cmp.Diff(newPod, newPodInfo.pod()))
  2582  						}
  2583  					case newPodHasHostUsers:
  2584  						// new pod should be changed
  2585  						if reflect.DeepEqual(newPod, newPodInfo.pod()) {
  2586  							t.Errorf("new pod was not changed")
  2587  						}
  2588  						// new pod should not have hostUsers
  2589  						if exp := podWithoutHostUsers(); !reflect.DeepEqual(newPod, exp) {
  2590  							t.Errorf("new pod had hostUsers: %v", cmp.Diff(newPod, exp))
  2591  						}
  2592  					default:
  2593  						// new pod should not need to be changed
  2594  						if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  2595  							t.Errorf("new pod changed: %v", cmp.Diff(newPod, newPodInfo.pod()))
  2596  						}
  2597  					}
  2598  				})
  2599  			}
  2600  		}
  2601  	}
  2602  
  2603  }
  2604  
  2605  func TestDropSchedulingGates(t *testing.T) {
  2606  	podWithSchedulingGates := func() *api.Pod {
  2607  		return &api.Pod{
  2608  			Spec: api.PodSpec{
  2609  				SchedulingGates: []api.PodSchedulingGate{
  2610  					{Name: "foo"},
  2611  					{Name: "bar"},
  2612  				},
  2613  			},
  2614  		}
  2615  	}
  2616  	podWithoutSchedulingGates := func() *api.Pod { return &api.Pod{} }
  2617  
  2618  	podInfo := []struct {
  2619  		description             string
  2620  		hasSchedulingGatesField bool
  2621  		pod                     func() *api.Pod
  2622  	}{
  2623  		{
  2624  			description:             "has SchedulingGates field",
  2625  			hasSchedulingGatesField: true,
  2626  			pod:                     podWithSchedulingGates,
  2627  		},
  2628  		{
  2629  			description:             "does not have SchedulingGates field",
  2630  			hasSchedulingGatesField: false,
  2631  			pod:                     podWithoutSchedulingGates,
  2632  		},
  2633  		{
  2634  			description:             "is nil",
  2635  			hasSchedulingGatesField: false,
  2636  			pod:                     func() *api.Pod { return nil },
  2637  		},
  2638  	}
  2639  
  2640  	for _, enabled := range []bool{true, false} {
  2641  		for _, oldPodInfo := range podInfo {
  2642  			for _, newPodInfo := range podInfo {
  2643  				oldPodHasSchedulingGates, oldPod := oldPodInfo.hasSchedulingGatesField, oldPodInfo.pod()
  2644  				newPodHasSchedulingGates, newPod := newPodInfo.hasSchedulingGatesField, newPodInfo.pod()
  2645  				if newPod == nil {
  2646  					continue
  2647  				}
  2648  
  2649  				t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
  2650  					defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.PodSchedulingReadiness, enabled)()
  2651  					var oldPodSpec *api.PodSpec
  2652  					if oldPod != nil {
  2653  						oldPodSpec = &oldPod.Spec
  2654  					}
  2655  					dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
  2656  					// Old Pod should never be changed.
  2657  					if diff := cmp.Diff(oldPod, oldPodInfo.pod()); diff != "" {
  2658  						t.Errorf("old pod changed: %v", diff)
  2659  					}
  2660  					switch {
  2661  					case enabled || oldPodHasSchedulingGates:
  2662  						// New Pod should not be changed if the feature is enabled, or if the old Pod had schedulingGates.
  2663  						if diff := cmp.Diff(newPod, newPodInfo.pod()); diff != "" {
  2664  							t.Errorf("new pod changed: %v", diff)
  2665  						}
  2666  					case newPodHasSchedulingGates:
  2667  						// New Pod should be changed.
  2668  						if reflect.DeepEqual(newPod, newPodInfo.pod()) {
  2669  							t.Errorf("new pod was not changed")
  2670  						}
  2671  						// New Pod should not have SchedulingGates field.
  2672  						if diff := cmp.Diff(newPod, podWithoutSchedulingGates()); diff != "" {
  2673  							t.Errorf("new pod has SchedulingGates field: %v", diff)
  2674  						}
  2675  					default:
  2676  						// New pod should not need to be changed.
  2677  						if diff := cmp.Diff(newPod, newPodInfo.pod()); diff != "" {
  2678  							t.Errorf("new pod changed: %v", diff)
  2679  						}
  2680  					}
  2681  				})
  2682  			}
  2683  		}
  2684  	}
  2685  }
  2686  
  2687  func TestValidateTopologySpreadConstraintLabelSelectorOption(t *testing.T) {
  2688  	testCases := []struct {
  2689  		name       string
  2690  		oldPodSpec *api.PodSpec
  2691  		wantOption bool
  2692  	}{
  2693  		{
  2694  			name:       "Create",
  2695  			wantOption: false,
  2696  		},
  2697  		{
  2698  			name: "UpdateInvalidLabelSelector",
  2699  			oldPodSpec: &api.PodSpec{
  2700  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  2701  					{
  2702  						LabelSelector: &metav1.LabelSelector{
  2703  							MatchLabels: map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "foo"},
  2704  						},
  2705  					},
  2706  				},
  2707  			},
  2708  			wantOption: true,
  2709  		},
  2710  		{
  2711  			name: "UpdateValidLabelSelector",
  2712  			oldPodSpec: &api.PodSpec{
  2713  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  2714  					{
  2715  						LabelSelector: &metav1.LabelSelector{
  2716  							MatchLabels: map[string]string{"foo": "foo"},
  2717  						},
  2718  					},
  2719  				},
  2720  			},
  2721  			wantOption: false,
  2722  		},
  2723  		{
  2724  			name: "UpdateEmptyLabelSelector",
  2725  			oldPodSpec: &api.PodSpec{
  2726  				TopologySpreadConstraints: []api.TopologySpreadConstraint{
  2727  					{
  2728  						LabelSelector: nil,
  2729  					},
  2730  				},
  2731  			},
  2732  			wantOption: false,
  2733  		},
  2734  	}
  2735  
  2736  	for _, tc := range testCases {
  2737  		t.Run(tc.name, func(t *testing.T) {
  2738  			// Pod meta doesn't impact the outcome.
  2739  			gotOptions := GetValidationOptionsFromPodSpecAndMeta(&api.PodSpec{}, tc.oldPodSpec, nil, nil)
  2740  			if tc.wantOption != gotOptions.AllowInvalidTopologySpreadConstraintLabelSelector {
  2741  				t.Errorf("Got AllowInvalidLabelValueInSelector=%t, want %t", gotOptions.AllowInvalidTopologySpreadConstraintLabelSelector, tc.wantOption)
  2742  			}
  2743  		})
  2744  	}
  2745  }
  2746  
  2747  func TestDropInPlacePodVerticalScaling(t *testing.T) {
  2748  	podWithInPlaceVerticalScaling := func() *api.Pod {
  2749  		return &api.Pod{
  2750  			Spec: api.PodSpec{
  2751  				Containers: []api.Container{
  2752  					{
  2753  						Name:  "c1",
  2754  						Image: "image",
  2755  						Resources: api.ResourceRequirements{
  2756  							Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
  2757  							Limits:   api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
  2758  						},
  2759  						ResizePolicy: []api.ContainerResizePolicy{
  2760  							{ResourceName: api.ResourceCPU, RestartPolicy: api.NotRequired},
  2761  							{ResourceName: api.ResourceMemory, RestartPolicy: api.RestartContainer},
  2762  						},
  2763  					},
  2764  				},
  2765  			},
  2766  			Status: api.PodStatus{
  2767  				Resize: api.PodResizeStatusInProgress,
  2768  				ContainerStatuses: []api.ContainerStatus{
  2769  					{
  2770  						Name:               "c1",
  2771  						Image:              "image",
  2772  						AllocatedResources: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
  2773  						Resources: &api.ResourceRequirements{
  2774  							Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
  2775  							Limits:   api.ResourceList{api.ResourceCPU: resource.MustParse("300m")},
  2776  						},
  2777  					},
  2778  				},
  2779  			},
  2780  		}
  2781  	}
  2782  	podWithoutInPlaceVerticalScaling := func() *api.Pod {
  2783  		return &api.Pod{
  2784  			Spec: api.PodSpec{
  2785  				Containers: []api.Container{
  2786  					{
  2787  						Name:  "c1",
  2788  						Image: "image",
  2789  						Resources: api.ResourceRequirements{
  2790  							Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
  2791  							Limits:   api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
  2792  						},
  2793  					},
  2794  				},
  2795  			},
  2796  			Status: api.PodStatus{
  2797  				ContainerStatuses: []api.ContainerStatus{
  2798  					{
  2799  						Name:  "c1",
  2800  						Image: "image",
  2801  					},
  2802  				},
  2803  			},
  2804  		}
  2805  	}
  2806  
  2807  	podInfo := []struct {
  2808  		description               string
  2809  		hasInPlaceVerticalScaling bool
  2810  		pod                       func() *api.Pod
  2811  	}{
  2812  		{
  2813  			description:               "has in-place vertical scaling enabled with resources",
  2814  			hasInPlaceVerticalScaling: true,
  2815  			pod:                       podWithInPlaceVerticalScaling,
  2816  		},
  2817  		{
  2818  			description:               "has in-place vertical scaling disabled",
  2819  			hasInPlaceVerticalScaling: false,
  2820  			pod:                       podWithoutInPlaceVerticalScaling,
  2821  		},
  2822  		{
  2823  			description:               "is nil",
  2824  			hasInPlaceVerticalScaling: false,
  2825  			pod:                       func() *api.Pod { return nil },
  2826  		},
  2827  	}
  2828  
  2829  	for _, enabled := range []bool{true, false} {
  2830  		for _, oldPodInfo := range podInfo {
  2831  			for _, newPodInfo := range podInfo {
  2832  				oldPodHasInPlaceVerticalScaling, oldPod := oldPodInfo.hasInPlaceVerticalScaling, oldPodInfo.pod()
  2833  				newPodHasInPlaceVerticalScaling, newPod := newPodInfo.hasInPlaceVerticalScaling, newPodInfo.pod()
  2834  				if newPod == nil {
  2835  					continue
  2836  				}
  2837  
  2838  				t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
  2839  					defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.InPlacePodVerticalScaling, enabled)()
  2840  
  2841  					var oldPodSpec *api.PodSpec
  2842  					var oldPodStatus *api.PodStatus
  2843  					if oldPod != nil {
  2844  						oldPodSpec = &oldPod.Spec
  2845  						oldPodStatus = &oldPod.Status
  2846  					}
  2847  					dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
  2848  					dropDisabledPodStatusFields(&newPod.Status, oldPodStatus, &newPod.Spec, oldPodSpec)
  2849  
  2850  					// old pod should never be changed
  2851  					if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
  2852  						t.Errorf("old pod changed: %v", cmp.Diff(oldPod, oldPodInfo.pod()))
  2853  					}
  2854  
  2855  					switch {
  2856  					case enabled || oldPodHasInPlaceVerticalScaling:
  2857  						// new pod shouldn't change if feature enabled or if old pod has ResizePolicy set
  2858  						if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  2859  							t.Errorf("new pod changed: %v", cmp.Diff(newPod, newPodInfo.pod()))
  2860  						}
  2861  					case newPodHasInPlaceVerticalScaling:
  2862  						// new pod should be changed
  2863  						if reflect.DeepEqual(newPod, newPodInfo.pod()) {
  2864  							t.Errorf("new pod was not changed")
  2865  						}
  2866  						// new pod should not have ResizePolicy
  2867  						if !reflect.DeepEqual(newPod, podWithoutInPlaceVerticalScaling()) {
  2868  							t.Errorf("new pod has ResizePolicy: %v", cmp.Diff(newPod, podWithoutInPlaceVerticalScaling()))
  2869  						}
  2870  					default:
  2871  						// new pod should not need to be changed
  2872  						if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  2873  							t.Errorf("new pod changed: %v", cmp.Diff(newPod, newPodInfo.pod()))
  2874  						}
  2875  					}
  2876  				})
  2877  			}
  2878  		}
  2879  	}
  2880  }
  2881  
  2882  func TestDropSidecarContainers(t *testing.T) {
  2883  	containerRestartPolicyAlways := api.ContainerRestartPolicyAlways
  2884  
  2885  	podWithSidecarContainers := func() *api.Pod {
  2886  		return &api.Pod{
  2887  			Spec: api.PodSpec{
  2888  				InitContainers: []api.Container{
  2889  					{
  2890  						Name:          "c1",
  2891  						Image:         "image",
  2892  						RestartPolicy: &containerRestartPolicyAlways,
  2893  					},
  2894  				},
  2895  			},
  2896  		}
  2897  	}
  2898  
  2899  	podWithoutSidecarContainers := func() *api.Pod {
  2900  		return &api.Pod{
  2901  			Spec: api.PodSpec{
  2902  				InitContainers: []api.Container{
  2903  					{
  2904  						Name:  "c1",
  2905  						Image: "image",
  2906  					},
  2907  				},
  2908  			},
  2909  		}
  2910  	}
  2911  
  2912  	podInfo := []struct {
  2913  		description         string
  2914  		hasSidecarContainer bool
  2915  		pod                 func() *api.Pod
  2916  	}{
  2917  		{
  2918  			description:         "has a sidecar container",
  2919  			hasSidecarContainer: true,
  2920  			pod:                 podWithSidecarContainers,
  2921  		},
  2922  		{
  2923  			description:         "does not have a sidecar container",
  2924  			hasSidecarContainer: false,
  2925  			pod:                 podWithoutSidecarContainers,
  2926  		},
  2927  		{
  2928  			description:         "is nil",
  2929  			hasSidecarContainer: false,
  2930  			pod:                 func() *api.Pod { return nil },
  2931  		},
  2932  	}
  2933  
  2934  	for _, enabled := range []bool{true, false} {
  2935  		for _, oldPodInfo := range podInfo {
  2936  			for _, newPodInfo := range podInfo {
  2937  				oldPodHasSidecarContainer, oldPod := oldPodInfo.hasSidecarContainer, oldPodInfo.pod()
  2938  				newPodHasSidecarContainer, newPod := newPodInfo.hasSidecarContainer, newPodInfo.pod()
  2939  				if newPod == nil {
  2940  					continue
  2941  				}
  2942  
  2943  				t.Run(fmt.Sprintf("feature enabled=%v, old pod %v, new pod %v", enabled, oldPodInfo.description, newPodInfo.description), func(t *testing.T) {
  2944  					defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.SidecarContainers, enabled)()
  2945  
  2946  					var oldPodSpec *api.PodSpec
  2947  					if oldPod != nil {
  2948  						oldPodSpec = &oldPod.Spec
  2949  					}
  2950  					dropDisabledFields(&newPod.Spec, nil, oldPodSpec, nil)
  2951  
  2952  					// old pod should never be changed
  2953  					if !reflect.DeepEqual(oldPod, oldPodInfo.pod()) {
  2954  						t.Errorf("old pod changed: %v", cmp.Diff(oldPod, oldPodInfo.pod()))
  2955  					}
  2956  
  2957  					switch {
  2958  					case enabled || oldPodHasSidecarContainer:
  2959  						// new pod shouldn't change if feature enabled or if old pod has
  2960  						// any sidecar container
  2961  						if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  2962  							t.Errorf("new pod changed: %v", cmp.Diff(newPod, newPodInfo.pod()))
  2963  						}
  2964  					case newPodHasSidecarContainer:
  2965  						// new pod should be changed
  2966  						if reflect.DeepEqual(newPod, newPodInfo.pod()) {
  2967  							t.Errorf("new pod was not changed")
  2968  						}
  2969  						// new pod should not have any sidecar container
  2970  						if !reflect.DeepEqual(newPod, podWithoutSidecarContainers()) {
  2971  							t.Errorf("new pod has a sidecar container: %v", cmp.Diff(newPod, podWithoutSidecarContainers()))
  2972  						}
  2973  					default:
  2974  						// new pod should not need to be changed
  2975  						if !reflect.DeepEqual(newPod, newPodInfo.pod()) {
  2976  							t.Errorf("new pod changed: %v", cmp.Diff(newPod, newPodInfo.pod()))
  2977  						}
  2978  					}
  2979  				})
  2980  			}
  2981  		}
  2982  	}
  2983  }
  2984  
  2985  func TestMarkPodProposedForResize(t *testing.T) {
  2986  	testCases := []struct {
  2987  		desc        string
  2988  		newPod      *api.Pod
  2989  		oldPod      *api.Pod
  2990  		expectedPod *api.Pod
  2991  	}{
  2992  		{
  2993  			desc: "nil requests",
  2994  			newPod: &api.Pod{
  2995  				Spec: api.PodSpec{
  2996  					Containers: []api.Container{
  2997  						{
  2998  							Name:  "c1",
  2999  							Image: "image",
  3000  						},
  3001  					},
  3002  				},
  3003  				Status: api.PodStatus{
  3004  					ContainerStatuses: []api.ContainerStatus{
  3005  						{
  3006  							Name:  "c1",
  3007  							Image: "image",
  3008  						},
  3009  					},
  3010  				},
  3011  			},
  3012  			oldPod: &api.Pod{
  3013  				Spec: api.PodSpec{
  3014  					Containers: []api.Container{
  3015  						{
  3016  							Name:  "c1",
  3017  							Image: "image",
  3018  						},
  3019  					},
  3020  				},
  3021  				Status: api.PodStatus{
  3022  					ContainerStatuses: []api.ContainerStatus{
  3023  						{
  3024  							Name:  "c1",
  3025  							Image: "image",
  3026  						},
  3027  					},
  3028  				},
  3029  			},
  3030  			expectedPod: &api.Pod{
  3031  				Spec: api.PodSpec{
  3032  					Containers: []api.Container{
  3033  						{
  3034  							Name:  "c1",
  3035  							Image: "image",
  3036  						},
  3037  					},
  3038  				},
  3039  				Status: api.PodStatus{
  3040  					ContainerStatuses: []api.ContainerStatus{
  3041  						{
  3042  							Name:  "c1",
  3043  							Image: "image",
  3044  						},
  3045  					},
  3046  				},
  3047  			},
  3048  		},
  3049  		{
  3050  			desc: "resources unchanged",
  3051  			newPod: &api.Pod{
  3052  				Spec: api.PodSpec{
  3053  					Containers: []api.Container{
  3054  						{
  3055  							Name:  "c1",
  3056  							Image: "image",
  3057  							Resources: api.ResourceRequirements{
  3058  								Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
  3059  								Limits:   api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
  3060  							},
  3061  						},
  3062  					},
  3063  				},
  3064  				Status: api.PodStatus{
  3065  					ContainerStatuses: []api.ContainerStatus{
  3066  						{
  3067  							Name:  "c1",
  3068  							Image: "image",
  3069  						},
  3070  					},
  3071  				},
  3072  			},
  3073  			oldPod: &api.Pod{
  3074  				Spec: api.PodSpec{
  3075  					Containers: []api.Container{
  3076  						{
  3077  							Name:  "c1",
  3078  							Image: "image",
  3079  							Resources: api.ResourceRequirements{
  3080  								Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
  3081  								Limits:   api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
  3082  							},
  3083  						},
  3084  					},
  3085  				},
  3086  				Status: api.PodStatus{
  3087  					ContainerStatuses: []api.ContainerStatus{
  3088  						{
  3089  							Name:  "c1",
  3090  							Image: "image",
  3091  						},
  3092  					},
  3093  				},
  3094  			},
  3095  			expectedPod: &api.Pod{
  3096  				Spec: api.PodSpec{
  3097  					Containers: []api.Container{
  3098  						{
  3099  							Name:  "c1",
  3100  							Image: "image",
  3101  							Resources: api.ResourceRequirements{
  3102  								Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
  3103  								Limits:   api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
  3104  							},
  3105  						},
  3106  					},
  3107  				},
  3108  				Status: api.PodStatus{
  3109  					ContainerStatuses: []api.ContainerStatus{
  3110  						{
  3111  							Name:  "c1",
  3112  							Image: "image",
  3113  						},
  3114  					},
  3115  				},
  3116  			},
  3117  		},
  3118  		{
  3119  			desc: "resize desired",
  3120  			newPod: &api.Pod{
  3121  				Spec: api.PodSpec{
  3122  					Containers: []api.Container{
  3123  						{
  3124  							Name:  "c1",
  3125  							Image: "image",
  3126  							Resources: api.ResourceRequirements{
  3127  								Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
  3128  								Limits:   api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
  3129  							},
  3130  						},
  3131  						{
  3132  							Name:  "c2",
  3133  							Image: "image",
  3134  							Resources: api.ResourceRequirements{
  3135  								Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("300m")},
  3136  								Limits:   api.ResourceList{api.ResourceCPU: resource.MustParse("400m")},
  3137  							},
  3138  						},
  3139  					},
  3140  				},
  3141  				Status: api.PodStatus{
  3142  					ContainerStatuses: []api.ContainerStatus{
  3143  						{
  3144  							Name:               "c1",
  3145  							Image:              "image",
  3146  							AllocatedResources: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
  3147  						},
  3148  						{
  3149  							Name:               "c2",
  3150  							Image:              "image",
  3151  							AllocatedResources: api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
  3152  						},
  3153  					},
  3154  				},
  3155  			},
  3156  			oldPod: &api.Pod{
  3157  				Spec: api.PodSpec{
  3158  					Containers: []api.Container{
  3159  						{
  3160  							Name:  "c1",
  3161  							Image: "image",
  3162  							Resources: api.ResourceRequirements{
  3163  								Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
  3164  								Limits:   api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
  3165  							},
  3166  						},
  3167  						{
  3168  							Name:  "c2",
  3169  							Image: "image",
  3170  							Resources: api.ResourceRequirements{
  3171  								Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
  3172  								Limits:   api.ResourceList{api.ResourceCPU: resource.MustParse("300m")},
  3173  							},
  3174  						},
  3175  					},
  3176  				},
  3177  				Status: api.PodStatus{
  3178  					ContainerStatuses: []api.ContainerStatus{
  3179  						{
  3180  							Name:               "c1",
  3181  							Image:              "image",
  3182  							AllocatedResources: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
  3183  						},
  3184  						{
  3185  							Name:               "c2",
  3186  							Image:              "image",
  3187  							AllocatedResources: api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
  3188  						},
  3189  					},
  3190  				},
  3191  			},
  3192  			expectedPod: &api.Pod{
  3193  				Spec: api.PodSpec{
  3194  					Containers: []api.Container{
  3195  						{
  3196  							Name:  "c1",
  3197  							Image: "image",
  3198  							Resources: api.ResourceRequirements{
  3199  								Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
  3200  								Limits:   api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
  3201  							},
  3202  						},
  3203  						{
  3204  							Name:  "c2",
  3205  							Image: "image",
  3206  							Resources: api.ResourceRequirements{
  3207  								Requests: api.ResourceList{api.ResourceCPU: resource.MustParse("300m")},
  3208  								Limits:   api.ResourceList{api.ResourceCPU: resource.MustParse("400m")},
  3209  							},
  3210  						},
  3211  					},
  3212  				},
  3213  				Status: api.PodStatus{
  3214  					Resize: api.PodResizeStatusProposed,
  3215  					ContainerStatuses: []api.ContainerStatus{
  3216  						{
  3217  							Name:               "c1",
  3218  							Image:              "image",
  3219  							AllocatedResources: api.ResourceList{api.ResourceCPU: resource.MustParse("100m")},
  3220  						},
  3221  						{
  3222  							Name:               "c2",
  3223  							Image:              "image",
  3224  							AllocatedResources: api.ResourceList{api.ResourceCPU: resource.MustParse("200m")},
  3225  						},
  3226  					},
  3227  				},
  3228  			},
  3229  		},
  3230  	}
  3231  	for _, tc := range testCases {
  3232  		t.Run(tc.desc, func(t *testing.T) {
  3233  			MarkPodProposedForResize(tc.oldPod, tc.newPod)
  3234  			if diff := cmp.Diff(tc.expectedPod, tc.newPod); diff != "" {
  3235  				t.Errorf("unexpected pod spec (-want, +got):\n%s", diff)
  3236  			}
  3237  		})
  3238  	}
  3239  }
  3240  
  3241  func TestDropClusterTrustBundleProjectedVolumes(t *testing.T) {
  3242  	testCases := []struct {
  3243  		description                         string
  3244  		clusterTrustBundleProjectionEnabled bool
  3245  		oldPod                              *api.PodSpec
  3246  		newPod                              *api.PodSpec
  3247  		wantPod                             *api.PodSpec
  3248  	}{
  3249  		{
  3250  			description: "feature gate disabled, cannot add CTB volume to pod",
  3251  			oldPod: &api.PodSpec{
  3252  				Volumes: []api.Volume{},
  3253  			},
  3254  			newPod: &api.PodSpec{
  3255  				Volumes: []api.Volume{
  3256  					{
  3257  						Name: "foo",
  3258  						VolumeSource: api.VolumeSource{
  3259  							Projected: &api.ProjectedVolumeSource{
  3260  								Sources: []api.VolumeProjection{
  3261  									{
  3262  										ClusterTrustBundle: &api.ClusterTrustBundleProjection{
  3263  											Name: pointer.String("foo"),
  3264  										},
  3265  									},
  3266  								},
  3267  							}},
  3268  					},
  3269  				},
  3270  			},
  3271  			wantPod: &api.PodSpec{
  3272  				Volumes: []api.Volume{
  3273  					{
  3274  						Name: "foo",
  3275  						VolumeSource: api.VolumeSource{
  3276  							Projected: &api.ProjectedVolumeSource{
  3277  								Sources: []api.VolumeProjection{
  3278  									{},
  3279  								},
  3280  							}},
  3281  					},
  3282  				},
  3283  			},
  3284  		},
  3285  		{
  3286  			description: "feature gate disabled, can keep CTB volume on pod",
  3287  			oldPod: &api.PodSpec{
  3288  				Volumes: []api.Volume{
  3289  					{
  3290  						Name: "foo",
  3291  						VolumeSource: api.VolumeSource{
  3292  							Projected: &api.ProjectedVolumeSource{
  3293  								Sources: []api.VolumeProjection{
  3294  									{
  3295  										ClusterTrustBundle: &api.ClusterTrustBundleProjection{
  3296  											Name: pointer.String("foo"),
  3297  										},
  3298  									},
  3299  								},
  3300  							}},
  3301  					},
  3302  				},
  3303  			},
  3304  			newPod: &api.PodSpec{
  3305  				Volumes: []api.Volume{
  3306  					{
  3307  						Name: "foo",
  3308  						VolumeSource: api.VolumeSource{
  3309  							Projected: &api.ProjectedVolumeSource{
  3310  								Sources: []api.VolumeProjection{
  3311  									{
  3312  										ClusterTrustBundle: &api.ClusterTrustBundleProjection{
  3313  											Name: pointer.String("foo"),
  3314  										},
  3315  									},
  3316  								},
  3317  							}},
  3318  					},
  3319  				},
  3320  			},
  3321  			wantPod: &api.PodSpec{
  3322  				Volumes: []api.Volume{
  3323  					{
  3324  						Name: "foo",
  3325  						VolumeSource: api.VolumeSource{
  3326  							Projected: &api.ProjectedVolumeSource{
  3327  								Sources: []api.VolumeProjection{
  3328  									{
  3329  										ClusterTrustBundle: &api.ClusterTrustBundleProjection{
  3330  											Name: pointer.String("foo"),
  3331  										},
  3332  									},
  3333  								},
  3334  							}},
  3335  					},
  3336  				},
  3337  			},
  3338  		},
  3339  		{
  3340  			description:                         "feature gate enabled, can add CTB volume to pod",
  3341  			clusterTrustBundleProjectionEnabled: true,
  3342  			oldPod: &api.PodSpec{
  3343  				Volumes: []api.Volume{},
  3344  			},
  3345  			newPod: &api.PodSpec{
  3346  				Volumes: []api.Volume{
  3347  					{
  3348  						Name: "foo",
  3349  						VolumeSource: api.VolumeSource{
  3350  							Projected: &api.ProjectedVolumeSource{
  3351  								Sources: []api.VolumeProjection{
  3352  									{
  3353  										ClusterTrustBundle: &api.ClusterTrustBundleProjection{
  3354  											Name: pointer.String("foo"),
  3355  										},
  3356  									},
  3357  								},
  3358  							}},
  3359  					},
  3360  				},
  3361  			},
  3362  			wantPod: &api.PodSpec{
  3363  				Volumes: []api.Volume{
  3364  					{
  3365  						Name: "foo",
  3366  						VolumeSource: api.VolumeSource{
  3367  							Projected: &api.ProjectedVolumeSource{
  3368  								Sources: []api.VolumeProjection{
  3369  									{
  3370  										ClusterTrustBundle: &api.ClusterTrustBundleProjection{
  3371  											Name: pointer.String("foo"),
  3372  										},
  3373  									},
  3374  								},
  3375  							}},
  3376  					},
  3377  				},
  3378  			},
  3379  		},
  3380  	}
  3381  
  3382  	for _, tc := range testCases {
  3383  		t.Run(tc.description, func(t *testing.T) {
  3384  			defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.ClusterTrustBundleProjection, tc.clusterTrustBundleProjectionEnabled)()
  3385  
  3386  			dropDisabledClusterTrustBundleProjection(tc.newPod, tc.oldPod)
  3387  			if diff := cmp.Diff(tc.newPod, tc.wantPod); diff != "" {
  3388  				t.Fatalf("Unexpected modification to new pod; diff (-got +want)\n%s", diff)
  3389  			}
  3390  		})
  3391  	}
  3392  }