mirror of
				https://github.com/moby/moby.git
				synced 2022-11-09 12:21:53 -05:00 
			
		
		
		
	add ability to publish range of ports
Closes #8899 Signed-off-by: Srini Brahmaroutu <srbrahma@us.ibm.com>
This commit is contained in:
		
							parent
							
								
									b2ab733c99
								
							
						
					
					
						commit
						2338a9cf5a
					
				
					 12 changed files with 332 additions and 32 deletions
				
			
		| 
						 | 
				
			
			@ -121,8 +121,10 @@ IMAGE [COMMAND] [ARG...]
 | 
			
		|||
   Publish all exposed ports to the host interfaces. The default is *false*.
 | 
			
		||||
 | 
			
		||||
**-p**, **--publish**=[]
 | 
			
		||||
   Publish a container's port to the host
 | 
			
		||||
   Publish a container's port, or a range of ports, to the host
 | 
			
		||||
                               format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort
 | 
			
		||||
                               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. (e.g., `-p 1234-1236:1234-1236/tcp`)
 | 
			
		||||
                               (use 'docker port' to see the actual mapping)
 | 
			
		||||
 | 
			
		||||
**--privileged**=*true*|*false*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -146,7 +146,7 @@ ENTRYPOINT.
 | 
			
		|||
   Read in a line delimited file of environment variables
 | 
			
		||||
 | 
			
		||||
**--expose**=[]
 | 
			
		||||
   Expose a port or a range of ports (e.g. --expose=3300-3310) from the container without publishing it to your host
 | 
			
		||||
   Expose a port, or a range of ports (e.g. --expose=3300-3310), from the container without publishing it to your host
 | 
			
		||||
 | 
			
		||||
**-h**, **--hostname**=""
 | 
			
		||||
   Container host name
 | 
			
		||||
| 
						 | 
				
			
			@ -224,8 +224,10 @@ ports to a random port on the host between 49153 and 65535. To find the
 | 
			
		|||
mapping between the host ports and the exposed ports, use **docker port**.
 | 
			
		||||
 | 
			
		||||
**-p**, **--publish**=[]
 | 
			
		||||
   Publish a container's port to the host
 | 
			
		||||
   Publish a container's port, or range of ports, to the host.
 | 
			
		||||
                               format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort
 | 
			
		||||
                               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. (e.g., `-p 1234-1236:1234-1236/tcp`)
 | 
			
		||||
                               (use 'docker port' to see the actual mapping)
 | 
			
		||||
 | 
			
		||||
**--privileged**=*true*|*false*
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -686,8 +686,10 @@ Creates a new container.
 | 
			
		|||
                                   'container:<name|id>': reuses another container network stack
 | 
			
		||||
                                   'host': use the host network stack inside the container.  Note: the host mode gives the container full access to local system services such as D-bus and is therefore considered insecure.
 | 
			
		||||
      -P, --publish-all=false    Publish all exposed ports to the host interfaces
 | 
			
		||||
      -p, --publish=[]           Publish a container's port to the host
 | 
			
		||||
      -p, --publish=[]           Publish a container's port, or a range of ports (e.g., `-p 3300-3310`), to the host
 | 
			
		||||
                                   format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort
 | 
			
		||||
                                   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. (e.g., `-p 1234-1236:1234-1236/tcp`)
 | 
			
		||||
                                   (use 'docker port' to see the actual mapping)
 | 
			
		||||
      --privileged=false         Give extended privileges to this container
 | 
			
		||||
      --restart=""               Restart policy to apply when a container exits (no, on-failure[:max-retry], always)
 | 
			
		||||
| 
						 | 
				
			
			@ -1514,6 +1516,8 @@ removed before the image is removed.
 | 
			
		|||
      -P, --publish-all=false    Publish all exposed ports to the host interfaces
 | 
			
		||||
      -p, --publish=[]           Publish a container's port to the host
 | 
			
		||||
                                   format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort
 | 
			
		||||
                                   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. (e.g., `-p 1234-1236:1234-1236/tcp`)
 | 
			
		||||
                                   (use 'docker port' to see the actual mapping)
 | 
			
		||||
      --privileged=false         Give extended privileges to this container
 | 
			
		||||
      --restart=""               Restart policy to apply when a container exits (no, on-failure[:max-retry], always)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -487,10 +487,11 @@ or override the Dockerfile's exposed defaults:
 | 
			
		|||
    --expose=[]: Expose a port or a range of ports from the container
 | 
			
		||||
                without publishing it to your host
 | 
			
		||||
    -P=false   : Publish all exposed ports to the host interfaces
 | 
			
		||||
    -p=[]      : Publish a container᾿s port to the host (format:
 | 
			
		||||
                 ip:hostPort:containerPort | ip::containerPort |
 | 
			
		||||
                 hostPort:containerPort | containerPort)
 | 
			
		||||
                 (use 'docker port' to see the actual mapping)
 | 
			
		||||
    -p=[]      : Publish a container᾿s port or a range of ports to the host 
 | 
			
		||||
                   format: ip:hostPort:containerPort | ip::containerPort | hostPort:containerPort | containerPort
 | 
			
		||||
                   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. (e.g., `-p 1234-1236:1234-1236/tcp`)
 | 
			
		||||
                   (use 'docker port' to see the actual mapping)
 | 
			
		||||
    --link=""  : Add link to another container (name:alias)
 | 
			
		||||
 | 
			
		||||
As mentioned previously, `EXPOSE` (and `--expose`) makes ports available
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2,6 +2,7 @@ package main
 | 
			
		|||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"github.com/docker/docker/nat"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"testing"
 | 
			
		||||
| 
						 | 
				
			
			@ -102,6 +103,104 @@ func TestCreateHostConfig(t *testing.T) {
 | 
			
		|||
	logDone("create - hostconfig")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCreateWithPortRange(t *testing.T) {
 | 
			
		||||
	runCmd := exec.Command(dockerBinary, "create", "-p", "3300-3303:3300-3303/tcp", "busybox", "echo")
 | 
			
		||||
	out, _, _, err := runCommandWithStdoutStderr(runCmd)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(out, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cleanedContainerID := stripTrailingCharacters(out)
 | 
			
		||||
 | 
			
		||||
	inspectCmd := exec.Command(dockerBinary, "inspect", cleanedContainerID)
 | 
			
		||||
	out, _, err = runCommandWithOutput(inspectCmd)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("out should've been a container id: %s, %v", out, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	containers := []struct {
 | 
			
		||||
		HostConfig *struct {
 | 
			
		||||
			PortBindings map[nat.Port][]nat.PortBinding
 | 
			
		||||
		}
 | 
			
		||||
	}{}
 | 
			
		||||
	if err := json.Unmarshal([]byte(out), &containers); err != nil {
 | 
			
		||||
		t.Fatalf("Error inspecting the container: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	if len(containers) != 1 {
 | 
			
		||||
		t.Fatalf("Unexpected container count. Expected 0, received: %d", len(containers))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := containers[0]
 | 
			
		||||
	if c.HostConfig == nil {
 | 
			
		||||
		t.Fatalf("Expected HostConfig, got none")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.HostConfig.PortBindings) != 4 {
 | 
			
		||||
		t.Fatalf("Expected 4 ports bindings, got %d", len(c.HostConfig.PortBindings))
 | 
			
		||||
	}
 | 
			
		||||
	for k, v := range c.HostConfig.PortBindings {
 | 
			
		||||
		if len(v) != 1 {
 | 
			
		||||
			t.Fatalf("Expected 1 ports binding, for the port  %s but found %s", k, v)
 | 
			
		||||
		}
 | 
			
		||||
		if k.Port() != v[0].HostPort {
 | 
			
		||||
			t.Fatalf("Expected host port %d to match published port  %d", k.Port(), v[0].HostPort)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	deleteAllContainers()
 | 
			
		||||
 | 
			
		||||
	logDone("create - port range")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestCreateWithiLargePortRange(t *testing.T) {
 | 
			
		||||
	runCmd := exec.Command(dockerBinary, "create", "-p", "1-65535:1-65535/tcp", "busybox", "echo")
 | 
			
		||||
	out, _, _, err := runCommandWithStdoutStderr(runCmd)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(out, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cleanedContainerID := stripTrailingCharacters(out)
 | 
			
		||||
 | 
			
		||||
	inspectCmd := exec.Command(dockerBinary, "inspect", cleanedContainerID)
 | 
			
		||||
	out, _, err = runCommandWithOutput(inspectCmd)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("out should've been a container id: %s, %v", out, err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	containers := []struct {
 | 
			
		||||
		HostConfig *struct {
 | 
			
		||||
			PortBindings map[nat.Port][]nat.PortBinding
 | 
			
		||||
		}
 | 
			
		||||
	}{}
 | 
			
		||||
	if err := json.Unmarshal([]byte(out), &containers); err != nil {
 | 
			
		||||
		t.Fatalf("Error inspecting the container: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	if len(containers) != 1 {
 | 
			
		||||
		t.Fatalf("Unexpected container count. Expected 0, received: %d", len(containers))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	c := containers[0]
 | 
			
		||||
	if c.HostConfig == nil {
 | 
			
		||||
		t.Fatalf("Expected HostConfig, got none")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.HostConfig.PortBindings) != 65535 {
 | 
			
		||||
		t.Fatalf("Expected 65535 ports bindings, got %d", len(c.HostConfig.PortBindings))
 | 
			
		||||
	}
 | 
			
		||||
	for k, v := range c.HostConfig.PortBindings {
 | 
			
		||||
		if len(v) != 1 {
 | 
			
		||||
			t.Fatalf("Expected 1 ports binding, for the port  %s but found %s", k, v)
 | 
			
		||||
		}
 | 
			
		||||
		if k.Port() != v[0].HostPort {
 | 
			
		||||
			t.Fatalf("Expected host port %d to match published port  %d", k.Port(), v[0].HostPort)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	deleteAllContainers()
 | 
			
		||||
 | 
			
		||||
	logDone("create - large port range")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// "test123" should be printed by docker create + start
 | 
			
		||||
func TestCreateEchoStdout(t *testing.T) {
 | 
			
		||||
	runCmd := exec.Command(dockerBinary, "create", "busybox", "echo", "test123")
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2737,3 +2737,27 @@ func TestRunNetHost(t *testing.T) {
 | 
			
		|||
 | 
			
		||||
	logDone("run - net host mode")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestRunAllowPortRangeThroughPublish(t *testing.T) {
 | 
			
		||||
	cmd := exec.Command(dockerBinary, "run", "-d", "--expose", "3000-3003", "-p", "3000-3003", "busybox", "top")
 | 
			
		||||
	out, _, err := runCommandWithOutput(cmd)
 | 
			
		||||
	defer deleteAllContainers()
 | 
			
		||||
 | 
			
		||||
	id := strings.TrimSpace(out)
 | 
			
		||||
	portstr, err := inspectFieldJSON(id, "NetworkSettings.Ports")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	var ports nat.PortMap
 | 
			
		||||
	err = unmarshalJSON([]byte(portstr), &ports)
 | 
			
		||||
	for port, binding := range ports {
 | 
			
		||||
		portnum, _ := strconv.Atoi(strings.Split(string(port), "/")[0])
 | 
			
		||||
		if portnum < 3000 || portnum > 3003 {
 | 
			
		||||
			t.Fatalf("Port is out of range ", portnum, binding, out)
 | 
			
		||||
		}
 | 
			
		||||
		if binding == nil || len(binding) != 1 || len(binding[0].HostPort) == 0 {
 | 
			
		||||
			t.Fatal("Port is not mapped for the port "+port, out)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	logDone("run - allow port range through --expose flag")
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										47
									
								
								nat/nat.go
									
										
									
									
									
								
							
							
						
						
									
										47
									
								
								nat/nat.go
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -122,31 +122,48 @@ func ParsePortSpecs(ports []string) (map[Port]struct{}, map[Port][]PortBinding,
 | 
			
		|||
		if containerPort == "" {
 | 
			
		||||
			return nil, nil, fmt.Errorf("No port specified: %s<empty>", rawPort)
 | 
			
		||||
		}
 | 
			
		||||
		if _, err := strconv.ParseUint(containerPort, 10, 16); err != nil {
 | 
			
		||||
 | 
			
		||||
		startPort, endPort, err := parsers.ParsePortRange(containerPort)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, nil, fmt.Errorf("Invalid containerPort: %s", containerPort)
 | 
			
		||||
		}
 | 
			
		||||
		if _, err := strconv.ParseUint(hostPort, 10, 16); hostPort != "" && err != nil {
 | 
			
		||||
			return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
 | 
			
		||||
 | 
			
		||||
		var startHostPort, endHostPort uint64 = 0, 0
 | 
			
		||||
		if len(hostPort) > 0 {
 | 
			
		||||
			startHostPort, endHostPort, err = parsers.ParsePortRange(hostPort)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, nil, fmt.Errorf("Invalid hostPort: %s", hostPort)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if hostPort != "" && (endPort-startPort) != (endHostPort-startHostPort) {
 | 
			
		||||
			return nil, nil, fmt.Errorf("Invalid ranges specified for container and host Ports: %s and %s", containerPort, hostPort)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if !validateProto(proto) {
 | 
			
		||||
			return nil, nil, fmt.Errorf("Invalid proto: %s", proto)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		port := NewPort(proto, containerPort)
 | 
			
		||||
		if _, exists := exposedPorts[port]; !exists {
 | 
			
		||||
			exposedPorts[port] = struct{}{}
 | 
			
		||||
		}
 | 
			
		||||
		for i := uint64(0); i <= (endPort - startPort); i++ {
 | 
			
		||||
			containerPort = strconv.FormatUint(startPort+i, 10)
 | 
			
		||||
			if len(hostPort) > 0 {
 | 
			
		||||
				hostPort = strconv.FormatUint(startHostPort+i, 10)
 | 
			
		||||
			}
 | 
			
		||||
			port := NewPort(proto, containerPort)
 | 
			
		||||
			if _, exists := exposedPorts[port]; !exists {
 | 
			
		||||
				exposedPorts[port] = struct{}{}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		binding := PortBinding{
 | 
			
		||||
			HostIp:   rawIp,
 | 
			
		||||
			HostPort: hostPort,
 | 
			
		||||
			binding := PortBinding{
 | 
			
		||||
				HostIp:   rawIp,
 | 
			
		||||
				HostPort: hostPort,
 | 
			
		||||
			}
 | 
			
		||||
			bslice, exists := bindings[port]
 | 
			
		||||
			if !exists {
 | 
			
		||||
				bslice = []PortBinding{}
 | 
			
		||||
			}
 | 
			
		||||
			bindings[port] = append(bslice, binding)
 | 
			
		||||
		}
 | 
			
		||||
		bslice, exists := bindings[port]
 | 
			
		||||
		if !exists {
 | 
			
		||||
			bslice = []PortBinding{}
 | 
			
		||||
		}
 | 
			
		||||
		bindings[port] = append(bslice, binding)
 | 
			
		||||
	}
 | 
			
		||||
	return exposedPorts, bindings, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -108,7 +108,7 @@ func TestParsePortSpecs(t *testing.T) {
 | 
			
		|||
	portMap, bindingMap, err = ParsePortSpecs([]string{"1234/tcp", "2345/udp"})
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Error while processing ParsePortSpecs: %s", err.Error())
 | 
			
		||||
		t.Fatalf("Error while processing ParsePortSpecs: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, ok := portMap[Port("1234/tcp")]; !ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -136,7 +136,7 @@ func TestParsePortSpecs(t *testing.T) {
 | 
			
		|||
	portMap, bindingMap, err = ParsePortSpecs([]string{"1234:1234/tcp", "2345:2345/udp"})
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Error while processing ParsePortSpecs: %s", err.Error())
 | 
			
		||||
		t.Fatalf("Error while processing ParsePortSpecs: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, ok := portMap[Port("1234/tcp")]; !ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -166,7 +166,7 @@ func TestParsePortSpecs(t *testing.T) {
 | 
			
		|||
	portMap, bindingMap, err = ParsePortSpecs([]string{"0.0.0.0:1234:1234/tcp", "0.0.0.0:2345:2345/udp"})
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Error while processing ParsePortSpecs: %s", err.Error())
 | 
			
		||||
		t.Fatalf("Error while processing ParsePortSpecs: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, ok := portMap[Port("1234/tcp")]; !ok {
 | 
			
		||||
| 
						 | 
				
			
			@ -199,3 +199,95 @@ func TestParsePortSpecs(t *testing.T) {
 | 
			
		|||
		t.Fatal("Received no error while trying to parse a hostname instead of ip")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParsePortSpecsWithRange(t *testing.T) {
 | 
			
		||||
	var (
 | 
			
		||||
		portMap    map[Port]struct{}
 | 
			
		||||
		bindingMap map[Port][]PortBinding
 | 
			
		||||
		err        error
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	portMap, bindingMap, err = ParsePortSpecs([]string{"1234-1236/tcp", "2345-2347/udp"})
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Error while processing ParsePortSpecs: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, ok := portMap[Port("1235/tcp")]; !ok {
 | 
			
		||||
		t.Fatal("1234/tcp was not parsed properly")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, ok := portMap[Port("2346/udp")]; !ok {
 | 
			
		||||
		t.Fatal("2345/udp was not parsed properly")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for portspec, bindings := range bindingMap {
 | 
			
		||||
		if len(bindings) != 1 {
 | 
			
		||||
			t.Fatalf("%s should have exactly one binding", portspec)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if bindings[0].HostIp != "" {
 | 
			
		||||
			t.Fatalf("HostIp should not be set for %s", portspec)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if bindings[0].HostPort != "" {
 | 
			
		||||
			t.Fatalf("HostPort should not be set for %s", portspec)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	portMap, bindingMap, err = ParsePortSpecs([]string{"1234-1236:1234-1236/tcp", "2345-2347:2345-2347/udp"})
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Error while processing ParsePortSpecs: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, ok := portMap[Port("1235/tcp")]; !ok {
 | 
			
		||||
		t.Fatal("1234/tcp was not parsed properly")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, ok := portMap[Port("2346/udp")]; !ok {
 | 
			
		||||
		t.Fatal("2345/udp was not parsed properly")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for portspec, bindings := range bindingMap {
 | 
			
		||||
		_, port := SplitProtoPort(string(portspec))
 | 
			
		||||
		if len(bindings) != 1 {
 | 
			
		||||
			t.Fatalf("%s should have exactly one binding", portspec)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if bindings[0].HostIp != "" {
 | 
			
		||||
			t.Fatalf("HostIp should not be set for %s", portspec)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if bindings[0].HostPort != port {
 | 
			
		||||
			t.Fatalf("HostPort should be %s for %s", port, portspec)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	portMap, bindingMap, err = ParsePortSpecs([]string{"0.0.0.0:1234-1236:1234-1236/tcp", "0.0.0.0:2345-2347:2345-2347/udp"})
 | 
			
		||||
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatalf("Error while processing ParsePortSpecs: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, ok := portMap[Port("1235/tcp")]; !ok {
 | 
			
		||||
		t.Fatal("1234/tcp was not parsed properly")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, ok := portMap[Port("2346/udp")]; !ok {
 | 
			
		||||
		t.Fatal("2345/udp was not parsed properly")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for portspec, bindings := range bindingMap {
 | 
			
		||||
		_, port := SplitProtoPort(string(portspec))
 | 
			
		||||
		if len(bindings) != 1 || bindings[0].HostIp != "0.0.0.0" || bindings[0].HostPort != port {
 | 
			
		||||
			t.Fatalf("Expect single binding to port %d but found %s", port, bindings)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_, _, err = ParsePortSpecs([]string{"localhost:1234-1236:1234-1236/tcp"})
 | 
			
		||||
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		t.Fatal("Received no error while trying to parse a hostname instead of ip")
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -104,3 +104,28 @@ func ParseKeyValueOpt(opt string) (string, string, error) {
 | 
			
		|||
	}
 | 
			
		||||
	return strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1]), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ParsePortRange(ports string) (uint64, uint64, error) {
 | 
			
		||||
	if ports == "" {
 | 
			
		||||
		return 0, 0, fmt.Errorf("Empty string specified for ports.")
 | 
			
		||||
	}
 | 
			
		||||
	if !strings.Contains(ports, "-") {
 | 
			
		||||
		start, err := strconv.ParseUint(ports, 10, 16)
 | 
			
		||||
		end := start
 | 
			
		||||
		return start, end, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	parts := strings.Split(ports, "-")
 | 
			
		||||
	start, err := strconv.ParseUint(parts[0], 10, 16)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, 0, err
 | 
			
		||||
	}
 | 
			
		||||
	end, err := strconv.ParseUint(parts[1], 10, 16)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, 0, err
 | 
			
		||||
	}
 | 
			
		||||
	if end < start {
 | 
			
		||||
		return 0, 0, fmt.Errorf("Invalid range specified for the Port: %s", ports)
 | 
			
		||||
	}
 | 
			
		||||
	return start, end, nil
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,7 @@
 | 
			
		|||
package parsers
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -81,3 +82,35 @@ func TestParsePortMapping(t *testing.T) {
 | 
			
		|||
		t.Fail()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParsePortRange(t *testing.T) {
 | 
			
		||||
	if start, end, err := ParsePortRange("8000-8080"); err != nil || start != 8000 || end != 8080 {
 | 
			
		||||
		t.Fatalf("Error: %s or Expecting {start,end} values {8000,8080} but found {%d,%d}.", err, start, end)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParsePortRangeIncorrectRange(t *testing.T) {
 | 
			
		||||
	if _, _, err := ParsePortRange("9000-8080"); err == nil || !strings.Contains(err.Error(), "Invalid range specified for the Port") {
 | 
			
		||||
		t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParsePortRangeIncorrectEndRange(t *testing.T) {
 | 
			
		||||
	if _, _, err := ParsePortRange("8000-a"); err == nil || !strings.Contains(err.Error(), "invalid syntax") {
 | 
			
		||||
		t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, _, err := ParsePortRange("8000-30a"); err == nil || !strings.Contains(err.Error(), "invalid syntax") {
 | 
			
		||||
		t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestParsePortRangeIncorrectStartRange(t *testing.T) {
 | 
			
		||||
	if _, _, err := ParsePortRange("a-8000"); err == nil || !strings.Contains(err.Error(), "invalid syntax") {
 | 
			
		||||
		t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if _, _, err := ParsePortRange("30a-8000"); err == nil || !strings.Contains(err.Error(), "invalid syntax") {
 | 
			
		||||
		t.Fatalf("Expecting error 'Invalid range specified for the Port' but received %s.", err)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -256,8 +256,8 @@ func TestMerge(t *testing.T) {
 | 
			
		|||
		t.Fatalf("Expected 4 ExposedPorts, 0000, 1111, 2222 and 3333, found %d", len(configUser.ExposedPorts))
 | 
			
		||||
	}
 | 
			
		||||
	for portSpecs := range configUser.ExposedPorts {
 | 
			
		||||
		if portSpecs.Port() != "0000" && portSpecs.Port() != "1111" && portSpecs.Port() != "2222" && portSpecs.Port() != "3333" {
 | 
			
		||||
			t.Fatalf("Expected 0000 or 1111 or 2222 or 3333, found %s", portSpecs)
 | 
			
		||||
		if portSpecs.Port() != "0" && portSpecs.Port() != "1111" && portSpecs.Port() != "2222" && portSpecs.Port() != "3333" {
 | 
			
		||||
			t.Fatalf("Expected %q or %q or %q or %q, found %s", 0, 1111, 2222, 3333, portSpecs)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -197,11 +197,12 @@ func Parse(cmd *flag.FlagSet, args []string) (*Config, *HostConfig, *flag.FlagSe
 | 
			
		|||
		if strings.Contains(e, "-") {
 | 
			
		||||
			proto, port := nat.SplitProtoPort(e)
 | 
			
		||||
			//parse the start and end port and create a sequence of ports to expose
 | 
			
		||||
			parts := strings.Split(port, "-")
 | 
			
		||||
			start, _ := strconv.Atoi(parts[0])
 | 
			
		||||
			end, _ := strconv.Atoi(parts[1])
 | 
			
		||||
			start, end, err := parsers.ParsePortRange(port)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, nil, cmd, fmt.Errorf("Invalid range format for --expose: %s, error: %s", e, err)
 | 
			
		||||
			}
 | 
			
		||||
			for i := start; i <= end; i++ {
 | 
			
		||||
				p := nat.NewPort(proto, strconv.Itoa(i))
 | 
			
		||||
				p := nat.NewPort(proto, strconv.FormatUint(i, 10))
 | 
			
		||||
				if _, exists := ports[p]; !exists {
 | 
			
		||||
					ports[p] = struct{}{}
 | 
			
		||||
				}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue