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