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 }