Skip to content

Commit

Permalink
feature/add-port-cmd
Browse files Browse the repository at this point in the history
Signed-off-by: yeya24 <yb532204897@gmail.com>

fix example

Signed-off-by: yeya24 <yb532204897@gmail.com>

add one arg example to test

Signed-off-by: yeya24 <yb532204897@gmail.com>
  • Loading branch information
yeya24 committed Jun 6, 2019
1 parent 9653c63 commit 29d3f13
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 1 deletion.
1 change: 1 addition & 0 deletions cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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{})
Expand Down
97 changes: 97 additions & 0 deletions cli/port.go
Original file line number Diff line number Diff line change
@@ -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
`
}
2 changes: 1 addition & 1 deletion test/cli_container_cp_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
76 changes: 76 additions & 0 deletions test/cli_container_port_test.go
Original file line number Diff line number Diff line change
@@ -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)
}
}
}

0 comments on commit 29d3f13

Please sign in to comment.