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 == "" {
|
||||
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)
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
} else {
|
||||
newPort.HostPort = newPort.ContainerPort
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
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`
|
||||
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`
|
||||
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`
|
||||
|
||||
**--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.
|
||||
|
||||
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**.
|
||||
|
||||
**--publish-all**, **-P**=**true**|**false**
|
||||
|
@ -43,6 +43,8 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping,
|
||||
containerPortValidate[proto] = make(map[string]map[uint16]uint16)
|
||||
}
|
||||
|
||||
postAssignHostPort := false
|
||||
|
||||
// Iterate through all port mappings, generating OCICNI PortMapping
|
||||
// structs and validating there is no overlap.
|
||||
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")
|
||||
}
|
||||
hostPort := port.HostPort
|
||||
if hostPort == 0 {
|
||||
hostPort = containerPort
|
||||
}
|
||||
if uint32(len-1)+uint32(containerPort) > 65535 {
|
||||
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
|
||||
hPort := hostPort + index
|
||||
|
||||
if cPort == 0 || hPort == 0 {
|
||||
return nil, nil, nil, errors.Errorf("host and container ports cannot be 0")
|
||||
if cPort == 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]
|
||||
if testCPort != 0 && testCPort != hPort {
|
||||
// This is an attempt to redefine a port
|
||||
@ -126,6 +140,7 @@ func parsePortMapping(portMappings []specgen.PortMapping) ([]ocicni.PortMapping,
|
||||
if testCPort == hPort && testHPort == cPort {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
// We appear to be clear. Make an OCICNI port
|
||||
// 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
|
||||
}
|
||||
|
||||
|
@ -435,7 +435,8 @@ type PortMapping struct {
|
||||
ContainerPort uint16 `json:"container_port"`
|
||||
// HostPort is the port number that will be forwarded from the host into
|
||||
// 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"`
|
||||
// Range is the number of ports that will be forwarded, starting at
|
||||
// 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(""))
|
||||
})
|
||||
|
||||
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() {
|
||||
SkipIfRootless()
|
||||
session := podmanTest.Podman([]string{"run", "-dt", "-p", "80:8000", ALPINE, "/bin/sh"})
|
||||
|
Reference in New Issue
Block a user