From 29d3f131ed3bb8109a6b2958146f5393050dff4a Mon Sep 17 00:00:00 2001 From: yeya24 Date: Wed, 5 Jun 2019 23:10:14 +0800 Subject: [PATCH] feature/add-port-cmd Signed-off-by: yeya24 fix example Signed-off-by: yeya24 add one arg example to test Signed-off-by: yeya24 --- cli/main.go | 1 + cli/port.go | 97 +++++++++++++++++++++++++++++++++ test/cli_container_cp_test.go | 2 +- test/cli_container_port_test.go | 76 ++++++++++++++++++++++++++ 4 files changed, 175 insertions(+), 1 deletion(-) create mode 100644 cli/port.go create mode 100644 test/cli_container_port_test.go diff --git a/cli/main.go b/cli/main.go index ecc094998b..c54ebcf4ce 100644 --- a/cli/main.go +++ b/cli/main.go @@ -57,6 +57,7 @@ func main() { cli.AddCommand(base, &StatsCommand{}) cli.AddCommand(base, &BuildCommand{}) cli.AddCommand(base, &CopyCommand{}) + cli.AddCommand(base, &PortCommand{}) // add generate doc command cli.AddCommand(base, &GenDocCommand{}) diff --git a/cli/port.go b/cli/port.go new file mode 100644 index 0000000000..95ed164f71 --- /dev/null +++ b/cli/port.go @@ -0,0 +1,97 @@ +package main + +import ( + "context" + "fmt" + "os" + "strings" + + "github.com/docker/go-connections/nat" + "github.com/pkg/errors" + "github.com/spf13/cobra" +) + +// portDescription is used to describe port command in detail and auto generate command doc. +var portDescription = "Return port binding information on Pouch container" + +// PortCommand is used to implement 'port' command. +type PortCommand struct { + baseCommand + container string + port string +} + +// Init initializes PortCommand command. +func (p *PortCommand) Init(c *Cli) { + p.cli = c + p.cmd = &cobra.Command{ + Use: "port CONTAINER [PRIVATE_PORT[/PROTO]]", + Short: "List port mappings or a specific mapping for the container", + Long: portDescription, + Args: cobra.RangeArgs(1, 2), + RunE: func(cmd *cobra.Command, args []string) error { + p.container = args[0] + if len(args) > 1 { + p.port = args[1] + } + return p.runPort() + }, + Example: portExample(), + } +} + +// runPort is the entry of PortCommand command. +func (p *PortCommand) runPort() error { + ctx := context.Background() + apiClient := p.cli.Client() + + c, err := apiClient.ContainerGet(ctx, p.container) + if err != nil { + return err + } + + if p.port != "" { + port := p.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 portBindings, exists := c.NetworkSettings.Ports[string(newP)]; exists && portBindings != nil { + for _, pb := range portBindings { + fmt.Fprintf(os.Stdout, "%s:%s\n", pb.HostIP, pb.HostPort) + } + return nil + } + return errors.Errorf("No public port '%s' published for %s", natPort, p.container) + } + + for from, portBindings := range c.NetworkSettings.Ports { + for _, pb := range portBindings { + fmt.Fprintf(os.Stdout, "%s -> %s:%s\n", from, pb.HostIP, pb.HostPort) + } + } + + return nil +} + +// portExample shows examples in port command, and is used in auto-generated cli docs. +func portExample() string { + return `$ pouch run -d -p 6379:6379 -p 6380:6380/udp redis:latest +179eba2c29fb27a000bcda75cb2be271d1833ab140d1133799d0d4d865abc44e +$ pouch port 179 +6379/tcp -> 0.0.0.0:6379 +6380/udp -> 0.0.0.0:6380 +$ pouch port 179 6379 +0.0.0.0:6379 +$ pouch port 179 6380/udp +0.0.0.0:6380 +` +} diff --git a/test/cli_container_cp_test.go b/test/cli_container_cp_test.go index 7796c33c98..43ea6d93bc 100644 --- a/test/cli_container_cp_test.go +++ b/test/cli_container_cp_test.go @@ -13,7 +13,7 @@ import ( "github.com/gotestyourself/gotestyourself/icmd" ) -// APIContainerCopySuite is the test suite for container cp CLI. +// PouchContainerCopySuite is the test suite for container cp CLI. type PouchContainerCopySuite struct{} func init() { diff --git a/test/cli_container_port_test.go b/test/cli_container_port_test.go new file mode 100644 index 0000000000..0a411b0501 --- /dev/null +++ b/test/cli_container_port_test.go @@ -0,0 +1,76 @@ +package main + +import ( + "strings" + + "github.com/alibaba/pouch/test/command" + "github.com/alibaba/pouch/test/environment" + + "github.com/go-check/check" + "github.com/gotestyourself/gotestyourself/icmd" +) + +// PouchContainerPortSuite is the test suite for container port CLI. +type PouchContainerPortSuite struct{} + +func init() { + check.Suite(&PouchContainerPortSuite{}) +} + +// SetUpSuite does common setup in the beginning of each test suite. +func (suite *PouchContainerPortSuite) SetUpSuite(c *check.C) { + SkipIfFalse(c, environment.IsLinux) + PullImage(c, busyboxImage) +} + +// Test pouch port +func (suite *PouchContainerPortSuite) TestPouchPort(c *check.C) { + testcase1 := "8000:8000" + testcase2 := "10000:10000/udp" + testcase3 := "127.0.0.1:5000:5000" + + name := "TestPouchPort" + command.PouchRun("run", + "--name", name, "-d", + "-p", testcase1, + "-p", testcase2, + "-p", testcase3, + busyboxImage, + "sh", "-c", "sleep 10000").Assert(c, icmd.Success) + defer DelContainerForceMultyTime(c, name) + + ret := command.PouchRun("port", + name, "5000").Assert(c, icmd.Success) + c.Assert(ret.Stdout(), check.Equals, "127.0.0.1:5000\n") + + ret = command.PouchRun("port", + name, "8000").Assert(c, icmd.Success) + c.Assert(ret.Stdout(), check.Equals, "0.0.0.0:8000\n") + + // Test for only one arg. + ret = command.PouchRun("port", + name, "10000/udp").Assert(c, icmd.Success) + c.Assert(ret.Stdout(), check.Equals, "0.0.0.0:10000\n") + + portBindingMap := map[string]string{ + "10000/udp": "0.0.0.0:10000", + "5000/tcp": "127.0.0.1:5000", + "8000/tcp": "0.0.0.0:8000", + } + ret = command.PouchRun("port", name).Assert(c, icmd.Success) + lines := strings.Split(ret.Stdout(), "\n") + for _, l := range lines { + outputs := strings.Split(l, "->") + // filter out the last line + if len(outputs) < 2 { + continue + } + port := strings.TrimSpace(outputs[0]) + actualPortBinding := strings.TrimSpace(outputs[1]) + if expected, exist := portBindingMap[port]; exist { + c.Assert(expected, check.Equals, actualPortBinding) + } else { + c.Errorf("Port %s not exists for container %s", port, name) + } + } +}