From 52f7902d40eb82f13f2eaabef24f1342332678de Mon Sep 17 00:00:00 2001 From: Djordje Lukic Date: Thu, 14 May 2020 12:17:06 +0200 Subject: [PATCH] Port parsing on the comand line --- Dockerfile | 1 + azure/backend.go | 4 +- cli/cmd/run/opts.go | 45 ++++++++++-------- cli/cmd/run/opts_test.go | 99 ++++++++++++++++++++++++++++++++++++++++ cli/cmd/run/run.go | 2 +- containers/api.go | 13 ++++-- go.mod | 1 + moby/backend.go | 15 ++++++ 8 files changed, 154 insertions(+), 26 deletions(-) create mode 100644 cli/cmd/run/opts_test.go diff --git a/Dockerfile b/Dockerfile index 39eca34b1..c3246033e 100644 --- a/Dockerfile +++ b/Dockerfile @@ -9,6 +9,7 @@ ENV GO111MODULE=on WORKDIR ${PWD} ADD go.* ${PWD} +RUN go mod download ADD . ${PWD} FROM golang:${GO_VERSION} AS protos-base diff --git a/azure/backend.go b/azure/backend.go index f1fedddff..bf1b4f741 100644 --- a/azure/backend.go +++ b/azure/backend.go @@ -141,8 +141,8 @@ func (cs *aciContainerService) Run(ctx context.Context, r containers.ContainerCo var ports []types.ServicePortConfig for _, p := range r.Ports { ports = append(ports, types.ServicePortConfig{ - Target: p.Destination, - Published: p.Source, + Target: p.ContainerPort, + Published: p.HostPort, }) } project := compose.Project{ diff --git a/cli/cmd/run/opts.go b/cli/cmd/run/opts.go index d8c84233f..7c05edccb 100644 --- a/cli/cmd/run/opts.go +++ b/cli/cmd/run/opts.go @@ -1,9 +1,9 @@ package run import ( - "fmt" "strconv" - "strings" + + "github.com/docker/go-connections/nat" "github.com/docker/api/containers" ) @@ -14,27 +14,34 @@ type runOpts struct { } func toPorts(ports []string) ([]containers.Port, error) { + _, bindings, err := nat.ParsePortSpecs(ports) + if err != nil { + return nil, err + } var result []containers.Port - for _, port := range ports { - parts := strings.Split(port, ":") - if len(parts) != 2 { - return nil, fmt.Errorf("unable to parse ports %q", port) - } - source, err := strconv.Atoi(parts[0]) - if err != nil { - return nil, err - } - destination, err := strconv.Atoi(parts[1]) - if err != nil { - return nil, err - } + for port, bind := range bindings { + for _, portbind := range bind { + var hostPort uint32 + if portbind.HostPort != "" { + hp, err := strconv.Atoi(portbind.HostPort) + if err != nil { + return nil, err + } + hostPort = uint32(hp) + } else { + hostPort = uint32(port.Int()) + } - result = append(result, containers.Port{ - Source: uint32(source), - Destination: uint32(destination), - }) + result = append(result, containers.Port{ + HostPort: hostPort, + ContainerPort: uint32(port.Int()), + Protocol: port.Proto(), + HostIP: portbind.HostIP, + }) + } } + return result, nil } diff --git a/cli/cmd/run/opts_test.go b/cli/cmd/run/opts_test.go new file mode 100644 index 000000000..df1cd69f7 --- /dev/null +++ b/cli/cmd/run/opts_test.go @@ -0,0 +1,99 @@ +package run + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "github.com/stretchr/testify/suite" + + "github.com/docker/api/containers" +) + +type RunOptsSuite struct { + suite.Suite +} + +func (s *RunOptsSuite) TestPortParse() { + testCases := []struct { + in string + expected []containers.Port + }{ + { + in: "80", + expected: []containers.Port{ + { + HostPort: 80, + ContainerPort: 80, + Protocol: "tcp", + }, + }, + }, + { + in: "80:80", + expected: []containers.Port{ + { + HostPort: 80, + ContainerPort: 80, + Protocol: "tcp", + }, + }, + }, + { + in: "80:80/udp", + expected: []containers.Port{ + { + ContainerPort: 80, + HostPort: 80, + Protocol: "udp", + }, + }, + }, + { + in: "8080:80", + expected: []containers.Port{ + { + HostPort: 8080, + ContainerPort: 80, + Protocol: "tcp", + }, + }, + }, + { + in: "192.168.0.2:8080:80", + expected: []containers.Port{ + { + HostPort: 8080, + ContainerPort: 80, + Protocol: "tcp", + HostIP: "192.168.0.2", + }, + }, + }, + { + in: "80-81:80-81", + expected: []containers.Port{ + { + HostPort: 80, + ContainerPort: 80, + Protocol: "tcp", + }, + { + HostPort: 81, + ContainerPort: 81, + Protocol: "tcp", + }, + }, + }, + } + + for _, testCase := range testCases { + result, err := toPorts([]string{testCase.in}) + require.Nil(s.T(), err) + assert.ElementsMatch(s.T(), testCase.expected, result) + } +} + +func TestExampleTestSuite(t *testing.T) { + suite.Run(t, new(RunOptsSuite)) +} diff --git a/cli/cmd/run/run.go b/cli/cmd/run/run.go index 656e774a0..e34615912 100644 --- a/cli/cmd/run/run.go +++ b/cli/cmd/run/run.go @@ -50,7 +50,7 @@ func Command() *cobra.Command { }, } - cmd.Flags().StringArrayVarP(&opts.publish, "publish", "p", []string{}, "Publish a container's port(s)") + cmd.Flags().StringArrayVarP(&opts.publish, "publish", "p", []string{}, "Publish a container's port(s). [HOST_PORT:]CONTAINER_PORT") cmd.Flags().StringVar(&opts.name, "name", getRandomName(), "Assign a name to the container") return cmd diff --git a/containers/api.go b/containers/api.go index 77d9aaead..feaf84f81 100644 --- a/containers/api.go +++ b/containers/api.go @@ -17,14 +17,19 @@ type Container struct { PidsCurrent uint64 PidsLimit uint64 Labels []string + Ports []Port } // Port represents a published port of a container type Port struct { - // Source is the source port - Source uint32 - // Destination is the destination port - Destination uint32 + // HostPort is the port number on the host + HostPort uint32 + // ContainerPort is the port number inside the container + ContainerPort uint32 + /// Protocol is the protocol of the port mapping + Protocol string + // HostIP is the host ip to use + HostIP string } // ContainerConfig contains the configuration data about a container diff --git a/go.mod b/go.mod index be5addf88..7fc45e53e 100644 --- a/go.mod +++ b/go.mod @@ -16,6 +16,7 @@ require ( github.com/containerd/containerd v1.3.4 // indirect github.com/docker/distribution v2.7.1+incompatible // indirect github.com/docker/docker v17.12.0-ce-rc1.0.20200309214505-aa6a9891b09c+incompatible + github.com/docker/go-connections v0.4.0 github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee // indirect github.com/gobwas/pool v0.2.0 // indirect github.com/gobwas/ws v1.0.3 diff --git a/moby/backend.go b/moby/backend.go index b9c1304cd..67194ec47 100644 --- a/moby/backend.go +++ b/moby/backend.go @@ -60,6 +60,7 @@ func (ms *mobyService) List(ctx context.Context) ([]containers.Container, error) Image: container.Image, Status: container.Status, Command: container.Command, + Ports: getPorts(container.Ports), }) } @@ -137,3 +138,17 @@ func (ms *mobyService) Delete(ctx context.Context, containerID string, force boo } return err } + +func getPorts(ports []types.Port) []containers.Port { + result := []containers.Port{} + for _, port := range ports { + result = append(result, containers.Port{ + ContainerPort: uint32(port.PrivatePort), + HostPort: uint32(port.PublicPort), + HostIP: port.IP, + Protocol: port.Type, + }) + } + + return result +}