diff --git a/api/client/commands.go b/api/client/commands.go index e5495dbf84..4a5f2faea8 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -861,26 +861,15 @@ func (cli *DockerCli) CmdTop(args ...string) error { } func (cli *DockerCli) CmdPort(args ...string) error { - cmd := cli.Subcmd("port", "CONTAINER PRIVATE_PORT", "Lookup the public-facing port that is NAT-ed to PRIVATE_PORT") + cmd := cli.Subcmd("port", "CONTAINER [PRIVATE_PORT[/PROTO]]", "List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT") if err := cmd.Parse(args); err != nil { return nil } - if cmd.NArg() != 2 { + if cmd.NArg() < 1 { cmd.Usage() return nil } - var ( - port = cmd.Arg(1) - proto = "tcp" - parts = strings.SplitN(port, "/", 2) - ) - - if len(parts) == 2 && len(parts[1]) != 0 { - port = parts[0] - proto = parts[1] - } - steam, _, err := cli.call("GET", "/containers/"+cmd.Arg(0)+"/json", nil, false) if err != nil { return err @@ -895,14 +884,34 @@ func (cli *DockerCli) CmdPort(args ...string) error { return err } - if frontends, exists := ports[nat.Port(port+"/"+proto)]; exists && frontends != nil { - for _, frontend := range frontends { - fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort) + if cmd.NArg() == 2 { + var ( + port = cmd.Arg(1) + proto = "tcp" + parts = strings.SplitN(port, "/", 2) + ) + + if len(parts) == 2 && len(parts[1]) != 0 { + port = parts[0] + proto = parts[1] } - return nil + natPort := port + "/" + proto + if frontends, exists := ports[nat.Port(port+"/"+proto)]; exists && frontends != nil { + for _, frontend := range frontends { + fmt.Fprintf(cli.out, "%s:%s\n", frontend.HostIp, frontend.HostPort) + } + return nil + } + return fmt.Errorf("Error: No public port '%s' published for %s", natPort, cmd.Arg(0)) } - return fmt.Errorf("Error: No public port '%s' published for %s", cmd.Arg(1), cmd.Arg(0)) + for from, frontends := range ports { + for _, frontend := range frontends { + fmt.Fprintf(cli.out, "%s -> %s:%s\n", from, frontend.HostIp, frontend.HostPort) + } + } + + return nil } // 'docker rmi IMAGE' removes all images with the name IMAGE diff --git a/docs/man/docker-port.1.md b/docs/man/docker-port.1.md index 07b84b12d9..97cc61b7e5 100644 --- a/docs/man/docker-port.1.md +++ b/docs/man/docker-port.1.md @@ -2,14 +2,30 @@ % Docker Community % JUNE 2014 # NAME -docker-port - Lookup the public-facing port that is NAT-ed to PRIVATE_PORT +docker-port - List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT # SYNOPSIS -**docker port** -CONTAINER PRIVATE_PORT +**docker port** CONTAINER [PRIVATE_PORT[/PROTO]] -# OPTIONS -There are no available options. +# DESCRIPTION +List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT + +# EXAMPLES +You can find out all the ports mapped by not specifying a `PRIVATE_PORT`, or +ask for just a specific mapping: + + $ docker ps test + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + b650456536c7 busybox:latest top 54 minutes ago Up 54 minutes 0.0.0.0:1234->9876/tcp, 0.0.0.0:4321->7890/tcp test + $ docker port test + 7890/tcp -> 0.0.0.0:4321 + 9876/tcp -> 0.0.0.0:1234 + $ docker port test 7890/tcp + 0.0.0.0:4321 + $ docker port test 7890/udp + 2014/06/24 11:53:36 Error: No public port '7890/udp' published for test + $ docker port test 7890 + 0.0.0.0:4321 # HISTORY April 2014, Originally compiled by William Henry (whenry at redhat dot com) diff --git a/docs/sources/reference/commandline/cli.md b/docs/sources/reference/commandline/cli.md index ef30113882..9c313b0c78 100644 --- a/docs/sources/reference/commandline/cli.md +++ b/docs/sources/reference/commandline/cli.md @@ -764,9 +764,25 @@ log entry. ## port - Usage: docker port CONTAINER PRIVATE_PORT + Usage: docker port CONTAINER [PRIVATE_PORT[/PROTO]] - Lookup the public-facing port that is NAT-ed to PRIVATE_PORT + List port mappings for the CONTAINER, or lookup the public-facing port that is NAT-ed to the PRIVATE_PORT + +You can find out all the ports mapped by not specifying a `PRIVATE_PORT`, or +just a specific mapping: + + $ docker ps test + CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES + b650456536c7 busybox:latest top 54 minutes ago Up 54 minutes 0.0.0.0:1234->9876/tcp, 0.0.0.0:4321->7890/tcp test + $ docker port test + 7890/tcp -> 0.0.0.0:4321 + 9876/tcp -> 0.0.0.0:1234 + $ docker port test 7890/tcp + 0.0.0.0:4321 + $ docker port test 7890/udp + 2014/06/24 11:53:36 Error: No public port '7890/udp' published for test + $ docker port test 7890 + 0.0.0.0:4321 ## pause diff --git a/integration-cli/docker_cli_port_test.go b/integration-cli/docker_cli_port_test.go new file mode 100644 index 0000000000..667889227f --- /dev/null +++ b/integration-cli/docker_cli_port_test.go @@ -0,0 +1,125 @@ +package main + +import ( + "os/exec" + "sort" + "strings" + "testing" +) + +func TestListPorts(t *testing.T) { + // one port + runCmd := exec.Command(dockerBinary, "run", "-d", "-p", "9876:80", "busybox", "top") + out, _, err := runCommandWithOutput(runCmd) + errorOut(err, t, out) + firstID := stripTrailingCharacters(out) + + runCmd = exec.Command(dockerBinary, "port", firstID, "80") + out, _, err = runCommandWithOutput(runCmd) + errorOut(err, t, out) + + if !assertPortList(t, out, []string{"0.0.0.0:9876"}) { + t.Error("Port list is not correct") + } + + runCmd = exec.Command(dockerBinary, "port", firstID) + out, _, err = runCommandWithOutput(runCmd) + errorOut(err, t, out) + + if !assertPortList(t, out, []string{"80/tcp -> 0.0.0.0:9876"}) { + t.Error("Port list is not correct") + } + runCmd = exec.Command(dockerBinary, "rm", "-f", firstID) + out, _, err = runCommandWithOutput(runCmd) + errorOut(err, t, out) + + // three port + runCmd = exec.Command(dockerBinary, "run", "-d", + "-p", "9876:80", + "-p", "9877:81", + "-p", "9878:82", + "busybox", "top") + out, _, err = runCommandWithOutput(runCmd) + errorOut(err, t, out) + ID := stripTrailingCharacters(out) + + runCmd = exec.Command(dockerBinary, "port", ID, "80") + out, _, err = runCommandWithOutput(runCmd) + errorOut(err, t, out) + + if !assertPortList(t, out, []string{"0.0.0.0:9876"}) { + t.Error("Port list is not correct") + } + + runCmd = exec.Command(dockerBinary, "port", ID) + out, _, err = runCommandWithOutput(runCmd) + errorOut(err, t, out) + + if !assertPortList(t, out, []string{ + "80/tcp -> 0.0.0.0:9876", + "81/tcp -> 0.0.0.0:9877", + "82/tcp -> 0.0.0.0:9878"}) { + t.Error("Port list is not correct") + } + runCmd = exec.Command(dockerBinary, "rm", "-f", ID) + out, _, err = runCommandWithOutput(runCmd) + errorOut(err, t, out) + + // more and one port mapped to the same container port + runCmd = exec.Command(dockerBinary, "run", "-d", + "-p", "9876:80", + "-p", "9999:80", + "-p", "9877:81", + "-p", "9878:82", + "busybox", "top") + out, _, err = runCommandWithOutput(runCmd) + errorOut(err, t, out) + ID = stripTrailingCharacters(out) + + runCmd = exec.Command(dockerBinary, "port", ID, "80") + out, _, err = runCommandWithOutput(runCmd) + errorOut(err, t, out) + + if !assertPortList(t, out, []string{"0.0.0.0:9876", "0.0.0.0:9999"}) { + t.Error("Port list is not correct") + } + + runCmd = exec.Command(dockerBinary, "port", ID) + out, _, err = runCommandWithOutput(runCmd) + errorOut(err, t, out) + + if !assertPortList(t, out, []string{ + "80/tcp -> 0.0.0.0:9876", + "80/tcp -> 0.0.0.0:9999", + "81/tcp -> 0.0.0.0:9877", + "82/tcp -> 0.0.0.0:9878"}) { + t.Error("Port list is not correct\n", out) + } + runCmd = exec.Command(dockerBinary, "rm", "-f", ID) + out, _, err = runCommandWithOutput(runCmd) + errorOut(err, t, out) + + deleteAllContainers() + + logDone("port - test port list") +} + +func assertPortList(t *testing.T, out string, expected []string) bool { + //lines := strings.Split(out, "\n") + lines := strings.Split(strings.Trim(out, "\n "), "\n") + if len(lines) != len(expected) { + t.Errorf("different size lists %s, %d, %d", out, len(lines), len(expected)) + return false + } + sort.Strings(lines) + sort.Strings(expected) + + for i := 0; i < len(expected); i++ { + if lines[i] != expected[i] { + t.Error("|" + lines[i] + "!=" + expected[i] + "|") + return false + } + } + + return true +}