Signed-off-by: Srini Brahmaroutu <srbrahma@us.ibm.com>
| ... | ... |
@@ -5,7 +5,7 @@ import ( |
| 5 | 5 |
"io/ioutil" |
| 6 | 6 |
"net" |
| 7 | 7 |
"os" |
| 8 |
- "strings" |
|
| 8 |
+ "strconv" |
|
| 9 | 9 |
"sync" |
| 10 | 10 |
|
| 11 | 11 |
log "github.com/Sirupsen/logrus" |
| ... | ... |
@@ -14,6 +14,7 @@ import ( |
| 14 | 14 |
"github.com/docker/docker/daemon/networkdriver/portallocator" |
| 15 | 15 |
"github.com/docker/docker/daemon/networkdriver/portmapper" |
| 16 | 16 |
"github.com/docker/docker/engine" |
| 17 |
+ "github.com/docker/docker/nat" |
|
| 17 | 18 |
"github.com/docker/docker/pkg/iptables" |
| 18 | 19 |
"github.com/docker/docker/pkg/networkfs/resolvconf" |
| 19 | 20 |
"github.com/docker/docker/pkg/parsers/kernel" |
| ... | ... |
@@ -515,18 +516,13 @@ func LinkContainers(job *engine.Job) engine.Status {
|
| 515 | 515 |
ignoreErrors = job.GetenvBool("IgnoreErrors")
|
| 516 | 516 |
ports = job.GetenvList("Ports")
|
| 517 | 517 |
) |
| 518 |
- split := func(p string) (string, string) {
|
|
| 519 |
- parts := strings.Split(p, "/") |
|
| 520 |
- return parts[0], parts[1] |
|
| 521 |
- } |
|
| 522 |
- |
|
| 523 |
- for _, p := range ports {
|
|
| 524 |
- port, proto := split(p) |
|
| 518 |
+ for _, value := range ports {
|
|
| 519 |
+ port := nat.Port(value) |
|
| 525 | 520 |
if output, err := iptables.Raw(action, "FORWARD", |
| 526 | 521 |
"-i", bridgeIface, "-o", bridgeIface, |
| 527 |
- "-p", proto, |
|
| 522 |
+ "-p", port.Proto(), |
|
| 528 | 523 |
"-s", parentIP, |
| 529 |
- "--dport", port, |
|
| 524 |
+ "--dport", strconv.Itoa(port.Int()), |
|
| 530 | 525 |
"-d", childIP, |
| 531 | 526 |
"-j", "ACCEPT"); !ignoreErrors && err != nil {
|
| 532 | 527 |
return job.Error(err) |
| ... | ... |
@@ -536,9 +532,9 @@ func LinkContainers(job *engine.Job) engine.Status {
|
| 536 | 536 |
|
| 537 | 537 |
if output, err := iptables.Raw(action, "FORWARD", |
| 538 | 538 |
"-i", bridgeIface, "-o", bridgeIface, |
| 539 |
- "-p", proto, |
|
| 539 |
+ "-p", port.Proto(), |
|
| 540 | 540 |
"-s", childIP, |
| 541 |
- "--sport", port, |
|
| 541 |
+ "--sport", strconv.Itoa(port.Int()), |
|
| 542 | 542 |
"-d", parentIP, |
| 543 | 543 |
"-j", "ACCEPT"); !ignoreErrors && err != nil {
|
| 544 | 544 |
return job.Error(err) |
| ... | ... |
@@ -79,7 +79,7 @@ docker-create - Create a new container |
| 79 | 79 |
Read in a line delimited file of environment variables |
| 80 | 80 |
|
| 81 | 81 |
**--expose**=[] |
| 82 |
- Expose a port from the container without publishing it to your host |
|
| 82 |
+ Expose a port or a range of ports (e.g. --expose=3300-3310) from the container without publishing it to your host |
|
| 83 | 83 |
|
| 84 | 84 |
**-h**, **--hostname**="" |
| 85 | 85 |
Container host name |
| ... | ... |
@@ -132,12 +132,8 @@ ENTRYPOINT. |
| 132 | 132 |
**--env-file**=[] |
| 133 | 133 |
Read in a line delimited file of environment variables |
| 134 | 134 |
|
| 135 |
-**--expose**=*port* |
|
| 136 |
- Expose a port from the container without publishing it to your host. A |
|
| 137 |
-containers port can be exposed to other containers in three ways: 1) The |
|
| 138 |
-developer can expose the port using the EXPOSE parameter of the Dockerfile, 2) |
|
| 139 |
-the operator can use the **--expose** option with **docker run**, or 3) the |
|
| 140 |
-container can be started with the **--link**. |
|
| 135 |
+**--expose**=[] |
|
| 136 |
+ Expose a port or a range of ports (e.g. --expose=3300-3310) from the container without publishing it to your host |
|
| 141 | 137 |
|
| 142 | 138 |
**-h**, **--hostname**=*hostname* |
| 143 | 139 |
Sets the container host name that is available inside the container. |
| ... | ... |
@@ -509,7 +509,7 @@ Creates a new container. |
| 509 | 509 |
-e, --env=[] Set environment variables |
| 510 | 510 |
--entrypoint="" Overwrite the default ENTRYPOINT of the image |
| 511 | 511 |
--env-file=[] Read in a line delimited file of environment variables |
| 512 |
- --expose=[] Expose a port from the container without publishing it to your host |
|
| 512 |
+ --expose=[] Expose a port or a range of ports (e.g. --expose=3300-3310) from the container without publishing it to your host |
|
| 513 | 513 |
-h, --hostname="" Container host name |
| 514 | 514 |
-i, --interactive=false Keep STDIN open even if not attached |
| 515 | 515 |
--link=[] Add link to another container in the form of name:alias |
| ... | ... |
@@ -1211,7 +1211,7 @@ removed before the image is removed. |
| 1211 | 1211 |
-e, --env=[] Set environment variables |
| 1212 | 1212 |
--entrypoint="" Overwrite the default ENTRYPOINT of the image |
| 1213 | 1213 |
--env-file=[] Read in a line delimited file of environment variables |
| 1214 |
- --expose=[] Expose a port from the container without publishing it to your host |
|
| 1214 |
+ --expose=[] Expose a port or a range of ports (e.g. --expose=3300-3310) from the container without publishing it to your host |
|
| 1215 | 1215 |
-h, --hostname="" Container host name |
| 1216 | 1216 |
-i, --interactive=false Keep STDIN open even if not attached |
| 1217 | 1217 |
--link=[] Add link to another container in the form of name:alias |
| ... | ... |
@@ -413,7 +413,7 @@ the `EXPOSE` instruction to give a hint to the operator about what |
| 413 | 413 |
incoming ports might provide services. The following options work with |
| 414 | 414 |
or override the Dockerfile's exposed defaults: |
| 415 | 415 |
|
| 416 |
- --expose=[]: Expose a port from the container |
|
| 416 |
+ --expose=[]: Expose a port or a range of ports from the container |
|
| 417 | 417 |
without publishing it to your host |
| 418 | 418 |
-P=false : Publish all exposed ports to the host interfaces |
| 419 | 419 |
-p=[] : Publish a container᾿s port to the host (format: |
| ... | ... |
@@ -422,7 +422,7 @@ or override the Dockerfile's exposed defaults: |
| 422 | 422 |
(use 'docker port' to see the actual mapping) |
| 423 | 423 |
--link="" : Add link to another container (name:alias) |
| 424 | 424 |
|
| 425 |
-As mentioned previously, `EXPOSE` (and `--expose`) make a port available |
|
| 425 |
+As mentioned previously, `EXPOSE` (and `--expose`) makes ports available |
|
| 426 | 426 |
**in** a container for incoming connections. The port number on the |
| 427 | 427 |
inside of the container (where the service listens) does not need to be |
| 428 | 428 |
the same number as the port exposed on the outside of the container |
| ... | ... |
@@ -13,11 +13,13 @@ import ( |
| 13 | 13 |
"reflect" |
| 14 | 14 |
"regexp" |
| 15 | 15 |
"sort" |
| 16 |
+ "strconv" |
|
| 16 | 17 |
"strings" |
| 17 | 18 |
"sync" |
| 18 | 19 |
"testing" |
| 19 | 20 |
"time" |
| 20 | 21 |
|
| 22 |
+ "github.com/docker/docker/nat" |
|
| 21 | 23 |
"github.com/docker/docker/pkg/mount" |
| 22 | 24 |
"github.com/docker/docker/pkg/networkfs/resolvconf" |
| 23 | 25 |
"github.com/kr/pty" |
| ... | ... |
@@ -2473,3 +2475,31 @@ func TestRunSlowStdoutConsumer(t *testing.T) {
|
| 2473 | 2473 |
|
| 2474 | 2474 |
logDone("run - slow consumer")
|
| 2475 | 2475 |
} |
| 2476 |
+ |
|
| 2477 |
+func TestRunAllowPortRangeThroughExpose(t *testing.T) {
|
|
| 2478 |
+ cmd := exec.Command(dockerBinary, "run", "-d", "--expose", "3000-3003", "-P", "busybox", "top") |
|
| 2479 |
+ out, _, err := runCommandWithOutput(cmd) |
|
| 2480 |
+ if err != nil {
|
|
| 2481 |
+ t.Fatal(err) |
|
| 2482 |
+ } |
|
| 2483 |
+ id := strings.TrimSpace(out) |
|
| 2484 |
+ portstr, err := inspectFieldJSON(id, "NetworkSettings.Ports") |
|
| 2485 |
+ if err != nil {
|
|
| 2486 |
+ t.Fatal(err) |
|
| 2487 |
+ } |
|
| 2488 |
+ var ports nat.PortMap |
|
| 2489 |
+ err = unmarshalJSON([]byte(portstr), &ports) |
|
| 2490 |
+ for port, binding := range ports {
|
|
| 2491 |
+ portnum, _ := strconv.Atoi(strings.Split(string(port), "/")[0]) |
|
| 2492 |
+ if portnum < 3000 || portnum > 3003 {
|
|
| 2493 |
+ t.Fatalf("Port is out of range ", portnum, binding, out)
|
|
| 2494 |
+ } |
|
| 2495 |
+ if binding == nil || len(binding) != 1 || len(binding[0].HostPort) == 0 {
|
|
| 2496 |
+ t.Fatal("Port is not mapped for the port "+port, out)
|
|
| 2497 |
+ } |
|
| 2498 |
+ } |
|
| 2499 |
+ if err := deleteContainer(id); err != nil {
|
|
| 2500 |
+ t.Fatal(err) |
|
| 2501 |
+ } |
|
| 2502 |
+ logDone("run - allow port range through --expose flag")
|
|
| 2503 |
+} |
| ... | ... |
@@ -47,6 +47,20 @@ func (l *Link) Alias() string {
|
| 47 | 47 |
return alias |
| 48 | 48 |
} |
| 49 | 49 |
|
| 50 |
+func nextContiguous(ports []nat.Port, value int, index int) int {
|
|
| 51 |
+ if index+1 == len(ports) {
|
|
| 52 |
+ return index |
|
| 53 |
+ } |
|
| 54 |
+ for i := index + 1; i < len(ports); i++ {
|
|
| 55 |
+ if ports[i].Int() > value+1 {
|
|
| 56 |
+ return i - 1 |
|
| 57 |
+ } |
|
| 58 |
+ |
|
| 59 |
+ value++ |
|
| 60 |
+ } |
|
| 61 |
+ return len(ports) - 1 |
|
| 62 |
+} |
|
| 63 |
+ |
|
| 50 | 64 |
func (l *Link) ToEnv() []string {
|
| 51 | 65 |
env := []string{}
|
| 52 | 66 |
alias := strings.Replace(strings.ToUpper(l.Alias()), "-", "_", -1) |
| ... | ... |
@@ -55,12 +69,35 @@ func (l *Link) ToEnv() []string {
|
| 55 | 55 |
env = append(env, fmt.Sprintf("%s_PORT=%s://%s:%s", alias, p.Proto(), l.ChildIP, p.Port()))
|
| 56 | 56 |
} |
| 57 | 57 |
|
| 58 |
- // Load exposed ports into the environment |
|
| 59 |
- for _, p := range l.Ports {
|
|
| 58 |
+ //sort the ports so that we can bulk the continuous ports together |
|
| 59 |
+ nat.Sort(l.Ports, func(ip, jp nat.Port) bool {
|
|
| 60 |
+ // If the two ports have the same number, tcp takes priority |
|
| 61 |
+ // Sort in desc order |
|
| 62 |
+ return ip.Int() < jp.Int() || (ip.Int() == jp.Int() && strings.ToLower(ip.Proto()) == "tcp") |
|
| 63 |
+ }) |
|
| 64 |
+ |
|
| 65 |
+ for i := 0; i < len(l.Ports); {
|
|
| 66 |
+ p := l.Ports[i] |
|
| 67 |
+ j := nextContiguous(l.Ports, p.Int(), i) |
|
| 68 |
+ if j > i+1 {
|
|
| 69 |
+ env = append(env, fmt.Sprintf("%s_PORT_%s_%s_START=%s://%s:%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto(), l.ChildIP, p.Port()))
|
|
| 70 |
+ env = append(env, fmt.Sprintf("%s_PORT_%s_%s_ADDR=%s", alias, p.Port(), strings.ToUpper(p.Proto()), l.ChildIP))
|
|
| 71 |
+ env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PROTO=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto()))
|
|
| 72 |
+ env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT_START=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Port()))
|
|
| 73 |
+ |
|
| 74 |
+ q := l.Ports[j] |
|
| 75 |
+ env = append(env, fmt.Sprintf("%s_PORT_%s_%s_END=%s://%s:%s", alias, p.Port(), strings.ToUpper(q.Proto()), q.Proto(), l.ChildIP, q.Port()))
|
|
| 76 |
+ env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT_END=%s", alias, p.Port(), strings.ToUpper(q.Proto()), q.Port()))
|
|
| 77 |
+ |
|
| 78 |
+ i = j + 1 |
|
| 79 |
+ continue |
|
| 80 |
+ } |
|
| 81 |
+ |
|
| 60 | 82 |
env = append(env, fmt.Sprintf("%s_PORT_%s_%s=%s://%s:%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto(), l.ChildIP, p.Port()))
|
| 61 | 83 |
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_ADDR=%s", alias, p.Port(), strings.ToUpper(p.Proto()), l.ChildIP))
|
| 62 | 84 |
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PORT=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Port()))
|
| 63 | 85 |
env = append(env, fmt.Sprintf("%s_PORT_%s_%s_PROTO=%s", alias, p.Port(), strings.ToUpper(p.Proto()), p.Proto()))
|
| 86 |
+ i++ |
|
| 64 | 87 |
} |
| 65 | 88 |
|
| 66 | 89 |
// Load the linked container's name into the environment |
| ... | ... |
@@ -125,7 +162,7 @@ func (l *Link) toggle(action string, ignoreErrors bool) error {
|
| 125 | 125 |
|
| 126 | 126 |
out := make([]string, len(l.Ports)) |
| 127 | 127 |
for i, p := range l.Ports {
|
| 128 |
- out[i] = fmt.Sprintf("%s/%s", p.Port(), p.Proto())
|
|
| 128 |
+ out[i] = string(p) |
|
| 129 | 129 |
} |
| 130 | 130 |
job.SetenvList("Ports", out)
|
| 131 | 131 |
|
| ... | ... |
@@ -107,3 +107,52 @@ func TestLinkEnv(t *testing.T) {
|
| 107 | 107 |
t.Fatalf("Expected gordon, got %s", env["DOCKER_ENV_PASSWORD"])
|
| 108 | 108 |
} |
| 109 | 109 |
} |
| 110 |
+ |
|
| 111 |
+func TestLinkMultipleEnv(t *testing.T) {
|
|
| 112 |
+ ports := make(nat.PortSet) |
|
| 113 |
+ ports[nat.Port("6379/tcp")] = struct{}{}
|
|
| 114 |
+ ports[nat.Port("6380/tcp")] = struct{}{}
|
|
| 115 |
+ ports[nat.Port("6381/tcp")] = struct{}{}
|
|
| 116 |
+ |
|
| 117 |
+ link, err := NewLink("172.0.17.3", "172.0.17.2", "/db/docker", []string{"PASSWORD=gordon"}, ports, nil)
|
|
| 118 |
+ if err != nil {
|
|
| 119 |
+ t.Fatal(err) |
|
| 120 |
+ } |
|
| 121 |
+ |
|
| 122 |
+ rawEnv := link.ToEnv() |
|
| 123 |
+ env := make(map[string]string, len(rawEnv)) |
|
| 124 |
+ for _, e := range rawEnv {
|
|
| 125 |
+ parts := strings.Split(e, "=") |
|
| 126 |
+ if len(parts) != 2 {
|
|
| 127 |
+ t.FailNow() |
|
| 128 |
+ } |
|
| 129 |
+ env[parts[0]] = parts[1] |
|
| 130 |
+ } |
|
| 131 |
+ if env["DOCKER_PORT"] != "tcp://172.0.17.2:6379" {
|
|
| 132 |
+ t.Fatalf("Expected 172.0.17.2:6379, got %s", env["DOCKER_PORT"])
|
|
| 133 |
+ } |
|
| 134 |
+ if env["DOCKER_PORT_6379_TCP_START"] != "tcp://172.0.17.2:6379" {
|
|
| 135 |
+ t.Fatalf("Expected tcp://172.0.17.2:6379, got %s", env["DOCKER_PORT_6379_TCP_START"])
|
|
| 136 |
+ } |
|
| 137 |
+ if env["DOCKER_PORT_6379_TCP_END"] != "tcp://172.0.17.2:6381" {
|
|
| 138 |
+ t.Fatalf("Expected tcp://172.0.17.2:6381, got %s", env["DOCKER_PORT_6379_TCP_END"])
|
|
| 139 |
+ } |
|
| 140 |
+ if env["DOCKER_PORT_6379_TCP_PROTO"] != "tcp" {
|
|
| 141 |
+ t.Fatalf("Expected tcp, got %s", env["DOCKER_PORT_6379_TCP_PROTO"])
|
|
| 142 |
+ } |
|
| 143 |
+ if env["DOCKER_PORT_6379_TCP_ADDR"] != "172.0.17.2" {
|
|
| 144 |
+ t.Fatalf("Expected 172.0.17.2, got %s", env["DOCKER_PORT_6379_TCP_ADDR"])
|
|
| 145 |
+ } |
|
| 146 |
+ if env["DOCKER_PORT_6379_TCP_PORT_START"] != "6379" {
|
|
| 147 |
+ t.Fatalf("Expected 6379, got %s", env["DOCKER_PORT_6379_TCP_PORT_START"])
|
|
| 148 |
+ } |
|
| 149 |
+ if env["DOCKER_PORT_6379_TCP_PORT_END"] != "6381" {
|
|
| 150 |
+ t.Fatalf("Expected 6381, got %s", env["DOCKER_PORT_6379_TCP_PORT_END"])
|
|
| 151 |
+ } |
|
| 152 |
+ if env["DOCKER_NAME"] != "/db/docker" {
|
|
| 153 |
+ t.Fatalf("Expected /db/docker, got %s", env["DOCKER_NAME"])
|
|
| 154 |
+ } |
|
| 155 |
+ if env["DOCKER_ENV_PASSWORD"] != "gordon" {
|
|
| 156 |
+ t.Fatalf("Expected gordon, got %s", env["DOCKER_ENV_PASSWORD"])
|
|
| 157 |
+ } |
|
| 158 |
+} |
| ... | ... |
@@ -42,44 +42,37 @@ func ParsePort(rawPort string) (int, error) {
|
| 42 | 42 |
} |
| 43 | 43 |
|
| 44 | 44 |
func (p Port) Proto() string {
|
| 45 |
- parts := strings.Split(string(p), "/") |
|
| 46 |
- if len(parts) == 1 {
|
|
| 47 |
- return "tcp" |
|
| 48 |
- } |
|
| 49 |
- return parts[1] |
|
| 45 |
+ proto, _ := SplitProtoPort(string(p)) |
|
| 46 |
+ return proto |
|
| 50 | 47 |
} |
| 51 | 48 |
|
| 52 | 49 |
func (p Port) Port() string {
|
| 53 |
- return strings.Split(string(p), "/")[0] |
|
| 50 |
+ _, port := SplitProtoPort(string(p)) |
|
| 51 |
+ return port |
|
| 54 | 52 |
} |
| 55 | 53 |
|
| 56 | 54 |
func (p Port) Int() int {
|
| 57 |
- i, err := ParsePort(p.Port()) |
|
| 55 |
+ port, err := ParsePort(p.Port()) |
|
| 58 | 56 |
if err != nil {
|
| 59 | 57 |
panic(err) |
| 60 | 58 |
} |
| 61 |
- return i |
|
| 59 |
+ return port |
|
| 62 | 60 |
} |
| 63 | 61 |
|
| 64 | 62 |
// Splits a port in the format of proto/port |
| 65 | 63 |
func SplitProtoPort(rawPort string) (string, string) {
|
| 66 |
- var port string |
|
| 67 |
- var proto string |
|
| 68 |
- |
|
| 69 | 64 |
parts := strings.Split(rawPort, "/") |
| 70 |
- |
|
| 71 |
- if len(parts) == 0 || parts[0] == "" { // we have "" or ""/
|
|
| 72 |
- port = "" |
|
| 73 |
- proto = "" |
|
| 74 |
- } else { // we have # or #/ or #/...
|
|
| 75 |
- port = parts[0] |
|
| 76 |
- if len(parts) > 1 && parts[1] != "" {
|
|
| 77 |
- proto = parts[1] // we have #/... |
|
| 78 |
- } else {
|
|
| 79 |
- proto = "tcp" // we have # or #/ |
|
| 80 |
- } |
|
| 65 |
+ l := len(parts) |
|
| 66 |
+ if len(rawPort) == 0 || l == 0 || len(parts[0]) == 0 {
|
|
| 67 |
+ return "", "" |
|
| 68 |
+ } |
|
| 69 |
+ if l == 1 {
|
|
| 70 |
+ return "tcp", rawPort |
|
| 71 |
+ } |
|
| 72 |
+ if len(parts[1]) == 0 {
|
|
| 73 |
+ return "tcp", parts[0] |
|
| 81 | 74 |
} |
| 82 |
- return proto, port |
|
| 75 |
+ return parts[1], parts[0] |
|
| 83 | 76 |
} |
| 84 | 77 |
|
| 85 | 78 |
func validateProto(proto string) bool {
|
| ... | ... |
@@ -76,13 +76,13 @@ func TestSplitProtoPort(t *testing.T) {
|
| 76 | 76 |
proto, port = SplitProtoPort("")
|
| 77 | 77 |
|
| 78 | 78 |
if proto != "" || port != "" {
|
| 79 |
- t.Fatal("parsing an empty string yielded surprising results")
|
|
| 79 |
+ t.Fatal("parsing an empty string yielded surprising results", proto, port)
|
|
| 80 | 80 |
} |
| 81 | 81 |
|
| 82 | 82 |
proto, port = SplitProtoPort("1234")
|
| 83 | 83 |
|
| 84 | 84 |
if proto != "tcp" || port != "1234" {
|
| 85 |
- t.Fatal("tcp is not the default protocol for portspec '1234'")
|
|
| 85 |
+ t.Fatal("tcp is not the default protocol for portspec '1234'", proto, port)
|
|
| 86 | 86 |
} |
| 87 | 87 |
|
| 88 | 88 |
proto, port = SplitProtoPort("1234/")
|
| ... | ... |
@@ -71,7 +71,7 @@ func Parse(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, |
| 71 | 71 |
cmd.Var(&flEnvFile, []string{"-env-file"}, "Read in a line delimited file of environment variables")
|
| 72 | 72 |
|
| 73 | 73 |
cmd.Var(&flPublish, []string{"p", "-publish"}, fmt.Sprintf("Publish a container's port to the host\nformat: %s\n(use 'docker port' to see the actual mapping)", nat.PortSpecTemplateFormat))
|
| 74 |
- cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port from the container without publishing it to your host")
|
|
| 74 |
+ cmd.Var(&flExpose, []string{"#expose", "-expose"}, "Expose a port or a range of ports (e.g. --expose=3300-3310) from the container without publishing it to your host")
|
|
| 75 | 75 |
cmd.Var(&flDns, []string{"#dns", "-dns"}, "Set custom DNS servers")
|
| 76 | 76 |
cmd.Var(&flDnsSearch, []string{"-dns-search"}, "Set custom DNS search domains (Use --dns-search=. if you don't wish to set the search domain)")
|
| 77 | 77 |
cmd.Var(&flExtraHosts, []string{"-add-host"}, "Add a custom host-to-IP mapping (host:ip)")
|
| ... | ... |
@@ -197,9 +197,24 @@ func Parse(cmd *flag.FlagSet, args []string, sysInfo *sysinfo.SysInfo) (*Config, |
| 197 | 197 |
if strings.Contains(e, ":") {
|
| 198 | 198 |
return nil, nil, cmd, fmt.Errorf("Invalid port format for --expose: %s", e)
|
| 199 | 199 |
} |
| 200 |
- p := nat.NewPort(nat.SplitProtoPort(e)) |
|
| 201 |
- if _, exists := ports[p]; !exists {
|
|
| 202 |
- ports[p] = struct{}{}
|
|
| 200 |
+ //support two formats for expose, original format <portnum>/[<proto>] or <startport-endport>/[<proto>] |
|
| 201 |
+ if strings.Contains(e, "-") {
|
|
| 202 |
+ proto, port := nat.SplitProtoPort(e) |
|
| 203 |
+ //parse the start and end port and create a sequence of ports to expose |
|
| 204 |
+ parts := strings.Split(port, "-") |
|
| 205 |
+ start, _ := strconv.Atoi(parts[0]) |
|
| 206 |
+ end, _ := strconv.Atoi(parts[1]) |
|
| 207 |
+ for i := start; i <= end; i++ {
|
|
| 208 |
+ p := nat.NewPort(proto, strconv.Itoa(i)) |
|
| 209 |
+ if _, exists := ports[p]; !exists {
|
|
| 210 |
+ ports[p] = struct{}{}
|
|
| 211 |
+ } |
|
| 212 |
+ } |
|
| 213 |
+ } else {
|
|
| 214 |
+ p := nat.NewPort(nat.SplitProtoPort(e)) |
|
| 215 |
+ if _, exists := ports[p]; !exists {
|
|
| 216 |
+ ports[p] = struct{}{}
|
|
| 217 |
+ } |
|
| 203 | 218 |
} |
| 204 | 219 |
} |
| 205 | 220 |
|