k8s.io/kubernetes@v1.31.0-alpha.0.0.20240520171757-56147500dadc/pkg/api/pod/warnings_test.go (about) 1 /* 2 Copyright 2021 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 "context" 21 "testing" 22 23 "k8s.io/apimachinery/pkg/api/resource" 24 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 25 "k8s.io/apimachinery/pkg/util/sets" 26 "k8s.io/apimachinery/pkg/util/validation/field" 27 api "k8s.io/kubernetes/pkg/apis/core" 28 utilpointer "k8s.io/utils/pointer" 29 ) 30 31 func BenchmarkNoWarnings(b *testing.B) { 32 ctx := context.TODO() 33 resources := api.ResourceList{ 34 api.ResourceCPU: resource.MustParse("100m"), 35 api.ResourceMemory: resource.MustParse("4M"), 36 api.ResourceEphemeralStorage: resource.MustParse("4G"), 37 } 38 env := []api.EnvVar{ 39 {Name: "a"}, 40 {Name: "b"}, 41 } 42 pod := &api.Pod{ 43 ObjectMeta: metav1.ObjectMeta{ 44 Annotations: map[string]string{`foo`: `bar`}, 45 }, 46 Spec: api.PodSpec{ 47 NodeSelector: map[string]string{"foo": "bar", "baz": "quux"}, 48 Affinity: &api.Affinity{ 49 NodeAffinity: &api.NodeAffinity{ 50 RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{ 51 NodeSelectorTerms: []api.NodeSelectorTerm{ 52 {MatchExpressions: []api.NodeSelectorRequirement{{Key: `foo`}}}, 53 }, 54 }, 55 PreferredDuringSchedulingIgnoredDuringExecution: []api.PreferredSchedulingTerm{ 56 {Preference: api.NodeSelectorTerm{MatchExpressions: []api.NodeSelectorRequirement{{Key: `foo`}}}}, 57 }, 58 }, 59 }, 60 TopologySpreadConstraints: []api.TopologySpreadConstraint{ 61 {TopologyKey: `foo`}, 62 }, 63 HostAliases: []api.HostAlias{ 64 {IP: "1.1.1.1"}, 65 {IP: "2.2.2.2"}, 66 }, 67 ImagePullSecrets: []api.LocalObjectReference{ 68 {Name: "secret1"}, 69 {Name: "secret2"}, 70 }, 71 InitContainers: []api.Container{ 72 {Name: "init1", Env: env, Resources: api.ResourceRequirements{Requests: resources, Limits: resources}}, 73 {Name: "init2", Env: env, Resources: api.ResourceRequirements{Requests: resources, Limits: resources}}, 74 }, 75 Containers: []api.Container{ 76 {Name: "container1", Env: env, Resources: api.ResourceRequirements{Requests: resources, Limits: resources}}, 77 {Name: "container2", Env: env, Resources: api.ResourceRequirements{Requests: resources, Limits: resources}}, 78 }, 79 Overhead: resources, 80 Volumes: []api.Volume{ 81 {Name: "a"}, 82 {Name: "b"}, 83 }, 84 }, 85 } 86 oldPod := &api.Pod{} 87 b.ResetTimer() 88 for i := 0; i < b.N; i++ { 89 w := GetWarningsForPod(ctx, pod, oldPod) 90 if len(w) > 0 { 91 b.Fatalf("expected 0 warnings, got %q", w) 92 } 93 } 94 } 95 96 func BenchmarkWarnings(b *testing.B) { 97 ctx := context.TODO() 98 resources := api.ResourceList{ 99 api.ResourceCPU: resource.MustParse("100m"), 100 api.ResourceMemory: resource.MustParse("4m"), 101 api.ResourceEphemeralStorage: resource.MustParse("4m"), 102 } 103 env := []api.EnvVar{ 104 {Name: "a"}, 105 {Name: "a"}, 106 } 107 pod := &api.Pod{ 108 Spec: api.PodSpec{ 109 HostAliases: []api.HostAlias{ 110 {IP: "1.1.1.1"}, 111 {IP: "1.1.1.1"}, 112 }, 113 ImagePullSecrets: []api.LocalObjectReference{ 114 {Name: "secret1"}, 115 {Name: "secret1"}, 116 {Name: ""}, 117 }, 118 InitContainers: []api.Container{ 119 {Name: "init1", Env: env, Resources: api.ResourceRequirements{Requests: resources, Limits: resources}}, 120 {Name: "init2", Env: env, Resources: api.ResourceRequirements{Requests: resources, Limits: resources}}, 121 }, 122 Containers: []api.Container{ 123 {Name: "container1", Env: env, Resources: api.ResourceRequirements{Requests: resources, Limits: resources}}, 124 {Name: "container2", Env: env, Resources: api.ResourceRequirements{Requests: resources, Limits: resources}}, 125 }, 126 Overhead: resources, 127 Volumes: []api.Volume{ 128 {Name: "a"}, 129 {Name: "a"}, 130 }, 131 }, 132 } 133 oldPod := &api.Pod{} 134 b.ResetTimer() 135 for i := 0; i < b.N; i++ { 136 GetWarningsForPod(ctx, pod, oldPod) 137 } 138 } 139 140 func TestWarnings(t *testing.T) { 141 resources := api.ResourceList{ 142 api.ResourceCPU: resource.MustParse("100m"), 143 api.ResourceMemory: resource.MustParse("4m"), 144 api.ResourceEphemeralStorage: resource.MustParse("4m"), 145 } 146 testcases := []struct { 147 name string 148 template *api.PodTemplateSpec 149 oldTemplate *api.PodTemplateSpec 150 expected []string 151 }{ 152 { 153 name: "null", 154 template: nil, 155 expected: nil, 156 }, 157 { 158 name: "photon", 159 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 160 Volumes: []api.Volume{ 161 {Name: "p", VolumeSource: api.VolumeSource{PhotonPersistentDisk: &api.PhotonPersistentDiskVolumeSource{}}}, 162 }}, 163 }, 164 expected: []string{`spec.volumes[0].photonPersistentDisk: deprecated in v1.11, non-functional in v1.16+`}, 165 }, 166 { 167 name: "gitRepo", 168 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 169 Volumes: []api.Volume{ 170 {Name: "s", VolumeSource: api.VolumeSource{GitRepo: &api.GitRepoVolumeSource{}}}, 171 }}, 172 }, 173 expected: []string{`spec.volumes[0].gitRepo: deprecated in v1.11`}, 174 }, 175 { 176 name: "scaleIO", 177 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 178 Volumes: []api.Volume{ 179 {Name: "s", VolumeSource: api.VolumeSource{ScaleIO: &api.ScaleIOVolumeSource{}}}, 180 }}, 181 }, 182 expected: []string{`spec.volumes[0].scaleIO: deprecated in v1.16, non-functional in v1.22+`}, 183 }, 184 { 185 name: "flocker", 186 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 187 Volumes: []api.Volume{ 188 {Name: "s", VolumeSource: api.VolumeSource{Flocker: &api.FlockerVolumeSource{}}}, 189 }}, 190 }, 191 expected: []string{`spec.volumes[0].flocker: deprecated in v1.22, non-functional in v1.25+`}, 192 }, 193 { 194 name: "storageOS", 195 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 196 Volumes: []api.Volume{ 197 {Name: "s", VolumeSource: api.VolumeSource{StorageOS: &api.StorageOSVolumeSource{}}}, 198 }}, 199 }, 200 expected: []string{`spec.volumes[0].storageOS: deprecated in v1.22, non-functional in v1.25+`}, 201 }, 202 { 203 name: "quobyte", 204 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 205 Volumes: []api.Volume{ 206 {Name: "s", VolumeSource: api.VolumeSource{Quobyte: &api.QuobyteVolumeSource{}}}, 207 }}, 208 }, 209 expected: []string{`spec.volumes[0].quobyte: deprecated in v1.22, non-functional in v1.25+`}, 210 }, 211 { 212 name: "glusterfs", 213 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 214 Volumes: []api.Volume{ 215 {Name: "s", VolumeSource: api.VolumeSource{Glusterfs: &api.GlusterfsVolumeSource{}}}, 216 }}, 217 }, 218 expected: []string{`spec.volumes[0].glusterfs: deprecated in v1.25, non-functional in v1.26+`}, 219 }, { 220 name: "CephFS", 221 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 222 Volumes: []api.Volume{ 223 {Name: "s", VolumeSource: api.VolumeSource{CephFS: &api.CephFSVolumeSource{}}}, 224 }}, 225 }, 226 expected: []string{`spec.volumes[0].cephfs: deprecated in v1.28, non-functional in v1.31+`}, 227 }, 228 229 { 230 name: "rbd", 231 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 232 Volumes: []api.Volume{ 233 {Name: "s", VolumeSource: api.VolumeSource{RBD: &api.RBDVolumeSource{}}}, 234 }}, 235 }, 236 expected: []string{`spec.volumes[0].rbd: deprecated in v1.28, non-functional in v1.31+`}, 237 }, 238 { 239 name: "duplicate hostAlias", 240 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 241 HostAliases: []api.HostAlias{ 242 {IP: "1.1.1.1"}, 243 {IP: "1.1.1.1"}, 244 {IP: "1.1.1.1"}, 245 }}, 246 }, 247 expected: []string{ 248 `spec.hostAliases[1].ip: duplicate ip "1.1.1.1"`, 249 `spec.hostAliases[2].ip: duplicate ip "1.1.1.1"`, 250 }, 251 }, 252 { 253 name: "duplicate imagePullSecret", 254 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 255 ImagePullSecrets: []api.LocalObjectReference{ 256 {Name: "a"}, 257 {Name: "a"}, 258 {Name: "a"}, 259 }}, 260 }, 261 expected: []string{ 262 `spec.imagePullSecrets[1].name: duplicate name "a"`, 263 `spec.imagePullSecrets[2].name: duplicate name "a"`, 264 }, 265 }, 266 { 267 name: "empty imagePullSecret", 268 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 269 ImagePullSecrets: []api.LocalObjectReference{ 270 {Name: ""}, 271 }}, 272 }, 273 expected: []string{ 274 `spec.imagePullSecrets[0].name: invalid empty name ""`, 275 }, 276 }, 277 { 278 name: "duplicate env", 279 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 280 InitContainers: []api.Container{{Env: []api.EnvVar{ 281 {Name: "a", Value: "a"}, 282 {Name: "a", Value: "a"}, 283 {Name: "a", Value: "other"}, 284 {Name: "a", Value: ""}, 285 {Name: "a", Value: "$(a)"}, 286 {Name: "a", ValueFrom: &api.EnvVarSource{}}, 287 {Name: "a", Value: "$(a) $(a)"}, // no warning 288 }}}, 289 Containers: []api.Container{{Env: []api.EnvVar{ 290 {Name: "b", Value: "b"}, 291 {Name: "b", Value: "b"}, 292 {Name: "b", Value: "other"}, 293 {Name: "b", Value: ""}, 294 {Name: "b", Value: "$(b)"}, 295 {Name: "b", ValueFrom: &api.EnvVarSource{}}, 296 {Name: "b", Value: "$(b) $(b)"}, // no warning 297 }}}, 298 }}, 299 expected: []string{ 300 `spec.initContainers[0].env[1]: hides previous definition of "a", which may be dropped when using apply`, 301 `spec.initContainers[0].env[2]: hides previous definition of "a", which may be dropped when using apply`, 302 `spec.initContainers[0].env[3]: hides previous definition of "a", which may be dropped when using apply`, 303 `spec.initContainers[0].env[4]: hides previous definition of "a", which may be dropped when using apply`, 304 `spec.initContainers[0].env[5]: hides previous definition of "a", which may be dropped when using apply`, 305 `spec.containers[0].env[1]: hides previous definition of "b", which may be dropped when using apply`, 306 `spec.containers[0].env[2]: hides previous definition of "b", which may be dropped when using apply`, 307 `spec.containers[0].env[3]: hides previous definition of "b", which may be dropped when using apply`, 308 `spec.containers[0].env[4]: hides previous definition of "b", which may be dropped when using apply`, 309 `spec.containers[0].env[5]: hides previous definition of "b", which may be dropped when using apply`, 310 }, 311 }, 312 { 313 name: "fractional resources", 314 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 315 InitContainers: []api.Container{{ 316 Resources: api.ResourceRequirements{Requests: resources, Limits: resources}, 317 }}, 318 Containers: []api.Container{{ 319 Resources: api.ResourceRequirements{Requests: resources, Limits: resources}, 320 }}, 321 Overhead: resources, 322 }}, 323 expected: []string{ 324 `spec.initContainers[0].resources.requests[ephemeral-storage]: fractional byte value "4m" is invalid, must be an integer`, 325 `spec.initContainers[0].resources.requests[memory]: fractional byte value "4m" is invalid, must be an integer`, 326 `spec.initContainers[0].resources.limits[ephemeral-storage]: fractional byte value "4m" is invalid, must be an integer`, 327 `spec.initContainers[0].resources.limits[memory]: fractional byte value "4m" is invalid, must be an integer`, 328 `spec.containers[0].resources.requests[ephemeral-storage]: fractional byte value "4m" is invalid, must be an integer`, 329 `spec.containers[0].resources.requests[memory]: fractional byte value "4m" is invalid, must be an integer`, 330 `spec.containers[0].resources.limits[ephemeral-storage]: fractional byte value "4m" is invalid, must be an integer`, 331 `spec.containers[0].resources.limits[memory]: fractional byte value "4m" is invalid, must be an integer`, 332 `spec.overhead[ephemeral-storage]: fractional byte value "4m" is invalid, must be an integer`, 333 `spec.overhead[memory]: fractional byte value "4m" is invalid, must be an integer`, 334 }, 335 }, 336 { 337 name: "node labels in nodeSelector", 338 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 339 NodeSelector: map[string]string{ 340 `beta.kubernetes.io/arch`: `true`, 341 `beta.kubernetes.io/os`: `true`, 342 `failure-domain.beta.kubernetes.io/region`: `true`, 343 `failure-domain.beta.kubernetes.io/zone`: `true`, 344 `beta.kubernetes.io/instance-type`: `true`, 345 }, 346 }}, 347 expected: []string{ 348 `spec.nodeSelector[beta.kubernetes.io/arch]: deprecated since v1.14; use "kubernetes.io/arch" instead`, 349 `spec.nodeSelector[beta.kubernetes.io/instance-type]: deprecated since v1.17; use "node.kubernetes.io/instance-type" instead`, 350 `spec.nodeSelector[beta.kubernetes.io/os]: deprecated since v1.14; use "kubernetes.io/os" instead`, 351 `spec.nodeSelector[failure-domain.beta.kubernetes.io/region]: deprecated since v1.17; use "topology.kubernetes.io/region" instead`, 352 `spec.nodeSelector[failure-domain.beta.kubernetes.io/zone]: deprecated since v1.17; use "topology.kubernetes.io/zone" instead`, 353 }, 354 }, 355 { 356 name: "node labels in affinity requiredDuringSchedulingIgnoredDuringExecution", 357 template: &api.PodTemplateSpec{ 358 Spec: api.PodSpec{ 359 Affinity: &api.Affinity{ 360 NodeAffinity: &api.NodeAffinity{ 361 RequiredDuringSchedulingIgnoredDuringExecution: &api.NodeSelector{ 362 NodeSelectorTerms: []api.NodeSelectorTerm{ 363 { 364 MatchExpressions: []api.NodeSelectorRequirement{ 365 {Key: `foo`}, 366 {Key: `beta.kubernetes.io/arch`}, 367 {Key: `beta.kubernetes.io/os`}, 368 {Key: `failure-domain.beta.kubernetes.io/region`}, 369 {Key: `failure-domain.beta.kubernetes.io/zone`}, 370 {Key: `beta.kubernetes.io/instance-type`}, 371 }, 372 }, 373 }, 374 }, 375 }, 376 }, 377 }, 378 }, 379 expected: []string{ 380 `spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[1].key: beta.kubernetes.io/arch is deprecated since v1.14; use "kubernetes.io/arch" instead`, 381 `spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[2].key: beta.kubernetes.io/os is deprecated since v1.14; use "kubernetes.io/os" instead`, 382 `spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[3].key: failure-domain.beta.kubernetes.io/region is deprecated since v1.17; use "topology.kubernetes.io/region" instead`, 383 `spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[4].key: failure-domain.beta.kubernetes.io/zone is deprecated since v1.17; use "topology.kubernetes.io/zone" instead`, 384 `spec.affinity.nodeAffinity.requiredDuringSchedulingIgnoredDuringExecution.nodeSelectorTerms[0].matchExpressions[5].key: beta.kubernetes.io/instance-type is deprecated since v1.17; use "node.kubernetes.io/instance-type" instead`, 385 }, 386 }, 387 { 388 name: "node labels in affinity preferredDuringSchedulingIgnoredDuringExecution", 389 template: &api.PodTemplateSpec{ 390 Spec: api.PodSpec{ 391 Affinity: &api.Affinity{ 392 NodeAffinity: &api.NodeAffinity{ 393 PreferredDuringSchedulingIgnoredDuringExecution: []api.PreferredSchedulingTerm{ 394 { 395 Preference: api.NodeSelectorTerm{ 396 MatchExpressions: []api.NodeSelectorRequirement{ 397 {Key: `foo`}, 398 {Key: `beta.kubernetes.io/arch`}, 399 {Key: `beta.kubernetes.io/os`}, 400 {Key: `failure-domain.beta.kubernetes.io/region`}, 401 {Key: `failure-domain.beta.kubernetes.io/zone`}, 402 {Key: `beta.kubernetes.io/instance-type`}, 403 }, 404 }, 405 }, 406 }, 407 }, 408 }, 409 }, 410 }, 411 expected: []string{ 412 `spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].preference.matchExpressions[1].key: beta.kubernetes.io/arch is deprecated since v1.14; use "kubernetes.io/arch" instead`, 413 `spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].preference.matchExpressions[2].key: beta.kubernetes.io/os is deprecated since v1.14; use "kubernetes.io/os" instead`, 414 `spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].preference.matchExpressions[3].key: failure-domain.beta.kubernetes.io/region is deprecated since v1.17; use "topology.kubernetes.io/region" instead`, 415 `spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].preference.matchExpressions[4].key: failure-domain.beta.kubernetes.io/zone is deprecated since v1.17; use "topology.kubernetes.io/zone" instead`, 416 `spec.affinity.nodeAffinity.preferredDuringSchedulingIgnoredDuringExecution[0].preference.matchExpressions[5].key: beta.kubernetes.io/instance-type is deprecated since v1.17; use "node.kubernetes.io/instance-type" instead`, 417 }, 418 }, 419 { 420 name: "node labels in topologySpreadConstraints", 421 template: &api.PodTemplateSpec{ 422 Spec: api.PodSpec{ 423 TopologySpreadConstraints: []api.TopologySpreadConstraint{ 424 { 425 TopologyKey: `foo`, 426 LabelSelector: &metav1.LabelSelector{}, 427 }, 428 { 429 TopologyKey: `beta.kubernetes.io/arch`, 430 LabelSelector: &metav1.LabelSelector{}, 431 }, 432 { 433 TopologyKey: `beta.kubernetes.io/os`, 434 LabelSelector: &metav1.LabelSelector{}, 435 }, 436 { 437 TopologyKey: `failure-domain.beta.kubernetes.io/region`, 438 LabelSelector: &metav1.LabelSelector{}, 439 }, 440 { 441 TopologyKey: `failure-domain.beta.kubernetes.io/zone`, 442 LabelSelector: &metav1.LabelSelector{}, 443 }, 444 { 445 TopologyKey: `beta.kubernetes.io/instance-type`, 446 LabelSelector: &metav1.LabelSelector{}, 447 }, 448 }, 449 }, 450 }, 451 expected: []string{ 452 `spec.topologySpreadConstraints[1].topologyKey: beta.kubernetes.io/arch is deprecated since v1.14; use "kubernetes.io/arch" instead`, 453 `spec.topologySpreadConstraints[2].topologyKey: beta.kubernetes.io/os is deprecated since v1.14; use "kubernetes.io/os" instead`, 454 `spec.topologySpreadConstraints[3].topologyKey: failure-domain.beta.kubernetes.io/region is deprecated since v1.17; use "topology.kubernetes.io/region" instead`, 455 `spec.topologySpreadConstraints[4].topologyKey: failure-domain.beta.kubernetes.io/zone is deprecated since v1.17; use "topology.kubernetes.io/zone" instead`, 456 `spec.topologySpreadConstraints[5].topologyKey: beta.kubernetes.io/instance-type is deprecated since v1.17; use "node.kubernetes.io/instance-type" instead`, 457 }, 458 }, 459 { 460 name: "annotations", 461 template: &api.PodTemplateSpec{ 462 ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{ 463 `foo`: `bar`, 464 `scheduler.alpha.kubernetes.io/critical-pod`: `true`, 465 `seccomp.security.alpha.kubernetes.io/pod`: `default`, 466 `container.seccomp.security.alpha.kubernetes.io/foo`: `default`, 467 `security.alpha.kubernetes.io/sysctls`: `a,b,c`, 468 `security.alpha.kubernetes.io/unsafe-sysctls`: `d,e,f`, 469 }}, 470 Spec: api.PodSpec{Containers: []api.Container{{Name: "foo"}}}, 471 }, 472 expected: []string{ 473 `metadata.annotations[scheduler.alpha.kubernetes.io/critical-pod]: non-functional in v1.16+; use the "priorityClassName" field instead`, 474 `metadata.annotations[seccomp.security.alpha.kubernetes.io/pod]: non-functional in v1.27+; use the "seccompProfile" field instead`, 475 `metadata.annotations[container.seccomp.security.alpha.kubernetes.io/foo]: non-functional in v1.27+; use the "seccompProfile" field instead`, 476 `metadata.annotations[security.alpha.kubernetes.io/sysctls]: non-functional in v1.11+; use the "sysctls" field instead`, 477 `metadata.annotations[security.alpha.kubernetes.io/unsafe-sysctls]: non-functional in v1.11+; use the "sysctls" field instead`, 478 }, 479 }, 480 { 481 name: "seccomp fields", 482 template: &api.PodTemplateSpec{ 483 ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{ 484 `seccomp.security.alpha.kubernetes.io/pod`: `default`, 485 `container.seccomp.security.alpha.kubernetes.io/foo`: `default`, 486 }}, 487 Spec: api.PodSpec{ 488 SecurityContext: &api.PodSecurityContext{ 489 SeccompProfile: &api.SeccompProfile{Type: api.SeccompProfileTypeRuntimeDefault}, 490 }, 491 Containers: []api.Container{{ 492 Name: "foo", 493 SecurityContext: &api.SecurityContext{ 494 SeccompProfile: &api.SeccompProfile{Type: api.SeccompProfileTypeRuntimeDefault}, 495 }, 496 }}, 497 }, 498 }, 499 expected: []string{}, 500 }, 501 { 502 name: "pod with ephemeral volume source 200Mi", 503 template: &api.PodTemplateSpec{ 504 ObjectMeta: metav1.ObjectMeta{}, 505 Spec: api.PodSpec{Volumes: []api.Volume{ 506 {Name: "ephemeral-volume", VolumeSource: api.VolumeSource{Ephemeral: &api.EphemeralVolumeSource{ 507 VolumeClaimTemplate: &api.PersistentVolumeClaimTemplate{ 508 Spec: api.PersistentVolumeClaimSpec{Resources: api.VolumeResourceRequirements{ 509 Requests: api.ResourceList{api.ResourceStorage: resource.MustParse("200Mi")}}}, 510 }, 511 }}}}}, 512 }, 513 expected: []string{}, 514 }, 515 { 516 name: "pod with ephemeral volume source 200m", 517 template: &api.PodTemplateSpec{ 518 ObjectMeta: metav1.ObjectMeta{}, 519 Spec: api.PodSpec{Volumes: []api.Volume{ 520 {Name: "ephemeral-volume", VolumeSource: api.VolumeSource{Ephemeral: &api.EphemeralVolumeSource{ 521 VolumeClaimTemplate: &api.PersistentVolumeClaimTemplate{ 522 Spec: api.PersistentVolumeClaimSpec{Resources: api.VolumeResourceRequirements{ 523 Requests: api.ResourceList{api.ResourceStorage: resource.MustParse("200m")}}}, 524 }, 525 }}}}}, 526 }, 527 expected: []string{ 528 `spec.volumes[0].ephemeral.volumeClaimTemplate.spec.resources.requests[storage]: fractional byte value "200m" is invalid, must be an integer`, 529 }, 530 }, 531 { 532 name: "terminationGracePeriodSeconds is negative", 533 template: &api.PodTemplateSpec{ 534 ObjectMeta: metav1.ObjectMeta{}, 535 Spec: api.PodSpec{ 536 TerminationGracePeriodSeconds: utilpointer.Int64Ptr(-1), 537 }, 538 }, 539 expected: []string{ 540 `spec.terminationGracePeriodSeconds: must be >= 0; negative values are invalid and will be treated as 1`, 541 }, 542 }, 543 { 544 name: "null LabelSelector in topologySpreadConstraints", 545 template: &api.PodTemplateSpec{ 546 ObjectMeta: metav1.ObjectMeta{}, 547 Spec: api.PodSpec{ 548 TopologySpreadConstraints: []api.TopologySpreadConstraint{ 549 { 550 LabelSelector: &metav1.LabelSelector{}, 551 }, 552 { 553 LabelSelector: nil, 554 }, 555 }, 556 }, 557 }, 558 expected: []string{ 559 `spec.topologySpreadConstraints[1].labelSelector: a null labelSelector results in matching no pod`, 560 }, 561 }, 562 { 563 name: "null LabelSelector in PodAffinity", 564 template: &api.PodTemplateSpec{ 565 ObjectMeta: metav1.ObjectMeta{}, 566 Spec: api.PodSpec{ 567 Affinity: &api.Affinity{ 568 PodAffinity: &api.PodAffinity{ 569 RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{ 570 { 571 LabelSelector: &metav1.LabelSelector{}, 572 }, 573 { 574 LabelSelector: nil, 575 }, 576 }, 577 PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{ 578 { 579 PodAffinityTerm: api.PodAffinityTerm{ 580 LabelSelector: &metav1.LabelSelector{}, 581 }, 582 }, 583 { 584 PodAffinityTerm: api.PodAffinityTerm{ 585 LabelSelector: nil, 586 }, 587 }, 588 }, 589 }, 590 PodAntiAffinity: &api.PodAntiAffinity{ 591 RequiredDuringSchedulingIgnoredDuringExecution: []api.PodAffinityTerm{ 592 { 593 LabelSelector: &metav1.LabelSelector{}, 594 }, 595 { 596 LabelSelector: nil, 597 }, 598 }, 599 PreferredDuringSchedulingIgnoredDuringExecution: []api.WeightedPodAffinityTerm{ 600 { 601 PodAffinityTerm: api.PodAffinityTerm{ 602 LabelSelector: &metav1.LabelSelector{}, 603 }, 604 }, 605 { 606 PodAffinityTerm: api.PodAffinityTerm{ 607 LabelSelector: nil, 608 }, 609 }, 610 }, 611 }, 612 }, 613 }, 614 }, 615 expected: []string{ 616 `spec.affinity.podAffinity.requiredDuringSchedulingIgnoredDuringExecution[1].labelSelector: a null labelSelector results in matching no pod`, 617 `spec.affinity.podAffinity.preferredDuringSchedulingIgnoredDuringExecution[1].podAffinityTerm.labelSelector: a null labelSelector results in matching no pod`, 618 `spec.affinity.podAntiAffinity.requiredDuringSchedulingIgnoredDuringExecution[1].labelSelector: a null labelSelector results in matching no pod`, 619 `spec.affinity.podAntiAffinity.preferredDuringSchedulingIgnoredDuringExecution[1].podAffinityTerm.labelSelector: a null labelSelector results in matching no pod`, 620 }, 621 }, 622 { 623 name: "container no ports", 624 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 625 Containers: []api.Container{{ 626 Name: "foo", 627 Ports: []api.ContainerPort{}, 628 }}, 629 }}, 630 expected: []string{}, 631 }, 632 { 633 name: "one container, one port", 634 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 635 Containers: []api.Container{{ 636 Name: "foo", 637 Ports: []api.ContainerPort{ 638 {ContainerPort: 80, HostPort: 80}, 639 }, 640 }}, 641 }}, 642 expected: []string{}, 643 }, 644 { 645 name: "one container, two ports, same protocol, different ports", 646 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 647 Containers: []api.Container{{ 648 Name: "foo", 649 Ports: []api.ContainerPort{ 650 {ContainerPort: 80, Protocol: api.ProtocolUDP}, 651 {ContainerPort: 81, Protocol: api.ProtocolUDP}, 652 }, 653 }}, 654 }}, 655 expected: []string{}, 656 }, 657 { 658 name: "one container, two ports, different protocols, same port", 659 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 660 Containers: []api.Container{{ 661 Name: "foo", 662 Ports: []api.ContainerPort{ 663 {ContainerPort: 80, Protocol: api.ProtocolUDP}, 664 {ContainerPort: 80, Protocol: api.ProtocolTCP}, 665 }, 666 }}, 667 }}, 668 expected: []string{}, 669 }, 670 { 671 name: "one container, two ports, same protocol, same port, different hostport", 672 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 673 Containers: []api.Container{{ 674 Name: "foo", 675 Ports: []api.ContainerPort{ 676 {ContainerPort: 80, Protocol: api.ProtocolTCP, HostPort: 80}, 677 {ContainerPort: 80, Protocol: api.ProtocolTCP, HostPort: 81}, 678 }, 679 }}, 680 }}, 681 expected: []string{}, 682 }, 683 { 684 name: "one container, two ports, same protocol, port and hostPort, different hostIP", 685 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 686 Containers: []api.Container{{ 687 Name: "foo", 688 Ports: []api.ContainerPort{ 689 {ContainerPort: 80, Protocol: api.ProtocolTCP, HostPort: 80, HostIP: "10.0.0.1"}, 690 {ContainerPort: 80, Protocol: api.ProtocolTCP, HostPort: 80, HostIP: "10.0.0.2"}, 691 }, 692 }}, 693 }}, 694 expected: []string{}, 695 }, 696 { 697 name: "two containers, one port each, same protocol, different ports", 698 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 699 Containers: []api.Container{{ 700 Name: "foo", 701 Ports: []api.ContainerPort{ 702 {ContainerPort: 80, Protocol: api.ProtocolUDP}, 703 }, 704 }, { 705 Name: "bar", 706 Ports: []api.ContainerPort{ 707 {ContainerPort: 81, Protocol: api.ProtocolUDP}, 708 }, 709 }}, 710 }}, 711 expected: []string{}, 712 }, 713 { 714 name: "two containers, one port each, different protocols, same port", 715 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 716 Containers: []api.Container{{ 717 Name: "foo", 718 Ports: []api.ContainerPort{ 719 {ContainerPort: 80, Protocol: api.ProtocolUDP}, 720 }, 721 }, { 722 Name: "bar", 723 Ports: []api.ContainerPort{ 724 {ContainerPort: 80, Protocol: api.ProtocolTCP}, 725 }, 726 }}, 727 }}, 728 expected: []string{}, 729 }, 730 { 731 name: "two containers, one port each, same protocol, same port, different hostport", 732 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 733 Containers: []api.Container{{ 734 Name: "foo", 735 Ports: []api.ContainerPort{ 736 {ContainerPort: 80, Protocol: api.ProtocolTCP, HostPort: 80}, 737 }, 738 }, { 739 Name: "bar", 740 Ports: []api.ContainerPort{ 741 {ContainerPort: 80, Protocol: api.ProtocolTCP, HostPort: 81}, 742 }, 743 }}, 744 }}, 745 expected: []string{}, 746 }, 747 { 748 name: "two containers, one port each, same protocol, port and hostPort, different hostIP", 749 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 750 Containers: []api.Container{{ 751 Name: "foo", 752 Ports: []api.ContainerPort{ 753 {ContainerPort: 80, Protocol: api.ProtocolTCP, HostPort: 80, HostIP: "10.0.0.1"}, 754 }, 755 }, { 756 Name: "bar", 757 Ports: []api.ContainerPort{ 758 {ContainerPort: 80, Protocol: api.ProtocolTCP, HostPort: 80, HostIP: "10.0.0.2"}, 759 }, 760 }}, 761 }}, 762 expected: []string{}, 763 }, 764 { 765 name: "duplicate container ports with same port and protocol", 766 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 767 Containers: []api.Container{{ 768 Name: "foo", 769 Ports: []api.ContainerPort{ 770 {ContainerPort: 80, Protocol: api.ProtocolUDP}, 771 {ContainerPort: 80, Protocol: api.ProtocolUDP}, 772 }, 773 }}, 774 }}, 775 expected: []string{ 776 `spec.containers[0].ports[1]: duplicate port definition with spec.containers[0].ports[0]`, 777 }, 778 }, 779 { 780 name: "duplicate container ports with same port, hostPort and protocol", 781 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 782 Containers: []api.Container{{ 783 Name: "foo", 784 Ports: []api.ContainerPort{ 785 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP}, 786 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP}, 787 }, 788 }}, 789 }}, 790 expected: []string{ 791 `spec.containers[0].ports[1]: duplicate port definition with spec.containers[0].ports[0]`, 792 }, 793 }, 794 { 795 name: "duplicate container ports with same port, host port, host IP and protocol", 796 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 797 Containers: []api.Container{{ 798 Name: "foo", 799 Ports: []api.ContainerPort{ 800 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP, HostIP: "10.0.0.1"}, 801 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP, HostIP: "10.0.0.1"}, 802 }, 803 }}, 804 }}, 805 expected: []string{ 806 `spec.containers[0].ports[1]: duplicate port definition with spec.containers[0].ports[0]`, 807 }, 808 }, 809 { 810 name: "one container port hostIP set without host port set", 811 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 812 Containers: []api.Container{{ 813 Name: "foo", 814 Ports: []api.ContainerPort{ 815 {ContainerPort: 80, Protocol: api.ProtocolUDP, HostIP: "10.0.0.1"}, 816 }, 817 }}, 818 }}, 819 expected: []string{ 820 `spec.containers[0].ports[0]: hostIP set without hostPort: {Name: HostPort:0 ContainerPort:80 Protocol:UDP HostIP:10.0.0.1}`, 821 }, 822 }, 823 { 824 name: "duplicate container ports with one host port set and one without", 825 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 826 Containers: []api.Container{{ 827 Name: "foo", 828 Ports: []api.ContainerPort{ 829 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP, HostIP: "10.0.0.1"}, 830 {ContainerPort: 80, Protocol: api.ProtocolUDP, HostIP: "10.0.0.1"}, 831 }, 832 }}, 833 }}, 834 expected: []string{ 835 `spec.containers[0].ports[1]: overlapping port definition with spec.containers[0].ports[0]`, 836 `spec.containers[0].ports[1]: hostIP set without hostPort: {Name: HostPort:0 ContainerPort:80 Protocol:UDP HostIP:10.0.0.1}`, 837 }, 838 }, 839 { 840 name: "duplicate container ports without one host IP set and two with", 841 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 842 Containers: []api.Container{{ 843 Name: "foo", 844 Ports: []api.ContainerPort{ 845 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP, HostIP: "10.0.0.1"}, 846 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP}, 847 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP, HostIP: "10.0.0.2"}, 848 }, 849 }}, 850 }}, 851 expected: []string{ 852 `spec.containers[0].ports[1]: dangerously ambiguous port definition with spec.containers[0].ports[0]`, 853 `spec.containers[0].ports[2]: dangerously ambiguous port definition with spec.containers[0].ports[1]`, 854 }, 855 }, 856 { 857 name: "duplicate container ports with one host IP set and one without", 858 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 859 Containers: []api.Container{{ 860 Name: "foo", 861 Ports: []api.ContainerPort{ 862 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP, HostIP: "10.0.0.1"}, 863 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP}, 864 }, 865 }}, 866 }}, 867 expected: []string{ 868 `spec.containers[0].ports[1]: dangerously ambiguous port definition with spec.containers[0].ports[0]`, 869 }, 870 }, 871 { 872 name: "duplicate containers with same port and protocol", 873 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 874 Containers: []api.Container{{ 875 Name: "foo", 876 Ports: []api.ContainerPort{ 877 {ContainerPort: 80, Protocol: api.ProtocolUDP}, 878 }, 879 }, { 880 Name: "bar", 881 Ports: []api.ContainerPort{ 882 {ContainerPort: 80, Protocol: api.ProtocolUDP}, 883 }, 884 }}, 885 }}, 886 expected: []string{ 887 `spec.containers[1].ports[0]: duplicate port definition with spec.containers[0].ports[0]`, 888 }, 889 }, 890 { 891 name: "duplicate containers with same port, hostPort and protocol", 892 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 893 Containers: []api.Container{{ 894 Name: "foo", 895 Ports: []api.ContainerPort{ 896 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP}, 897 }, 898 }, { 899 Name: "bar", 900 Ports: []api.ContainerPort{ 901 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP}, 902 }, 903 }}, 904 }}, 905 expected: []string{ 906 `spec.containers[1].ports[0]: duplicate port definition with spec.containers[0].ports[0]`, 907 }, 908 }, 909 { 910 name: "duplicate containers with same port, host port, host IP and protocol", 911 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 912 Containers: []api.Container{{ 913 Name: "foo", 914 Ports: []api.ContainerPort{ 915 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP, HostIP: "10.0.0.1"}, 916 }, 917 }, { 918 Name: "bar", 919 Ports: []api.ContainerPort{ 920 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP, HostIP: "10.0.0.1"}, 921 }, 922 }}, 923 }}, 924 expected: []string{ 925 `spec.containers[1].ports[0]: duplicate port definition with spec.containers[0].ports[0]`, 926 }, 927 }, 928 { 929 name: "duplicate containers with one host port set and one without", 930 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 931 Containers: []api.Container{{ 932 Name: "foo", 933 Ports: []api.ContainerPort{ 934 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP, HostIP: "10.0.0.1"}, 935 }, 936 }, { 937 Name: "bar", 938 Ports: []api.ContainerPort{ 939 {ContainerPort: 80, Protocol: api.ProtocolUDP, HostIP: "10.0.0.1"}, 940 }, 941 }}, 942 }}, 943 expected: []string{ 944 `spec.containers[1].ports[0]: overlapping port definition with spec.containers[0].ports[0]`, 945 `spec.containers[1].ports[0]: hostIP set without hostPort: {Name: HostPort:0 ContainerPort:80 Protocol:UDP HostIP:10.0.0.1}`, 946 }, 947 }, 948 { 949 name: "duplicate container ports without one host IP set and one with", 950 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 951 Containers: []api.Container{{ 952 Name: "foo", 953 Ports: []api.ContainerPort{ 954 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP}, 955 }, 956 }, { 957 Name: "bar", 958 Ports: []api.ContainerPort{ 959 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP, HostIP: "10.0.0.1"}, 960 }, 961 }}, 962 }}, 963 expected: []string{ 964 `spec.containers[1].ports[0]: dangerously ambiguous port definition with spec.containers[0].ports[0]`, 965 }, 966 }, 967 { 968 name: "duplicate container ports with one host IP set and one without", 969 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 970 Containers: []api.Container{{ 971 Name: "foo", 972 Ports: []api.ContainerPort{ 973 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP, HostIP: "10.0.0.1"}, 974 }, 975 }, { 976 Name: "bar", 977 Ports: []api.ContainerPort{ 978 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP}, 979 }, 980 }}, 981 }}, 982 expected: []string{ 983 `spec.containers[1].ports[0]: dangerously ambiguous port definition with spec.containers[0].ports[0]`, 984 }, 985 }, 986 { 987 name: "create duplicate container ports in two containers", 988 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 989 Containers: []api.Container{ 990 { 991 Name: "foo1", 992 Ports: []api.ContainerPort{ 993 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP}, 994 {ContainerPort: 180, HostPort: 80, Protocol: api.ProtocolUDP}, 995 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP}, 996 }, 997 }, 998 { 999 Name: "foo", 1000 Ports: []api.ContainerPort{ 1001 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP}, 1002 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolUDP}, 1003 }, 1004 }}, 1005 }}, 1006 expected: []string{ 1007 `spec.containers[0].ports[2]: duplicate port definition with spec.containers[0].ports[0]`, 1008 `spec.containers[1].ports[0]: duplicate port definition with spec.containers[0].ports[0]`, 1009 `spec.containers[1].ports[0]: duplicate port definition with spec.containers[0].ports[2]`, 1010 `spec.containers[1].ports[1]: duplicate port definition with spec.containers[0].ports[0]`, 1011 `spec.containers[1].ports[1]: duplicate port definition with spec.containers[0].ports[2]`, 1012 `spec.containers[1].ports[1]: duplicate port definition with spec.containers[1].ports[0]`, 1013 }, 1014 }, 1015 { 1016 name: "update duplicate container ports in two containers", 1017 template: &api.PodTemplateSpec{Spec: api.PodSpec{ 1018 Containers: []api.Container{ 1019 { 1020 Name: "foo1", 1021 Ports: []api.ContainerPort{ 1022 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolTCP}, 1023 {ContainerPort: 180, HostPort: 80, Protocol: api.ProtocolTCP}, 1024 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolTCP}, 1025 }, 1026 }, 1027 { 1028 Name: "foo", 1029 Ports: []api.ContainerPort{ 1030 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolTCP}, 1031 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolTCP}, 1032 }, 1033 }}, 1034 }}, 1035 oldTemplate: &api.PodTemplateSpec{Spec: api.PodSpec{ 1036 Containers: []api.Container{ 1037 { 1038 Name: "foo1", 1039 Ports: []api.ContainerPort{ 1040 {ContainerPort: 80, HostPort: 180, Protocol: api.ProtocolTCP}, 1041 {ContainerPort: 180, HostPort: 80, Protocol: api.ProtocolTCP}, 1042 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolTCP}, 1043 }, 1044 }, 1045 { 1046 Name: "foo", 1047 Ports: []api.ContainerPort{ 1048 {ContainerPort: 80, HostPort: 180, Protocol: api.ProtocolTCP}, 1049 {ContainerPort: 80, HostPort: 80, Protocol: api.ProtocolTCP}, 1050 }, 1051 }}, 1052 }}, 1053 expected: []string{ 1054 `spec.containers[0].ports[2]: duplicate port definition with spec.containers[0].ports[0]`, 1055 `spec.containers[1].ports[0]: duplicate port definition with spec.containers[0].ports[0]`, 1056 `spec.containers[1].ports[0]: duplicate port definition with spec.containers[0].ports[2]`, 1057 `spec.containers[1].ports[1]: duplicate port definition with spec.containers[0].ports[0]`, 1058 `spec.containers[1].ports[1]: duplicate port definition with spec.containers[0].ports[2]`, 1059 `spec.containers[1].ports[1]: duplicate port definition with spec.containers[1].ports[0]`, 1060 }, 1061 }, 1062 } 1063 1064 for _, tc := range testcases { 1065 t.Run("podspec_"+tc.name, func(t *testing.T) { 1066 var oldTemplate *api.PodTemplateSpec 1067 if tc.oldTemplate != nil { 1068 oldTemplate = tc.oldTemplate 1069 } 1070 actual := sets.New[string](GetWarningsForPodTemplate(context.TODO(), nil, tc.template, oldTemplate)...) 1071 expected := sets.New[string](tc.expected...) 1072 for _, missing := range sets.List[string](expected.Difference(actual)) { 1073 t.Errorf("missing: %s", missing) 1074 } 1075 for _, extra := range sets.List[string](actual.Difference(expected)) { 1076 t.Errorf("extra: %s", extra) 1077 } 1078 }) 1079 1080 t.Run("pod_"+tc.name, func(t *testing.T) { 1081 var pod *api.Pod 1082 if tc.template != nil { 1083 pod = &api.Pod{ 1084 ObjectMeta: tc.template.ObjectMeta, 1085 Spec: tc.template.Spec, 1086 } 1087 } 1088 actual := sets.New[string](GetWarningsForPod(context.TODO(), pod, &api.Pod{})...) 1089 expected := sets.New[string](tc.expected...) 1090 for _, missing := range sets.List[string](expected.Difference(actual)) { 1091 t.Errorf("missing: %s", missing) 1092 } 1093 for _, extra := range sets.List[string](actual.Difference(expected)) { 1094 t.Errorf("extra: %s", extra) 1095 } 1096 }) 1097 } 1098 } 1099 1100 func TestTemplateOnlyWarnings(t *testing.T) { 1101 testcases := []struct { 1102 name string 1103 template *api.PodTemplateSpec 1104 oldTemplate *api.PodTemplateSpec 1105 expected []string 1106 }{ 1107 { 1108 name: "annotations", 1109 template: &api.PodTemplateSpec{ 1110 ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{ 1111 `container.apparmor.security.beta.kubernetes.io/foo`: `unconfined`, 1112 }}, 1113 Spec: api.PodSpec{Containers: []api.Container{{Name: "foo"}}}, 1114 }, 1115 expected: []string{ 1116 `template.metadata.annotations[container.apparmor.security.beta.kubernetes.io/foo]: deprecated since v1.30; use the "appArmorProfile" field instead`, 1117 }, 1118 }, 1119 { 1120 name: "AppArmor pod field", 1121 template: &api.PodTemplateSpec{ 1122 ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{ 1123 `container.apparmor.security.beta.kubernetes.io/foo`: `unconfined`, 1124 }}, 1125 Spec: api.PodSpec{ 1126 SecurityContext: &api.PodSecurityContext{ 1127 AppArmorProfile: &api.AppArmorProfile{Type: api.AppArmorProfileTypeUnconfined}, 1128 }, 1129 Containers: []api.Container{{ 1130 Name: "foo", 1131 }}, 1132 }, 1133 }, 1134 expected: []string{}, 1135 }, 1136 { 1137 name: "AppArmor container field", 1138 template: &api.PodTemplateSpec{ 1139 ObjectMeta: metav1.ObjectMeta{Annotations: map[string]string{ 1140 `container.apparmor.security.beta.kubernetes.io/foo`: `unconfined`, 1141 }}, 1142 Spec: api.PodSpec{ 1143 Containers: []api.Container{{ 1144 Name: "foo", 1145 SecurityContext: &api.SecurityContext{ 1146 AppArmorProfile: &api.AppArmorProfile{Type: api.AppArmorProfileTypeUnconfined}, 1147 }, 1148 }}, 1149 }, 1150 }, 1151 expected: []string{}, 1152 }, 1153 } 1154 1155 for _, tc := range testcases { 1156 t.Run("podspec_"+tc.name, func(t *testing.T) { 1157 var oldTemplate *api.PodTemplateSpec 1158 if tc.oldTemplate != nil { 1159 oldTemplate = tc.oldTemplate 1160 } 1161 actual := sets.New[string](GetWarningsForPodTemplate(context.TODO(), field.NewPath("template"), tc.template, oldTemplate)...) 1162 expected := sets.New[string](tc.expected...) 1163 for _, missing := range sets.List[string](expected.Difference(actual)) { 1164 t.Errorf("missing: %s", missing) 1165 } 1166 for _, extra := range sets.List[string](actual.Difference(expected)) { 1167 t.Errorf("extra: %s", extra) 1168 } 1169 }) 1170 1171 t.Run("pod_"+tc.name, func(t *testing.T) { 1172 var pod *api.Pod 1173 if tc.template != nil { 1174 pod = &api.Pod{ 1175 ObjectMeta: tc.template.ObjectMeta, 1176 Spec: tc.template.Spec, 1177 } 1178 } 1179 actual := GetWarningsForPod(context.TODO(), pod, &api.Pod{}) 1180 if len(actual) > 0 { 1181 t.Errorf("unexpected template-only warnings on pod: %v", actual) 1182 } 1183 }) 1184 } 1185 }