Change:
I found that kubelet will not allocate hostports specified from initContainers. And PodFitsHostPorts in kube-scheduler does not take initContainer into consideration. Also, kube-apiserver think such pod is illegal, but I think it OK. We should make the behavior of spec.HostPorts in initContainer clear
/sig node
Exactly. I think this makes sense. Since initContainers are expected to be completed, we should not expose extra port mappings during creating pod sandbox, which may bring in security risks.
Sounds good to me.
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle stale
/remove-lifecycle stale

Discussion:
func validateInitContainers(containers, otherContainers []core.Container, deviceVolumes map[string]core.VolumeSource, fldPath *field.Path) field.ErrorList { var allErrs field.ErrorList if len(containers) > 0 { <del> allErrs = append(allErrs, validateContainers(containers, deviceVolumes, fldPath)...) </del> <ins> allErrs = append(allErrs, validateContainers(containers, true, deviceVolumes, fldPath)...) </ins> } allNames := sets.String{}

return allErrs } <del> func validateContainers(containers []core.Container, volumes map[string]core.VolumeSource, fldPath *field.Path) field.ErrorList { </del> <ins> func validateContainers(containers []core.Container, isInitContainers bool, volumes map[string]core.VolumeSource, fldPath *field.Path) field.ErrorList { </ins> allErrs := field.ErrorList{} if len(containers) == 0 {

allErrs = append(allErrs, ValidateResourceRequirements(&ctr.Resources, idxPath.Child("resources"))...) allErrs = append(allErrs, ValidateSecurityContext(ctr.SecurityContext, idxPath.Child("securityContext"))...) } <del> // Check for colliding ports across all containers. allErrs = append(allErrs, checkHostPortConflicts(containers, fldPath)...) </del> <ins> if isInitContainers { // check initContainers one by one since they are running in sequential order. for _, initContainer := range containers { allErrs = append(allErrs, checkHostPortConflicts([]core.Container{initContainer}, fldPath)...) } } else { // Check for colliding ports across all containers. allErrs = append(allErrs, checkHostPortConflicts(containers, fldPath)...) } </ins> return allErrs }

vols, vErrs := ValidateVolumes(spec.Volumes, fldPath.Child("volumes")) allErrs = append(allErrs, vErrs...) <del> allErrs = append(allErrs, validateContainers(spec.Containers, vols, fldPath.Child("containers"))...) </del> <ins> allErrs = append(allErrs, validateContainers(spec.Containers, false, vols, fldPath.Child("containers"))...) </ins> allErrs = append(allErrs, validateInitContainers(spec.InitContainers, spec.Containers, vols, fldPath.Child("initContainers"))...) allErrs = append(allErrs, validateRestartPolicy(&spec.RestartPolicy, fldPath.Child("restartPolicy"))...) allErrs = append(allErrs, validateDNSPolicy(&spec.DNSPolicy, fldPath.Child("dnsPolicy"))...)

}, {Name: "abc-1234", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: "File", SecurityContext: fakeValidSecurityContext(true)}, } <del> if errs := validateContainers(successCase, volumeDevices, field.NewPath("field")); len(errs) != 0 { </del> <ins> if errs := validateContainers(successCase, false, volumeDevices, field.NewPath("field")); len(errs) != 0 { </ins> t.Errorf("expected success: %v", errs) }

}, } for k, v := range errorCases { <del> if errs := validateContainers(v, volumeDevices, field.NewPath("field")); len(errs) == 0 { </del> <ins> if errs := validateContainers(v, false, volumeDevices, field.NewPath("field")); len(errs) == 0 { t.Errorf("expected failure for %s", k) } } } func TestValidateInitContainers(t *testing.T) { volumeDevices := make(map[string]core.VolumeSource) capabilities.SetForTests(capabilities.Capabilities{ AllowPrivileged: true, }) successCase := []core.Container{ { Name:  "container-1-same-host-port-different-protocol", Image: "image", Ports: []core.ContainerPort{ {ContainerPort: 80, HostPort: 80, Protocol: "TCP"}, {ContainerPort: 80, HostPort: 80, Protocol: "UDP"}, }, ImagePullPolicy:          "IfNotPresent", TerminationMessagePolicy: "File", }, { Name:  "container-2-same-host-port-different-protocol", Image: "image", Ports: []core.ContainerPort{ {ContainerPort: 80, HostPort: 80, Protocol: "TCP"}, {ContainerPort: 80, HostPort: 80, Protocol: "UDP"}, }, ImagePullPolicy:          "IfNotPresent", TerminationMessagePolicy: "File", }, } if errs := validateContainers(successCase, true, volumeDevices, field.NewPath("field")); len(errs) != 0 { t.Errorf("expected success: %v", errs) } capabilities.SetForTests(capabilities.Capabilities{ AllowPrivileged: false, }) errorCases := map[string][]core.Container{ "duplicate ports": { { Name:  "abc", Image: "image", Ports: []core.ContainerPort{ { ContainerPort: 8080, HostPort: 8080, Protocol: "TCP", }, { ContainerPort: 8080, HostPort: 8080, Protocol: "TCP", }, }, ImagePullPolicy:          "IfNotPresent", TerminationMessagePolicy: "File", }, }, } for k, v := range errorCases { if errs := validateContainers(v, true, volumeDevices, field.NewPath("field")); len(errs) == 0 { </ins> t.Errorf("expected failure for %s", k) } }