mirror of
https://github.com/containers/podman.git
synced 2025-06-26 12:56:45 +08:00
Merge pull request #6808 from mheon/allow_empty_hostport
Allow empty host port in --publish flag
This commit is contained in:
@ -184,8 +184,10 @@ func parseSplitPort(hostIP, hostPort *string, ctrPort string, protocol *string)
|
|||||||
}
|
}
|
||||||
if hostPort != nil {
|
if hostPort != nil {
|
||||||
if *hostPort == "" {
|
if *hostPort == "" {
|
||||||
return newPort, errors.Errorf("must provide a non-empty container host port to publish")
|
// Set 0 as a placeholder. The server side of Specgen
|
||||||
}
|
// will find a random, open, unused port to use.
|
||||||
|
newPort.HostPort = 0
|
||||||
|
} else {
|
||||||
hostStart, hostLen, err := parseAndValidateRange(*hostPort)
|
hostStart, hostLen, err := parseAndValidateRange(*hostPort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return newPort, errors.Wrapf(err, "error parsing host port")
|
return newPort, errors.Wrapf(err, "error parsing host port")
|
||||||
@ -195,11 +197,11 @@ func parseSplitPort(hostIP, hostPort *string, ctrPort string, protocol *string)
|
|||||||
}
|
}
|
||||||
newPort.HostPort = hostStart
|
newPort.HostPort = hostStart
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
newPort.HostPort = newPort.ContainerPort
|
||||||
|
}
|
||||||
|
|
||||||
hport := newPort.HostPort
|
hport := newPort.HostPort
|
||||||
if hport == 0 {
|
|
||||||
hport = newPort.ContainerPort
|
|
||||||
}
|
|
||||||
logrus.Debugf("Adding port mapping from %d to %d length %d protocol %q", hport, newPort.ContainerPort, newPort.Range, newPort.Protocol)
|
logrus.Debugf("Adding port mapping from %d to %d length %d protocol %q", hport, newPort.ContainerPort, newPort.Range, newPort.Protocol)
|
||||||
|
|
||||||
return newPort, nil
|
return newPort, nil
|
||||||
|
@ -626,6 +626,8 @@ When specifying ranges for both, the number of container ports in the range must
|
|||||||
(e.g., `podman run -p 1234-1236:1222-1224 --name thisWorks -t busybox`
|
(e.g., `podman run -p 1234-1236:1222-1224 --name thisWorks -t busybox`
|
||||||
but not `podman run -p 1230-1236:1230-1240 --name RangeContainerPortsBiggerThanRangeHostPorts -t busybox`)
|
but not `podman run -p 1230-1236:1230-1240 --name RangeContainerPortsBiggerThanRangeHostPorts -t busybox`)
|
||||||
With ip: `podman run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t someimage`
|
With ip: `podman run -p 127.0.0.1:$HOSTPORT:$CONTAINERPORT --name CONTAINER -t someimage`
|
||||||
|
Host port does not have to be specified (e.g. `podman run -p 127.0.0.1::80`).
|
||||||
|
If it is not, the container port will be randomly assigned a port on the host.
|
||||||
Use `podman port` to see the actual mapping: `podman port CONTAINER $CONTAINERPORT`
|
Use `podman port` to see the actual mapping: `podman port CONTAINER $CONTAINERPORT`
|
||||||
|
|
||||||
**--publish-all**, **-P**=*true|false*
|
**--publish-all**, **-P**=*true|false*
|
||||||
|
@ -638,6 +638,9 @@ Both hostPort and containerPort can be specified as a range of ports.
|
|||||||
|
|
||||||
When specifying ranges for both, the number of container ports in the range must match the number of host ports in the range.
|
When specifying ranges for both, the number of container ports in the range must match the number of host ports in the range.
|
||||||
|
|
||||||
|
Host port does not have to be specified (e.g. `podman run -p 127.0.0.1::80`).
|
||||||
|
If it is not, the container port will be randomly assigned a port on the host.
|
||||||
|
|
||||||
Use **podman port** to see the actual mapping: **podman port $CONTAINER $CONTAINERPORT**.
|
Use **podman port** to see the actual mapping: **podman port $CONTAINER $CONTAINERPORT**.
|
||||||
|
|
||||||
**--publish-all**, **-P**=**true**|**false**
|
**--publish-all**, **-P**=**true**|**false**
|
||||||
|
@ -43,6 +43,8 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping,
|
|||||||
containerPortValidate[proto] = make(map[string]map[uint16]uint16)
|
containerPortValidate[proto] = make(map[string]map[uint16]uint16)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postAssignHostPort := false
|
||||||
|
|
||||||
// Iterate through all port mappings, generating OCICNI PortMapping
|
// Iterate through all port mappings, generating OCICNI PortMapping
|
||||||
// structs and validating there is no overlap.
|
// structs and validating there is no overlap.
|
||||||
for _, port := range portMappings {
|
for _, port := range portMappings {
|
||||||
@ -71,9 +73,6 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping,
|
|||||||
return nil, nil, nil, errors.Errorf("container port number must be non-0")
|
return nil, nil, nil, errors.Errorf("container port number must be non-0")
|
||||||
}
|
}
|
||||||
hostPort := port.HostPort
|
hostPort := port.HostPort
|
||||||
if hostPort == 0 {
|
|
||||||
hostPort = containerPort
|
|
||||||
}
|
|
||||||
if uint32(len-1)+uint32(containerPort) > 65535 {
|
if uint32(len-1)+uint32(containerPort) > 65535 {
|
||||||
return nil, nil, nil, errors.Errorf("container port range exceeds maximum allowable port number")
|
return nil, nil, nil, errors.Errorf("container port range exceeds maximum allowable port number")
|
||||||
}
|
}
|
||||||
@ -105,10 +104,25 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping,
|
|||||||
cPort := containerPort + index
|
cPort := containerPort + index
|
||||||
hPort := hostPort + index
|
hPort := hostPort + index
|
||||||
|
|
||||||
if cPort == 0 || hPort == 0 {
|
if cPort == 0 {
|
||||||
return nil, nil, nil, errors.Errorf("host and container ports cannot be 0")
|
return nil, nil, nil, errors.Errorf("container port cannot be 0")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Host port is allowed to be 0. If it is, we
|
||||||
|
// select a random port on the host.
|
||||||
|
// This will happen *after* all other ports are
|
||||||
|
// placed, to ensure we don't accidentally
|
||||||
|
// select a port that a later mapping wanted.
|
||||||
|
if hPort == 0 {
|
||||||
|
// If we already have a host port
|
||||||
|
// assigned to their container port -
|
||||||
|
// just use that.
|
||||||
|
if ctrPortMap[cPort] != 0 {
|
||||||
|
hPort = ctrPortMap[cPort]
|
||||||
|
} else {
|
||||||
|
postAssignHostPort = true
|
||||||
|
}
|
||||||
|
} else {
|
||||||
testCPort := ctrPortMap[cPort]
|
testCPort := ctrPortMap[cPort]
|
||||||
if testCPort != 0 && testCPort != hPort {
|
if testCPort != 0 && testCPort != hPort {
|
||||||
// This is an attempt to redefine a port
|
// This is an attempt to redefine a port
|
||||||
@ -126,6 +140,7 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping,
|
|||||||
if testCPort == hPort && testHPort == cPort {
|
if testCPort == hPort && testHPort == cPort {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// We appear to be clear. Make an OCICNI port
|
// We appear to be clear. Make an OCICNI port
|
||||||
// struct.
|
// struct.
|
||||||
@ -142,6 +157,61 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Handle any 0 host ports now by setting random container ports.
|
||||||
|
if postAssignHostPort {
|
||||||
|
remadeMappings := make([]ocicni.PortMapping, 0, len(finalMappings))
|
||||||
|
|
||||||
|
// Iterate over all
|
||||||
|
for _, p := range finalMappings {
|
||||||
|
if p.HostPort != 0 {
|
||||||
|
remadeMappings = append(remadeMappings, p)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
hostIPMap := hostPortValidate[p.Protocol]
|
||||||
|
ctrIPMap := containerPortValidate[p.Protocol]
|
||||||
|
|
||||||
|
hostPortMap, ok := hostIPMap[p.HostIP]
|
||||||
|
if !ok {
|
||||||
|
hostPortMap = make(map[uint16]uint16)
|
||||||
|
hostIPMap[p.HostIP] = hostPortMap
|
||||||
|
}
|
||||||
|
ctrPortMap, ok := ctrIPMap[p.HostIP]
|
||||||
|
if !ok {
|
||||||
|
ctrPortMap = make(map[uint16]uint16)
|
||||||
|
ctrIPMap[p.HostIP] = ctrPortMap
|
||||||
|
}
|
||||||
|
|
||||||
|
// See if container port has been used elsewhere
|
||||||
|
if ctrPortMap[uint16(p.ContainerPort)] != 0 {
|
||||||
|
// Duplicate definition. Let's not bother
|
||||||
|
// including it.
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Max retries to ensure we don't loop forever.
|
||||||
|
for i := 0; i < 15; i++ {
|
||||||
|
candidate, err := getRandomPort()
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, errors.Wrapf(err, "error getting candidate host port for container port %d", p.ContainerPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
if hostPortMap[uint16(candidate)] == 0 {
|
||||||
|
logrus.Debugf("Successfully assigned container port %d to host port %d (IP %s Protocol %s)", p.ContainerPort, candidate, p.HostIP, p.Protocol)
|
||||||
|
hostPortMap[uint16(candidate)] = uint16(p.ContainerPort)
|
||||||
|
ctrPortMap[uint16(p.ContainerPort)] = uint16(candidate)
|
||||||
|
p.HostPort = int32(candidate)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if p.HostPort == 0 {
|
||||||
|
return nil, nil, nil, errors.Errorf("could not find open host port to map container port %d to", p.ContainerPort)
|
||||||
|
}
|
||||||
|
remadeMappings = append(remadeMappings, p)
|
||||||
|
}
|
||||||
|
return remadeMappings, containerPortValidate, hostPortValidate, nil
|
||||||
|
}
|
||||||
|
|
||||||
return finalMappings, containerPortValidate, hostPortValidate, nil
|
return finalMappings, containerPortValidate, hostPortValidate, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -435,7 +435,8 @@ type PortMapping struct {
|
|||||||
ContainerPort uint16 `json:"container_port"`
|
ContainerPort uint16 `json:"container_port"`
|
||||||
// HostPort is the port number that will be forwarded from the host into
|
// HostPort is the port number that will be forwarded from the host into
|
||||||
// the container.
|
// the container.
|
||||||
// If omitted, will be assumed to be identical to
|
// If omitted, a random port on the host (guaranteed to be over 1024)
|
||||||
|
// will be assigned.
|
||||||
HostPort uint16 `json:"host_port,omitempty"`
|
HostPort uint16 `json:"host_port,omitempty"`
|
||||||
// Range is the number of ports that will be forwarded, starting at
|
// Range is the number of ports that will be forwarded, starting at
|
||||||
// HostPort and ContainerPort and counting up.
|
// HostPort and ContainerPort and counting up.
|
||||||
|
@ -184,6 +184,30 @@ var _ = Describe("Podman run networking", func() {
|
|||||||
Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal(""))
|
Expect(inspectOut[0].NetworkSettings.Ports["80/tcp"][0].HostIP).To(Equal(""))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("podman run -p 127.0.0.1::8080/udp", func() {
|
||||||
|
name := "testctr"
|
||||||
|
session := podmanTest.Podman([]string{"create", "-t", "-p", "127.0.0.1::8080/udp", "--name", name, ALPINE, "/bin/sh"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
inspectOut := podmanTest.InspectContainer(name)
|
||||||
|
Expect(len(inspectOut)).To(Equal(1))
|
||||||
|
Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1))
|
||||||
|
Expect(len(inspectOut[0].NetworkSettings.Ports["8080/udp"])).To(Equal(1))
|
||||||
|
Expect(inspectOut[0].NetworkSettings.Ports["8080/udp"][0].HostPort).To(Not(Equal("8080")))
|
||||||
|
Expect(inspectOut[0].NetworkSettings.Ports["8080/udp"][0].HostIP).To(Equal("127.0.0.1"))
|
||||||
|
})
|
||||||
|
|
||||||
|
It("podman run -p :8080", func() {
|
||||||
|
name := "testctr"
|
||||||
|
session := podmanTest.Podman([]string{"create", "-t", "-p", ":8080", "--name", name, ALPINE, "/bin/sh"})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
inspectOut := podmanTest.InspectContainer(name)
|
||||||
|
Expect(len(inspectOut)).To(Equal(1))
|
||||||
|
Expect(len(inspectOut[0].NetworkSettings.Ports)).To(Equal(1))
|
||||||
|
Expect(len(inspectOut[0].NetworkSettings.Ports["8080/tcp"])).To(Equal(1))
|
||||||
|
Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostPort).To(Not(Equal("8080")))
|
||||||
|
Expect(inspectOut[0].NetworkSettings.Ports["8080/tcp"][0].HostIP).To(Equal(""))
|
||||||
|
})
|
||||||
|
|
||||||
It("podman run network expose host port 80 to container port 8000", func() {
|
It("podman run network expose host port 80 to container port 8000", func() {
|
||||||
SkipIfRootless()
|
SkipIfRootless()
|
||||||
session := podmanTest.Podman([]string{"run", "-dt", "-p", "80:8000", ALPINE, "/bin/sh"})
|
session := podmanTest.Podman([]string{"run", "-dt", "-p", "80:8000", ALPINE, "/bin/sh"})
|
||||||
|
Reference in New Issue
Block a user