diff --git a/api/client/commands.go b/api/client/commands.go index bf49939b5b..8387db58d1 100644 --- a/api/client/commands.go +++ b/api/client/commands.go @@ -19,7 +19,6 @@ func (cli *DockerCli) Command(name string) func(...string) error { "login": cli.CmdLogin, "logout": cli.CmdLogout, "pause": cli.CmdPause, - "port": cli.CmdPort, "ps": cli.CmdPs, "pull": cli.CmdPull, "push": cli.CmdPush, diff --git a/api/client/container/port.go b/api/client/container/port.go new file mode 100644 index 0000000000..71b78598a1 --- /dev/null +++ b/api/client/container/port.go @@ -0,0 +1,80 @@ +package container + +import ( + "fmt" + "strings" + + "golang.org/x/net/context" + + "github.com/docker/docker/api/client" + "github.com/docker/docker/cli" + "github.com/docker/go-connections/nat" + "github.com/spf13/cobra" +) + +type portOptions struct { + container string + + port string +} + +// NewPortCommand creats a new cobra.Command for `docker port` +func NewPortCommand(dockerCli *client.DockerCli) *cobra.Command { + var opts portOptions + + cmd := &cobra.Command{ + Use: "port CONTAINER [PRIVATE_PORT[/PROTO]]", + Short: "List port mappings or a specific mapping for the container", + Args: cli.RequiresMinMaxArgs(1, 2), + RunE: func(cmd *cobra.Command, args []string) error { + opts.container = args[0] + if len(args) > 1 { + opts.port = args[1] + } + return runPort(dockerCli, &opts) + }, + } + cmd.SetFlagErrorFunc(flagErrorFunc) + + return cmd +} + +func runPort(dockerCli *client.DockerCli, opts *portOptions) error { + ctx := context.Background() + + c, err := dockerCli.Client().ContainerInspect(ctx, opts.container) + if err != nil { + return err + } + + if opts.port != "" { + port := opts.port + proto := "tcp" + parts := strings.SplitN(port, "/", 2) + + if len(parts) == 2 && len(parts[1]) != 0 { + port = parts[0] + proto = parts[1] + } + natPort := port + "/" + proto + newP, err := nat.NewPort(proto, port) + if err != nil { + return err + } + if frontends, exists := c.NetworkSettings.Ports[newP]; exists && frontends != nil { + for _, frontend := range frontends { + fmt.Fprintf(dockerCli.Out(), "%s:%s\n", frontend.HostIP, frontend.HostPort) + } + return nil + } + return fmt.Errorf("Error: No public port '%s' published for %s", natPort, opts.container) + } + + for from, frontends := range c.NetworkSettings.Ports { + for _, frontend := range frontends { + fmt.Fprintf(dockerCli.Out(), "%s -> %s:%s\n", from, frontend.HostIP, frontend.HostPort) + } + } + + return nil +} diff --git a/api/client/port.go b/api/client/port.go deleted file mode 100644 index 9b545f5651..0000000000 --- a/api/client/port.go +++ /dev/null @@ -1,61 +0,0 @@ -package client - -import ( - "fmt" - "strings" - - "golang.org/x/net/context" - - Cli "github.com/docker/docker/cli" - flag "github.com/docker/docker/pkg/mflag" - "github.com/docker/go-connections/nat" -) - -// CmdPort lists port mappings for a container. -// If a private port is specified, it also shows the public-facing port that is NATed to the private port. -// -// Usage: docker port CONTAINER [PRIVATE_PORT[/PROTO]] -func (cli *DockerCli) CmdPort(args ...string) error { - cmd := Cli.Subcmd("port", []string{"CONTAINER [PRIVATE_PORT[/PROTO]]"}, Cli.DockerCommands["port"].Description, true) - cmd.Require(flag.Min, 1) - - cmd.ParseFlags(args, true) - - c, err := cli.client.ContainerInspect(context.Background(), cmd.Arg(0)) - if err != nil { - return err - } - - 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] - } - natPort := port + "/" + proto - newP, err := nat.NewPort(proto, port) - if err != nil { - return err - } - if frontends, exists := c.NetworkSettings.Ports[newP]; 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)) - } - - for from, frontends := range c.NetworkSettings.Ports { - for _, frontend := range frontends { - fmt.Fprintf(cli.out, "%s -> %s:%s\n", from, frontend.HostIP, frontend.HostPort) - } - } - - return nil -} diff --git a/cli/cobraadaptor/adaptor.go b/cli/cobraadaptor/adaptor.go index ee51435b87..a99f376ca1 100644 --- a/cli/cobraadaptor/adaptor.go +++ b/cli/cobraadaptor/adaptor.go @@ -38,6 +38,7 @@ func NewCobraAdaptor(clientFlags *cliflags.ClientFlags) CobraAdaptor { container.NewDiffCommand(dockerCli), container.NewExportCommand(dockerCli), container.NewLogsCommand(dockerCli), + container.NewPortCommand(dockerCli), container.NewRunCommand(dockerCli), container.NewStartCommand(dockerCli), container.NewStopCommand(dockerCli), diff --git a/cli/required.go b/cli/required.go index 1bbd55f524..0cf35b3d68 100644 --- a/cli/required.go +++ b/cli/required.go @@ -43,6 +43,24 @@ func RequiresMinArgs(min int) cobra.PositionalArgs { } } +// RequiresMinMaxArgs returns an error if there is not at least min args and at most max args +func RequiresMinMaxArgs(min int, max int) cobra.PositionalArgs { + return func(cmd *cobra.Command, args []string) error { + if len(args) >= min && len(args) <= max { + return nil + } + return fmt.Errorf( + "\"%s\" requires at least %d and at most %d argument(s).\nSee '%s --help'.\n\nUsage: %s\n\n%s", + cmd.CommandPath(), + min, + max, + cmd.CommandPath(), + cmd.UseLine(), + cmd.Short, + ) + } +} + // ExactArgs returns an error if there is not the exact number of args func ExactArgs(number int) cobra.PositionalArgs { return func(cmd *cobra.Command, args []string) error { diff --git a/cli/usage.go b/cli/usage.go index 23c357e1cf..7ea082629f 100644 --- a/cli/usage.go +++ b/cli/usage.go @@ -24,7 +24,6 @@ var DockerCommandUsage = []Command{ {"login", "Log in to a Docker registry"}, {"logout", "Log out from a Docker registry"}, {"pause", "Pause all processes within a container"}, - {"port", "List port mappings or a specific mapping for the CONTAINER"}, {"ps", "List containers"}, {"pull", "Pull an image or a repository from a registry"}, {"push", "Push an image or a repository to a registry"},