mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-11 21:12:07 +00:00
Merge pull request #117696 from thockin/hostnet_hostport_default_pod_not_podspec
Do hostNet Pod-ports -> hostPorts in Pod defaults
This commit is contained in:
commit
21afcd453a
@ -213,6 +213,30 @@ func TestValidateStatefulSet(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validHostNetPodTemplate := api.PodTemplate{
|
||||||
|
Template: api.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: validLabels,
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
SecurityContext: &api.PodSecurityContext{
|
||||||
|
HostNetwork: true,
|
||||||
|
},
|
||||||
|
RestartPolicy: api.RestartPolicyAlways,
|
||||||
|
DNSPolicy: api.DNSClusterFirst,
|
||||||
|
Containers: []api.Container{{
|
||||||
|
Name: "abc",
|
||||||
|
Image: "image",
|
||||||
|
ImagePullPolicy: "IfNotPresent",
|
||||||
|
Ports: []api.ContainerPort{{
|
||||||
|
ContainerPort: 12345,
|
||||||
|
Protocol: api.ProtocolTCP,
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
|
invalidLabels := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
|
||||||
invalidPodTemplate := api.PodTemplate{
|
invalidPodTemplate := api.PodTemplate{
|
||||||
Template: api.PodTemplateSpec{
|
Template: api.PodTemplateSpec{
|
||||||
@ -254,6 +278,9 @@ func TestValidateStatefulSet(t *testing.T) {
|
|||||||
}, {
|
}, {
|
||||||
name: "alphanumeric name",
|
name: "alphanumeric name",
|
||||||
set: mkStatefulSet(&validPodTemplate, tweakName("abc-123")),
|
set: mkStatefulSet(&validPodTemplate, tweakName("abc-123")),
|
||||||
|
}, {
|
||||||
|
name: "hostNetwork true",
|
||||||
|
set: mkStatefulSet(&validHostNetPodTemplate),
|
||||||
}, {
|
}, {
|
||||||
name: "parallel pod management",
|
name: "parallel pod management",
|
||||||
set: mkStatefulSet(&validPodTemplate, tweakPodManagementPolicy(apps.ParallelPodManagement)),
|
set: mkStatefulSet(&validPodTemplate, tweakPodManagementPolicy(apps.ParallelPodManagement)),
|
||||||
@ -1988,6 +2015,30 @@ func TestValidateDaemonSet(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
validHostNetPodTemplate := api.PodTemplate{
|
||||||
|
Template: api.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: validSelector,
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
SecurityContext: &api.PodSecurityContext{
|
||||||
|
HostNetwork: true,
|
||||||
|
},
|
||||||
|
RestartPolicy: api.RestartPolicyAlways,
|
||||||
|
DNSPolicy: api.DNSClusterFirst,
|
||||||
|
Containers: []api.Container{{
|
||||||
|
Name: "abc",
|
||||||
|
Image: "image",
|
||||||
|
ImagePullPolicy: "IfNotPresent",
|
||||||
|
TerminationMessagePolicy: api.TerminationMessageReadFile,
|
||||||
|
Ports: []api.ContainerPort{{
|
||||||
|
ContainerPort: 12345,
|
||||||
|
Protocol: api.ProtocolTCP,
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
|
invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
|
||||||
invalidPodTemplate := api.PodTemplate{
|
invalidPodTemplate := api.PodTemplate{
|
||||||
Template: api.PodTemplateSpec{
|
Template: api.PodTemplateSpec{
|
||||||
@ -2018,6 +2069,15 @@ func TestValidateDaemonSet(t *testing.T) {
|
|||||||
Type: apps.OnDeleteDaemonSetStrategyType,
|
Type: apps.OnDeleteDaemonSetStrategyType,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "hostnet", Namespace: metav1.NamespaceDefault},
|
||||||
|
Spec: apps.DaemonSetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{MatchLabels: validSelector},
|
||||||
|
Template: validHostNetPodTemplate.Template,
|
||||||
|
UpdateStrategy: apps.DaemonSetUpdateStrategy{
|
||||||
|
Type: apps.OnDeleteDaemonSetStrategyType,
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, successCase := range successCases {
|
for _, successCase := range successCases {
|
||||||
@ -2187,8 +2247,8 @@ func TestValidateDaemonSet(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func validDeployment() *apps.Deployment {
|
func validDeployment(tweaks ...func(d *apps.Deployment)) *apps.Deployment {
|
||||||
return &apps.Deployment{
|
d := &apps.Deployment{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "abc",
|
Name: "abc",
|
||||||
Namespace: metav1.NamespaceDefault,
|
Namespace: metav1.NamespaceDefault,
|
||||||
@ -2230,11 +2290,26 @@ func validDeployment() *apps.Deployment {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for _, tweak := range tweaks {
|
||||||
|
tweak(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
return d
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestValidateDeployment(t *testing.T) {
|
func TestValidateDeployment(t *testing.T) {
|
||||||
successCases := []*apps.Deployment{
|
successCases := []*apps.Deployment{
|
||||||
validDeployment(),
|
validDeployment(),
|
||||||
|
validDeployment(func(d *apps.Deployment) {
|
||||||
|
d.Spec.Template.Spec.SecurityContext = &api.PodSecurityContext{
|
||||||
|
HostNetwork: true,
|
||||||
|
}
|
||||||
|
d.Spec.Template.Spec.Containers[0].Ports = []api.ContainerPort{{
|
||||||
|
ContainerPort: 12345,
|
||||||
|
Protocol: api.ProtocolTCP,
|
||||||
|
}}
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
for _, successCase := range successCases {
|
for _, successCase := range successCases {
|
||||||
if errs := ValidateDeployment(successCase, corevalidation.PodValidationOptions{}); len(errs) != 0 {
|
if errs := ValidateDeployment(successCase, corevalidation.PodValidationOptions{}); len(errs) != 0 {
|
||||||
@ -3174,6 +3249,30 @@ func TestValidateReplicaSet(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
validHostNetPodTemplate := api.PodTemplate{
|
||||||
|
Template: api.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: validLabels,
|
||||||
|
},
|
||||||
|
Spec: api.PodSpec{
|
||||||
|
SecurityContext: &api.PodSecurityContext{
|
||||||
|
HostNetwork: true,
|
||||||
|
},
|
||||||
|
RestartPolicy: api.RestartPolicyAlways,
|
||||||
|
DNSPolicy: api.DNSClusterFirst,
|
||||||
|
Containers: []api.Container{{
|
||||||
|
Name: "abc",
|
||||||
|
Image: "image",
|
||||||
|
ImagePullPolicy: "IfNotPresent",
|
||||||
|
TerminationMessagePolicy: api.TerminationMessageReadFile,
|
||||||
|
Ports: []api.ContainerPort{{
|
||||||
|
ContainerPort: 12345,
|
||||||
|
Protocol: api.ProtocolTCP,
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
readWriteVolumePodTemplate := api.PodTemplate{
|
readWriteVolumePodTemplate := api.PodTemplate{
|
||||||
Template: api.PodTemplateSpec{
|
Template: api.PodTemplateSpec{
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
@ -3211,6 +3310,12 @@ func TestValidateReplicaSet(t *testing.T) {
|
|||||||
Selector: &metav1.LabelSelector{MatchLabels: validLabels},
|
Selector: &metav1.LabelSelector{MatchLabels: validLabels},
|
||||||
Template: validPodTemplate.Template,
|
Template: validPodTemplate.Template,
|
||||||
},
|
},
|
||||||
|
}, {
|
||||||
|
ObjectMeta: metav1.ObjectMeta{Name: "hostnet", Namespace: metav1.NamespaceDefault},
|
||||||
|
Spec: apps.ReplicaSetSpec{
|
||||||
|
Selector: &metav1.LabelSelector{MatchLabels: validLabels},
|
||||||
|
Template: validHostNetPodTemplate.Template,
|
||||||
|
},
|
||||||
}, {
|
}, {
|
||||||
ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
|
ObjectMeta: metav1.ObjectMeta{Name: "abc-123", Namespace: metav1.NamespaceDefault},
|
||||||
Spec: apps.ReplicaSetSpec{
|
Spec: apps.ReplicaSetSpec{
|
||||||
|
@ -99,6 +99,17 @@ func TestValidateJob(t *testing.T) {
|
|||||||
validPodTemplateSpecForGenerated := getValidPodTemplateSpecForGenerated(validGeneratedSelector)
|
validPodTemplateSpecForGenerated := getValidPodTemplateSpecForGenerated(validGeneratedSelector)
|
||||||
validPodTemplateSpecForGeneratedRestartPolicyNever := getValidPodTemplateSpecForGenerated(validGeneratedSelector)
|
validPodTemplateSpecForGeneratedRestartPolicyNever := getValidPodTemplateSpecForGenerated(validGeneratedSelector)
|
||||||
validPodTemplateSpecForGeneratedRestartPolicyNever.Spec.RestartPolicy = api.RestartPolicyNever
|
validPodTemplateSpecForGeneratedRestartPolicyNever.Spec.RestartPolicy = api.RestartPolicyNever
|
||||||
|
validHostNetPodTemplateSpec := func() api.PodTemplateSpec {
|
||||||
|
spec := getValidPodTemplateSpecForGenerated(validGeneratedSelector)
|
||||||
|
spec.Spec.SecurityContext = &api.PodSecurityContext{
|
||||||
|
HostNetwork: true,
|
||||||
|
}
|
||||||
|
spec.Spec.Containers[0].Ports = []api.ContainerPort{{
|
||||||
|
ContainerPort: 12345,
|
||||||
|
Protocol: api.ProtocolTCP,
|
||||||
|
}}
|
||||||
|
return spec
|
||||||
|
}()
|
||||||
|
|
||||||
successCases := map[string]struct {
|
successCases := map[string]struct {
|
||||||
opts JobValidationOptions
|
opts JobValidationOptions
|
||||||
@ -179,6 +190,20 @@ func TestValidateJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"valid hostnet": {
|
||||||
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
|
job: batch.Job{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "myjob",
|
||||||
|
Namespace: metav1.NamespaceDefault,
|
||||||
|
UID: types.UID("1a2b3c"),
|
||||||
|
},
|
||||||
|
Spec: batch.JobSpec{
|
||||||
|
Selector: validGeneratedSelector,
|
||||||
|
Template: validHostNetPodTemplateSpec,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
"valid NonIndexed completion mode": {
|
"valid NonIndexed completion mode": {
|
||||||
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
opts: JobValidationOptions{RequirePrefixedLabels: true},
|
||||||
job: batch.Job{
|
job: batch.Job{
|
||||||
@ -1784,6 +1809,17 @@ func TestValidateCronJob(t *testing.T) {
|
|||||||
validManualSelector := getValidManualSelector()
|
validManualSelector := getValidManualSelector()
|
||||||
validPodTemplateSpec := getValidPodTemplateSpecForGenerated(getValidGeneratedSelector())
|
validPodTemplateSpec := getValidPodTemplateSpecForGenerated(getValidGeneratedSelector())
|
||||||
validPodTemplateSpec.Labels = map[string]string{}
|
validPodTemplateSpec.Labels = map[string]string{}
|
||||||
|
validHostNetPodTemplateSpec := func() api.PodTemplateSpec {
|
||||||
|
spec := getValidPodTemplateSpecForGenerated(getValidGeneratedSelector())
|
||||||
|
spec.Spec.SecurityContext = &api.PodSecurityContext{
|
||||||
|
HostNetwork: true,
|
||||||
|
}
|
||||||
|
spec.Spec.Containers[0].Ports = []api.ContainerPort{{
|
||||||
|
ContainerPort: 12345,
|
||||||
|
Protocol: api.ProtocolTCP,
|
||||||
|
}}
|
||||||
|
return spec
|
||||||
|
}()
|
||||||
|
|
||||||
successCases := map[string]batch.CronJob{
|
successCases := map[string]batch.CronJob{
|
||||||
"basic scheduled job": {
|
"basic scheduled job": {
|
||||||
@ -1802,6 +1838,22 @@ func TestValidateCronJob(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
"hostnet job": {
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Name: "mycronjob",
|
||||||
|
Namespace: metav1.NamespaceDefault,
|
||||||
|
UID: types.UID("1a2b3c"),
|
||||||
|
},
|
||||||
|
Spec: batch.CronJobSpec{
|
||||||
|
Schedule: "* * * * ?",
|
||||||
|
ConcurrencyPolicy: batch.AllowConcurrent,
|
||||||
|
JobTemplate: batch.JobTemplateSpec{
|
||||||
|
Spec: batch.JobSpec{
|
||||||
|
Template: validHostNetPodTemplateSpec,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
"non-standard scheduled": {
|
"non-standard scheduled": {
|
||||||
ObjectMeta: metav1.ObjectMeta{
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
Name: "mycronjob",
|
Name: "mycronjob",
|
||||||
|
@ -199,6 +199,11 @@ func SetDefaults_Pod(obj *v1.Pod) {
|
|||||||
enableServiceLinks := v1.DefaultEnableServiceLinks
|
enableServiceLinks := v1.DefaultEnableServiceLinks
|
||||||
obj.Spec.EnableServiceLinks = &enableServiceLinks
|
obj.Spec.EnableServiceLinks = &enableServiceLinks
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if obj.Spec.HostNetwork {
|
||||||
|
defaultHostNetworkPorts(&obj.Spec.Containers)
|
||||||
|
defaultHostNetworkPorts(&obj.Spec.InitContainers)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
func SetDefaults_PodSpec(obj *v1.PodSpec) {
|
func SetDefaults_PodSpec(obj *v1.PodSpec) {
|
||||||
// New fields added here will break upgrade tests:
|
// New fields added here will break upgrade tests:
|
||||||
@ -211,9 +216,11 @@ func SetDefaults_PodSpec(obj *v1.PodSpec) {
|
|||||||
if obj.RestartPolicy == "" {
|
if obj.RestartPolicy == "" {
|
||||||
obj.RestartPolicy = v1.RestartPolicyAlways
|
obj.RestartPolicy = v1.RestartPolicyAlways
|
||||||
}
|
}
|
||||||
if obj.HostNetwork {
|
if utilfeature.DefaultFeatureGate.Enabled(features.DefaultHostNetworkHostPortsInPodTemplates) {
|
||||||
defaultHostNetworkPorts(&obj.Containers)
|
if obj.HostNetwork {
|
||||||
defaultHostNetworkPorts(&obj.InitContainers)
|
defaultHostNetworkPorts(&obj.Containers)
|
||||||
|
defaultHostNetworkPorts(&obj.InitContainers)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if obj.SecurityContext == nil {
|
if obj.SecurityContext == nil {
|
||||||
obj.SecurityContext = &v1.PodSecurityContext{}
|
obj.SecurityContext = &v1.PodSecurityContext{}
|
||||||
|
@ -47,14 +47,12 @@ func TestWorkloadDefaults(t *testing.T) {
|
|||||||
t.Run("disabled_features", func(t *testing.T) { testWorkloadDefaults(t, false) })
|
t.Run("disabled_features", func(t *testing.T) { testWorkloadDefaults(t, false) })
|
||||||
}
|
}
|
||||||
func testWorkloadDefaults(t *testing.T, featuresEnabled bool) {
|
func testWorkloadDefaults(t *testing.T, featuresEnabled bool) {
|
||||||
features := utilfeature.DefaultFeatureGate.DeepCopy().GetAll()
|
allFeatures := utilfeature.DefaultFeatureGate.DeepCopy().GetAll()
|
||||||
for feature, featureSpec := range features {
|
for feature, featureSpec := range allFeatures {
|
||||||
if !featureSpec.LockToDefault {
|
if !featureSpec.LockToDefault {
|
||||||
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, feature, featuresEnabled)()
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, feature, featuresEnabled)()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rc := &v1.ReplicationController{Spec: v1.ReplicationControllerSpec{Template: &v1.PodTemplateSpec{}}}
|
|
||||||
template := rc.Spec.Template
|
|
||||||
// New defaults under PodTemplateSpec are only acceptable if they would not be applied when reading data from a previous release.
|
// New defaults under PodTemplateSpec are only acceptable if they would not be applied when reading data from a previous release.
|
||||||
// Forbidden: adding a new field `MyField *bool` and defaulting it to a non-nil value
|
// Forbidden: adding a new field `MyField *bool` and defaulting it to a non-nil value
|
||||||
// Forbidden: defaulting an existing field `MyField *bool` when it was previously not defaulted
|
// Forbidden: defaulting an existing field `MyField *bool` when it was previously not defaulted
|
||||||
@ -177,11 +175,59 @@ func testWorkloadDefaults(t *testing.T, featuresEnabled bool) {
|
|||||||
".Spec.Volumes[0].VolumeSource.ScaleIO.StorageMode": `"ThinProvisioned"`,
|
".Spec.Volumes[0].VolumeSource.ScaleIO.StorageMode": `"ThinProvisioned"`,
|
||||||
".Spec.Volumes[0].VolumeSource.Secret.DefaultMode": `420`,
|
".Spec.Volumes[0].VolumeSource.Secret.DefaultMode": `420`,
|
||||||
}
|
}
|
||||||
defaults := detectDefaults(t, rc, reflect.ValueOf(template))
|
t.Run("empty PodTemplateSpec", func(t *testing.T) {
|
||||||
if !reflect.DeepEqual(expectedDefaults, defaults) {
|
rc := &v1.ReplicationController{Spec: v1.ReplicationControllerSpec{Template: &v1.PodTemplateSpec{}}}
|
||||||
t.Errorf("Defaults for PodTemplateSpec changed. This can cause spurious rollouts of workloads on API server upgrade.")
|
template := rc.Spec.Template
|
||||||
t.Logf(cmp.Diff(expectedDefaults, defaults))
|
defaults := detectDefaults(t, rc, reflect.ValueOf(template))
|
||||||
}
|
if !reflect.DeepEqual(expectedDefaults, defaults) {
|
||||||
|
t.Errorf("Defaults for PodTemplateSpec changed. This can cause spurious rollouts of workloads on API server upgrade.")
|
||||||
|
t.Logf(cmp.Diff(expectedDefaults, defaults))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
t.Run("hostnet PodTemplateSpec with ports", func(t *testing.T) {
|
||||||
|
rc := &v1.ReplicationController{
|
||||||
|
Spec: v1.ReplicationControllerSpec{
|
||||||
|
Template: &v1.PodTemplateSpec{
|
||||||
|
Spec: v1.PodSpec{
|
||||||
|
HostNetwork: true,
|
||||||
|
Containers: []v1.Container{{
|
||||||
|
Ports: []v1.ContainerPort{{
|
||||||
|
ContainerPort: 12345,
|
||||||
|
Protocol: v1.ProtocolTCP,
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
template := rc.Spec.Template
|
||||||
|
defaults := detectDefaults(t, rc, reflect.ValueOf(template))
|
||||||
|
expected := func() map[string]string {
|
||||||
|
// Set values that are known inputs
|
||||||
|
m := map[string]string{
|
||||||
|
".Spec.HostNetwork": "true",
|
||||||
|
".Spec.Containers[0].Ports[0].ContainerPort": "12345",
|
||||||
|
}
|
||||||
|
if utilfeature.DefaultFeatureGate.Enabled(features.DefaultHostNetworkHostPortsInPodTemplates) {
|
||||||
|
m[".Spec.Containers"] = `[{"name":"","ports":[{"hostPort":12345,"containerPort":12345,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"IfNotPresent"}]`
|
||||||
|
m[".Spec.Containers[0].Ports"] = `[{"hostPort":12345,"containerPort":12345,"protocol":"TCP"}]`
|
||||||
|
m[".Spec.Containers[0].Ports[0].HostPort"] = "12345"
|
||||||
|
} else {
|
||||||
|
m[".Spec.Containers"] = `[{"name":"","ports":[{"containerPort":12345,"protocol":"TCP"}],"resources":{},"terminationMessagePath":"/dev/termination-log","terminationMessagePolicy":"File","imagePullPolicy":"IfNotPresent"}]`
|
||||||
|
m[".Spec.Containers[0].Ports"] = `[{"containerPort":12345,"protocol":"TCP"}]`
|
||||||
|
}
|
||||||
|
for k, v := range expectedDefaults {
|
||||||
|
if _, found := m[k]; !found {
|
||||||
|
m[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m
|
||||||
|
}()
|
||||||
|
if !reflect.DeepEqual(expected, defaults) {
|
||||||
|
t.Errorf("Defaults for PodTemplateSpec changed. This can cause spurious rollouts of workloads on API server upgrade.")
|
||||||
|
t.Logf(cmp.Diff(expected, defaults))
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// TestPodDefaults detects changes to defaults within PodSpec.
|
// TestPodDefaults detects changes to defaults within PodSpec.
|
||||||
@ -330,6 +376,78 @@ func testPodDefaults(t *testing.T, featuresEnabled bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPodHostNetworkDefaults(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
gate bool
|
||||||
|
hostNet bool
|
||||||
|
expectPodDefault bool
|
||||||
|
expectPodSpecDefault bool
|
||||||
|
}{{
|
||||||
|
name: "gate disabled, hostNetwork=false",
|
||||||
|
gate: false,
|
||||||
|
hostNet: false,
|
||||||
|
expectPodDefault: false,
|
||||||
|
expectPodSpecDefault: false,
|
||||||
|
}, {
|
||||||
|
name: "gate disabled, hostNetwork=true",
|
||||||
|
gate: false,
|
||||||
|
hostNet: true,
|
||||||
|
expectPodDefault: true,
|
||||||
|
expectPodSpecDefault: false,
|
||||||
|
}, {
|
||||||
|
name: "gate enabled, hostNetwork=false",
|
||||||
|
gate: true,
|
||||||
|
hostNet: false,
|
||||||
|
expectPodDefault: false,
|
||||||
|
expectPodSpecDefault: false,
|
||||||
|
}, {
|
||||||
|
name: "gate enabled, hostNetwork=true",
|
||||||
|
gate: true,
|
||||||
|
hostNet: true,
|
||||||
|
expectPodDefault: true,
|
||||||
|
expectPodSpecDefault: true,
|
||||||
|
}}
|
||||||
|
|
||||||
|
for _, tc := range cases {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features.DefaultHostNetworkHostPortsInPodTemplates, tc.gate)()
|
||||||
|
|
||||||
|
const portNum = 12345
|
||||||
|
spec := v1.PodSpec{
|
||||||
|
HostNetwork: tc.hostNet,
|
||||||
|
Containers: []v1.Container{{
|
||||||
|
Ports: []v1.ContainerPort{{
|
||||||
|
ContainerPort: portNum,
|
||||||
|
Protocol: v1.ProtocolTCP,
|
||||||
|
// Note: HostPort is not set
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Pod defaulting.
|
||||||
|
p := v1.Pod{Spec: *spec.DeepCopy()}
|
||||||
|
corev1.SetDefaults_Pod(&p)
|
||||||
|
if got := p.Spec.Containers[0].Ports[0].HostPort; tc.expectPodDefault && got == 0 {
|
||||||
|
t.Errorf("expected Pod HostPort to be defaulted, got %v", got)
|
||||||
|
}
|
||||||
|
if got := p.Spec.Containers[0].Ports[0].HostPort; !tc.expectPodDefault && got != 0 {
|
||||||
|
t.Errorf("expected Pod HostPort to be 0, got %v", got)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test PodSpec defaulting.
|
||||||
|
s := spec.DeepCopy()
|
||||||
|
corev1.SetDefaults_PodSpec(s)
|
||||||
|
if got := s.Containers[0].Ports[0].HostPort; tc.expectPodSpecDefault && got == 0 {
|
||||||
|
t.Errorf("expected PodSpec HostPort to be defaulted, got %v", got)
|
||||||
|
}
|
||||||
|
if got := s.Containers[0].Ports[0].HostPort; !tc.expectPodSpecDefault && got != 0 {
|
||||||
|
t.Errorf("expected PodSpec HostPort to be 0, got %v", got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type testPath struct {
|
type testPath struct {
|
||||||
path string
|
path string
|
||||||
value reflect.Value
|
value reflect.Value
|
||||||
@ -359,9 +477,12 @@ func detectDefaults(t *testing.T, obj runtime.Object, v reflect.Value) map[strin
|
|||||||
|
|
||||||
case visit.value.Kind() == reflect.Slice:
|
case visit.value.Kind() == reflect.Slice:
|
||||||
if !visit.value.IsNil() {
|
if !visit.value.IsNil() {
|
||||||
// if we already have a value, we got defaulted
|
// if we already have a value, we either got defaulted or there
|
||||||
|
// was a fixed input - flag it and see if we can descend
|
||||||
|
// anyway.
|
||||||
marshaled, _ := json.Marshal(defaultedV.Interface())
|
marshaled, _ := json.Marshal(defaultedV.Interface())
|
||||||
defaults[visit.path] = string(marshaled)
|
defaults[visit.path] = string(marshaled)
|
||||||
|
toVisit = append(toVisit, testPath{path: visit.path + "[0]", value: visit.value.Index(0)})
|
||||||
} else if visit.value.Type().Elem().Kind() == reflect.Struct {
|
} else if visit.value.Type().Elem().Kind() == reflect.Struct {
|
||||||
if strings.HasPrefix(visit.path, ".ObjectMeta.ManagedFields[") {
|
if strings.HasPrefix(visit.path, ".ObjectMeta.ManagedFields[") {
|
||||||
break
|
break
|
||||||
|
@ -3481,15 +3481,35 @@ func validatePodDNSConfig(dnsConfig *core.PodDNSConfig, dnsPolicy *core.DNSPolic
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateHostNetwork(hostNetwork bool, containers []core.Container, fldPath *field.Path) field.ErrorList {
|
// validatePodHostNetworkDeps checks fields which depend on whether HostNetwork is
|
||||||
|
// true or not. It should be called on all PodSpecs, but opts can change what
|
||||||
|
// is enforce. E.g. opts.ResourceIsPod should only be set when called in the
|
||||||
|
// context of a Pod, and not on PodSpecs which are embedded in other resources
|
||||||
|
// (e.g. Deployments).
|
||||||
|
func validatePodHostNetworkDeps(spec *core.PodSpec, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
|
||||||
|
// For <reasons> we keep `.HostNetwork` in .SecurityContext on the internal
|
||||||
|
// version of Pod.
|
||||||
|
hostNetwork := false
|
||||||
|
if spec.SecurityContext != nil {
|
||||||
|
hostNetwork = spec.SecurityContext.HostNetwork
|
||||||
|
}
|
||||||
|
|
||||||
allErrors := field.ErrorList{}
|
allErrors := field.ErrorList{}
|
||||||
|
|
||||||
if hostNetwork {
|
if hostNetwork {
|
||||||
for i, container := range containers {
|
fldPath := fldPath.Child("containers")
|
||||||
|
for i, container := range spec.Containers {
|
||||||
portsPath := fldPath.Index(i).Child("ports")
|
portsPath := fldPath.Index(i).Child("ports")
|
||||||
for i, port := range container.Ports {
|
for i, port := range container.Ports {
|
||||||
idxPath := portsPath.Index(i)
|
idxPath := portsPath.Index(i)
|
||||||
if port.HostPort != port.ContainerPort {
|
// At this point, we know that HostNetwork is true. If this
|
||||||
allErrors = append(allErrors, field.Invalid(idxPath.Child("containerPort"), port.ContainerPort, "must match `hostPort` when `hostNetwork` is true"))
|
// PodSpec is in a Pod (opts.ResourceIsPod), then HostPort must
|
||||||
|
// be the same value as ContainerPort. If this PodSpec is in
|
||||||
|
// some other resource (e.g. Deployment) we allow 0 (i.e.
|
||||||
|
// unspecified) because it will be defaulted when the Pod is
|
||||||
|
// ultimately created, but we do not allow any other values.
|
||||||
|
if hp, cp := port.HostPort, port.ContainerPort; (opts.ResourceIsPod || hp != 0) && hp != cp {
|
||||||
|
allErrors = append(allErrors, field.Invalid(idxPath.Child("hostPort"), port.HostPort, "must match `containerPort` when `hostNetwork` is true"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3694,19 +3714,23 @@ type PodValidationOptions struct {
|
|||||||
AllowInvalidTopologySpreadConstraintLabelSelector bool
|
AllowInvalidTopologySpreadConstraintLabelSelector bool
|
||||||
// Allow node selector additions for gated pods.
|
// Allow node selector additions for gated pods.
|
||||||
AllowMutableNodeSelectorAndNodeAffinity bool
|
AllowMutableNodeSelectorAndNodeAffinity bool
|
||||||
|
// The top-level resource being validated is a Pod, not just a PodSpec
|
||||||
|
// embedded in some other resource.
|
||||||
|
ResourceIsPod bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// validatePodMetadataAndSpec tests if required fields in the pod.metadata and pod.spec are set,
|
// validatePodMetadataAndSpec tests if required fields in the pod.metadata and pod.spec are set,
|
||||||
// and is called by ValidatePodCreate and ValidatePodUpdate.
|
// and is called by ValidatePodCreate and ValidatePodUpdate.
|
||||||
func validatePodMetadataAndSpec(pod *core.Pod, opts PodValidationOptions) field.ErrorList {
|
func validatePodMetadataAndSpec(pod *core.Pod, opts PodValidationOptions) field.ErrorList {
|
||||||
fldPath := field.NewPath("metadata")
|
metaPath := field.NewPath("metadata")
|
||||||
allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, fldPath)
|
specPath := field.NewPath("spec")
|
||||||
allErrs = append(allErrs, ValidatePodSpecificAnnotations(pod.ObjectMeta.Annotations, &pod.Spec, fldPath.Child("annotations"), opts)...)
|
|
||||||
allErrs = append(allErrs, ValidatePodSpec(&pod.Spec, &pod.ObjectMeta, field.NewPath("spec"), opts)...)
|
allErrs := ValidateObjectMeta(&pod.ObjectMeta, true, ValidatePodName, metaPath)
|
||||||
|
allErrs = append(allErrs, ValidatePodSpecificAnnotations(pod.ObjectMeta.Annotations, &pod.Spec, metaPath.Child("annotations"), opts)...)
|
||||||
|
allErrs = append(allErrs, ValidatePodSpec(&pod.Spec, &pod.ObjectMeta, specPath, opts)...)
|
||||||
|
|
||||||
// we do additional validation only pertinent for pods and not pod templates
|
// we do additional validation only pertinent for pods and not pod templates
|
||||||
// this was done to preserve backwards compatibility
|
// this was done to preserve backwards compatibility
|
||||||
specPath := field.NewPath("spec")
|
|
||||||
|
|
||||||
if pod.Spec.ServiceAccountName == "" {
|
if pod.Spec.ServiceAccountName == "" {
|
||||||
for vi, volume := range pod.Spec.Volumes {
|
for vi, volume := range pod.Spec.Volumes {
|
||||||
@ -3790,10 +3814,11 @@ func ValidatePodSpec(spec *core.PodSpec, podMeta *metav1.ObjectMeta, fldPath *fi
|
|||||||
allErrs = append(allErrs, validateContainers(spec.Containers, vols, podClaimNames, fldPath.Child("containers"), opts)...)
|
allErrs = append(allErrs, validateContainers(spec.Containers, vols, podClaimNames, fldPath.Child("containers"), opts)...)
|
||||||
allErrs = append(allErrs, validateInitContainers(spec.InitContainers, spec.Containers, vols, podClaimNames, fldPath.Child("initContainers"), opts)...)
|
allErrs = append(allErrs, validateInitContainers(spec.InitContainers, spec.Containers, vols, podClaimNames, fldPath.Child("initContainers"), opts)...)
|
||||||
allErrs = append(allErrs, validateEphemeralContainers(spec.EphemeralContainers, spec.Containers, spec.InitContainers, vols, podClaimNames, fldPath.Child("ephemeralContainers"), opts)...)
|
allErrs = append(allErrs, validateEphemeralContainers(spec.EphemeralContainers, spec.Containers, spec.InitContainers, vols, podClaimNames, fldPath.Child("ephemeralContainers"), opts)...)
|
||||||
|
allErrs = append(allErrs, validatePodHostNetworkDeps(spec, fldPath, opts)...)
|
||||||
allErrs = append(allErrs, validateRestartPolicy(&spec.RestartPolicy, fldPath.Child("restartPolicy"))...)
|
allErrs = append(allErrs, validateRestartPolicy(&spec.RestartPolicy, fldPath.Child("restartPolicy"))...)
|
||||||
allErrs = append(allErrs, validateDNSPolicy(&spec.DNSPolicy, fldPath.Child("dnsPolicy"))...)
|
allErrs = append(allErrs, validateDNSPolicy(&spec.DNSPolicy, fldPath.Child("dnsPolicy"))...)
|
||||||
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.NodeSelector, fldPath.Child("nodeSelector"))...)
|
allErrs = append(allErrs, unversionedvalidation.ValidateLabels(spec.NodeSelector, fldPath.Child("nodeSelector"))...)
|
||||||
allErrs = append(allErrs, ValidatePodSecurityContext(spec.SecurityContext, spec, fldPath, fldPath.Child("securityContext"), opts)...)
|
allErrs = append(allErrs, validatePodSpecSecurityContext(spec.SecurityContext, spec, fldPath, fldPath.Child("securityContext"), opts)...)
|
||||||
allErrs = append(allErrs, validateImagePullSecrets(spec.ImagePullSecrets, fldPath.Child("imagePullSecrets"))...)
|
allErrs = append(allErrs, validateImagePullSecrets(spec.ImagePullSecrets, fldPath.Child("imagePullSecrets"))...)
|
||||||
allErrs = append(allErrs, validateAffinity(spec.Affinity, opts, fldPath.Child("affinity"))...)
|
allErrs = append(allErrs, validateAffinity(spec.Affinity, opts, fldPath.Child("affinity"))...)
|
||||||
allErrs = append(allErrs, validatePodDNSConfig(spec.DNSConfig, &spec.DNSPolicy, fldPath.Child("dnsConfig"), opts)...)
|
allErrs = append(allErrs, validatePodDNSConfig(spec.DNSConfig, &spec.DNSPolicy, fldPath.Child("dnsConfig"), opts)...)
|
||||||
@ -4396,12 +4421,13 @@ func validateSysctls(sysctls []core.Sysctl, fldPath *field.Path) field.ErrorList
|
|||||||
return allErrs
|
return allErrs
|
||||||
}
|
}
|
||||||
|
|
||||||
// ValidatePodSecurityContext test that the specified PodSecurityContext has valid data.
|
// validatePodSpecSecurityContext verifies the SecurityContext of a PodSpec,
|
||||||
func ValidatePodSecurityContext(securityContext *core.PodSecurityContext, spec *core.PodSpec, specPath, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
|
// whether that is defined in a Pod or in an embedded PodSpec (e.g. a
|
||||||
|
// Deployment's pod template).
|
||||||
|
func validatePodSpecSecurityContext(securityContext *core.PodSecurityContext, spec *core.PodSpec, specPath, fldPath *field.Path, opts PodValidationOptions) field.ErrorList {
|
||||||
allErrs := field.ErrorList{}
|
allErrs := field.ErrorList{}
|
||||||
|
|
||||||
if securityContext != nil {
|
if securityContext != nil {
|
||||||
allErrs = append(allErrs, validateHostNetwork(securityContext.HostNetwork, spec.Containers, specPath.Child("containers"))...)
|
|
||||||
if securityContext.FSGroup != nil {
|
if securityContext.FSGroup != nil {
|
||||||
for _, msg := range validation.IsValidGroupID(*securityContext.FSGroup) {
|
for _, msg := range validation.IsValidGroupID(*securityContext.FSGroup) {
|
||||||
allErrs = append(allErrs, field.Invalid(fldPath.Child("fsGroup"), *(securityContext.FSGroup), msg))
|
allErrs = append(allErrs, field.Invalid(fldPath.Child("fsGroup"), *(securityContext.FSGroup), msg))
|
||||||
|
@ -8816,7 +8816,10 @@ func TestValidatePodSpec(t *testing.T) {
|
|||||||
}
|
}
|
||||||
for k, v := range successCases {
|
for k, v := range successCases {
|
||||||
t.Run(k, func(t *testing.T) {
|
t.Run(k, func(t *testing.T) {
|
||||||
if errs := ValidatePodSpec(&v, nil, field.NewPath("field"), PodValidationOptions{}); len(errs) != 0 {
|
opts := PodValidationOptions{
|
||||||
|
ResourceIsPod: true,
|
||||||
|
}
|
||||||
|
if errs := ValidatePodSpec(&v, nil, field.NewPath("field"), opts); len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@ -8868,6 +8871,18 @@ func TestValidatePodSpec(t *testing.T) {
|
|||||||
DNSPolicy: core.DNSClusterFirst,
|
DNSPolicy: core.DNSClusterFirst,
|
||||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||||
},
|
},
|
||||||
|
"with hostNetwork hostPort unspecified": {
|
||||||
|
Containers: []core.Container{
|
||||||
|
{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", Ports: []core.ContainerPort{
|
||||||
|
{HostPort: 0, ContainerPort: 2600, Protocol: "TCP"}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
SecurityContext: &core.PodSecurityContext{
|
||||||
|
HostNetwork: true,
|
||||||
|
},
|
||||||
|
RestartPolicy: core.RestartPolicyAlways,
|
||||||
|
DNSPolicy: core.DNSClusterFirst,
|
||||||
|
},
|
||||||
"with hostNetwork hostPort not equal to containerPort": {
|
"with hostNetwork hostPort not equal to containerPort": {
|
||||||
Containers: []core.Container{
|
Containers: []core.Container{
|
||||||
{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", Ports: []core.ContainerPort{
|
{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", Ports: []core.ContainerPort{
|
||||||
@ -8895,7 +8910,6 @@ func TestValidatePodSpec(t *testing.T) {
|
|||||||
"bad supplementalGroups large than math.MaxInt32": {
|
"bad supplementalGroups large than math.MaxInt32": {
|
||||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||||
SecurityContext: &core.PodSecurityContext{
|
SecurityContext: &core.PodSecurityContext{
|
||||||
HostNetwork: false,
|
|
||||||
SupplementalGroups: []int64{maxGroupID, 1234},
|
SupplementalGroups: []int64{maxGroupID, 1234},
|
||||||
},
|
},
|
||||||
RestartPolicy: core.RestartPolicyAlways,
|
RestartPolicy: core.RestartPolicyAlways,
|
||||||
@ -8904,7 +8918,6 @@ func TestValidatePodSpec(t *testing.T) {
|
|||||||
"bad supplementalGroups less than 0": {
|
"bad supplementalGroups less than 0": {
|
||||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||||
SecurityContext: &core.PodSecurityContext{
|
SecurityContext: &core.PodSecurityContext{
|
||||||
HostNetwork: false,
|
|
||||||
SupplementalGroups: []int64{minGroupID, 1234},
|
SupplementalGroups: []int64{minGroupID, 1234},
|
||||||
},
|
},
|
||||||
RestartPolicy: core.RestartPolicyAlways,
|
RestartPolicy: core.RestartPolicyAlways,
|
||||||
@ -8913,8 +8926,7 @@ func TestValidatePodSpec(t *testing.T) {
|
|||||||
"bad runAsUser large than math.MaxInt32": {
|
"bad runAsUser large than math.MaxInt32": {
|
||||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||||
SecurityContext: &core.PodSecurityContext{
|
SecurityContext: &core.PodSecurityContext{
|
||||||
HostNetwork: false,
|
RunAsUser: &maxUserID,
|
||||||
RunAsUser: &maxUserID,
|
|
||||||
},
|
},
|
||||||
RestartPolicy: core.RestartPolicyAlways,
|
RestartPolicy: core.RestartPolicyAlways,
|
||||||
DNSPolicy: core.DNSClusterFirst,
|
DNSPolicy: core.DNSClusterFirst,
|
||||||
@ -8922,8 +8934,7 @@ func TestValidatePodSpec(t *testing.T) {
|
|||||||
"bad runAsUser less than 0": {
|
"bad runAsUser less than 0": {
|
||||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||||
SecurityContext: &core.PodSecurityContext{
|
SecurityContext: &core.PodSecurityContext{
|
||||||
HostNetwork: false,
|
RunAsUser: &minUserID,
|
||||||
RunAsUser: &minUserID,
|
|
||||||
},
|
},
|
||||||
RestartPolicy: core.RestartPolicyAlways,
|
RestartPolicy: core.RestartPolicyAlways,
|
||||||
DNSPolicy: core.DNSClusterFirst,
|
DNSPolicy: core.DNSClusterFirst,
|
||||||
@ -8931,8 +8942,7 @@ func TestValidatePodSpec(t *testing.T) {
|
|||||||
"bad fsGroup large than math.MaxInt32": {
|
"bad fsGroup large than math.MaxInt32": {
|
||||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||||
SecurityContext: &core.PodSecurityContext{
|
SecurityContext: &core.PodSecurityContext{
|
||||||
HostNetwork: false,
|
FSGroup: &maxGroupID,
|
||||||
FSGroup: &maxGroupID,
|
|
||||||
},
|
},
|
||||||
RestartPolicy: core.RestartPolicyAlways,
|
RestartPolicy: core.RestartPolicyAlways,
|
||||||
DNSPolicy: core.DNSClusterFirst,
|
DNSPolicy: core.DNSClusterFirst,
|
||||||
@ -8940,8 +8950,7 @@ func TestValidatePodSpec(t *testing.T) {
|
|||||||
"bad fsGroup less than 0": {
|
"bad fsGroup less than 0": {
|
||||||
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
Containers: []core.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File"}},
|
||||||
SecurityContext: &core.PodSecurityContext{
|
SecurityContext: &core.PodSecurityContext{
|
||||||
HostNetwork: false,
|
FSGroup: &minGroupID,
|
||||||
FSGroup: &minGroupID,
|
|
||||||
},
|
},
|
||||||
RestartPolicy: core.RestartPolicyAlways,
|
RestartPolicy: core.RestartPolicyAlways,
|
||||||
DNSPolicy: core.DNSClusterFirst,
|
DNSPolicy: core.DNSClusterFirst,
|
||||||
@ -9042,7 +9051,10 @@ func TestValidatePodSpec(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
for k, v := range failureCases {
|
for k, v := range failureCases {
|
||||||
if errs := ValidatePodSpec(&v, nil, field.NewPath("field"), PodValidationOptions{}); len(errs) == 0 {
|
opts := PodValidationOptions{
|
||||||
|
ResourceIsPod: true,
|
||||||
|
}
|
||||||
|
if errs := ValidatePodSpec(&v, nil, field.NewPath("field"), opts); len(errs) == 0 {
|
||||||
t.Errorf("expected failure for %q", k)
|
t.Errorf("expected failure for %q", k)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -15381,6 +15393,30 @@ func TestValidateReplicationController(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
hostnetPodTemplate := core.PodTemplate{
|
||||||
|
Template: core.PodTemplateSpec{
|
||||||
|
ObjectMeta: metav1.ObjectMeta{
|
||||||
|
Labels: validSelector,
|
||||||
|
},
|
||||||
|
Spec: core.PodSpec{
|
||||||
|
SecurityContext: &core.PodSecurityContext{
|
||||||
|
HostNetwork: true,
|
||||||
|
},
|
||||||
|
RestartPolicy: core.RestartPolicyAlways,
|
||||||
|
DNSPolicy: core.DNSClusterFirst,
|
||||||
|
Containers: []core.Container{{
|
||||||
|
Name: "abc",
|
||||||
|
Image: "image",
|
||||||
|
ImagePullPolicy: "IfNotPresent",
|
||||||
|
TerminationMessagePolicy: "File",
|
||||||
|
Ports: []core.ContainerPort{{
|
||||||
|
ContainerPort: 12345,
|
||||||
|
Protocol: core.ProtocolTCP,
|
||||||
|
}},
|
||||||
|
}},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
|
invalidSelector := map[string]string{"NoUppercaseOrSpecialCharsLike=Equals": "b"}
|
||||||
invalidPodTemplate := core.PodTemplate{
|
invalidPodTemplate := core.PodTemplate{
|
||||||
Template: core.PodTemplateSpec{
|
Template: core.PodTemplateSpec{
|
||||||
@ -15412,8 +15448,14 @@ func TestValidateReplicationController(t *testing.T) {
|
|||||||
Selector: validSelector,
|
Selector: validSelector,
|
||||||
Template: &readWriteVolumePodTemplate.Template,
|
Template: &readWriteVolumePodTemplate.Template,
|
||||||
},
|
},
|
||||||
},
|
}, {
|
||||||
}
|
ObjectMeta: metav1.ObjectMeta{Name: "hostnet", Namespace: metav1.NamespaceDefault},
|
||||||
|
Spec: core.ReplicationControllerSpec{
|
||||||
|
Replicas: 1,
|
||||||
|
Selector: validSelector,
|
||||||
|
Template: &hostnetPodTemplate.Template,
|
||||||
|
},
|
||||||
|
}}
|
||||||
for _, successCase := range successCases {
|
for _, successCase := range successCases {
|
||||||
if errs := ValidateReplicationController(&successCase, PodValidationOptions{}); len(errs) != 0 {
|
if errs := ValidateReplicationController(&successCase, PodValidationOptions{}); len(errs) != 0 {
|
||||||
t.Errorf("expected success: %v", errs)
|
t.Errorf("expected success: %v", errs)
|
||||||
|
@ -212,6 +212,15 @@ const (
|
|||||||
// Enables support for time zones in CronJobs.
|
// Enables support for time zones in CronJobs.
|
||||||
CronJobTimeZone featuregate.Feature = "CronJobTimeZone"
|
CronJobTimeZone featuregate.Feature = "CronJobTimeZone"
|
||||||
|
|
||||||
|
// owner: @thockin
|
||||||
|
// deprecated: v1.28
|
||||||
|
//
|
||||||
|
// Changes when the default value of PodSpec.containers[].ports[].hostPort
|
||||||
|
// is assigned. The default is to only set a default value in Pods.
|
||||||
|
// Enabling this means a default will be assigned even to embeddedPodSpecs
|
||||||
|
// (e.g. in a Deployment), which is the historical default.
|
||||||
|
DefaultHostNetworkHostPortsInPodTemplates featuregate.Feature = "DefaultHostNetworkHostPortsInPodTemplates"
|
||||||
|
|
||||||
// owner: @andrewsykim
|
// owner: @andrewsykim
|
||||||
// alpha: v1.22
|
// alpha: v1.22
|
||||||
//
|
//
|
||||||
@ -893,6 +902,8 @@ var defaultKubernetesFeatureGates = map[featuregate.Feature]featuregate.FeatureS
|
|||||||
|
|
||||||
CronJobTimeZone: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.29
|
CronJobTimeZone: {Default: true, PreRelease: featuregate.GA, LockToDefault: true}, // remove in 1.29
|
||||||
|
|
||||||
|
DefaultHostNetworkHostPortsInPodTemplates: {Default: false, PreRelease: featuregate.Deprecated},
|
||||||
|
|
||||||
DisableCloudProviders: {Default: false, PreRelease: featuregate.Alpha},
|
DisableCloudProviders: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
|
||||||
DisableKubeletCloudCredentialProviders: {Default: false, PreRelease: featuregate.Alpha},
|
DisableKubeletCloudCredentialProviders: {Default: false, PreRelease: featuregate.Alpha},
|
||||||
|
@ -112,6 +112,7 @@ func (podStrategy) PrepareForUpdate(ctx context.Context, obj, old runtime.Object
|
|||||||
func (podStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
func (podStrategy) Validate(ctx context.Context, obj runtime.Object) field.ErrorList {
|
||||||
pod := obj.(*api.Pod)
|
pod := obj.(*api.Pod)
|
||||||
opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&pod.Spec, nil, &pod.ObjectMeta, nil)
|
opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&pod.Spec, nil, &pod.ObjectMeta, nil)
|
||||||
|
opts.ResourceIsPod = true
|
||||||
return corevalidation.ValidatePodCreate(pod, opts)
|
return corevalidation.ValidatePodCreate(pod, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,6 +142,7 @@ func (podStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Object)
|
|||||||
pod := obj.(*api.Pod)
|
pod := obj.(*api.Pod)
|
||||||
oldPod := old.(*api.Pod)
|
oldPod := old.(*api.Pod)
|
||||||
opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&pod.Spec, &oldPod.Spec, &pod.ObjectMeta, &oldPod.ObjectMeta)
|
opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&pod.Spec, &oldPod.Spec, &pod.ObjectMeta, &oldPod.ObjectMeta)
|
||||||
|
opts.ResourceIsPod = true
|
||||||
return corevalidation.ValidatePodUpdate(obj.(*api.Pod), old.(*api.Pod), opts)
|
return corevalidation.ValidatePodUpdate(obj.(*api.Pod), old.(*api.Pod), opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,6 +227,7 @@ func (podStatusStrategy) ValidateUpdate(ctx context.Context, obj, old runtime.Ob
|
|||||||
pod := obj.(*api.Pod)
|
pod := obj.(*api.Pod)
|
||||||
oldPod := old.(*api.Pod)
|
oldPod := old.(*api.Pod)
|
||||||
opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&pod.Spec, &oldPod.Spec, &pod.ObjectMeta, &oldPod.ObjectMeta)
|
opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&pod.Spec, &oldPod.Spec, &pod.ObjectMeta, &oldPod.ObjectMeta)
|
||||||
|
opts.ResourceIsPod = true
|
||||||
|
|
||||||
return corevalidation.ValidatePodStatusUpdate(obj.(*api.Pod), old.(*api.Pod), opts)
|
return corevalidation.ValidatePodStatusUpdate(obj.(*api.Pod), old.(*api.Pod), opts)
|
||||||
}
|
}
|
||||||
@ -264,6 +267,7 @@ func (podEphemeralContainersStrategy) ValidateUpdate(ctx context.Context, obj, o
|
|||||||
newPod := obj.(*api.Pod)
|
newPod := obj.(*api.Pod)
|
||||||
oldPod := old.(*api.Pod)
|
oldPod := old.(*api.Pod)
|
||||||
opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&newPod.Spec, &oldPod.Spec, &newPod.ObjectMeta, &oldPod.ObjectMeta)
|
opts := podutil.GetValidationOptionsFromPodSpecAndMeta(&newPod.Spec, &oldPod.Spec, &newPod.ObjectMeta, &oldPod.ObjectMeta)
|
||||||
|
opts.ResourceIsPod = true
|
||||||
return corevalidation.ValidatePodEphemeralContainersUpdate(newPod, oldPod, opts)
|
return corevalidation.ValidatePodEphemeralContainersUpdate(newPod, oldPod, opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user