Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 14 additions & 5 deletions cmd/compose/port.go
Original file line number Diff line number Diff line change
Expand Up @@ -40,10 +40,15 @@ func portCommand(p *ProjectOptions, dockerCli command.Cli, backend api.Service)
ProjectOptions: p,
}
cmd := &cobra.Command{
Use: "port [OPTIONS] SERVICE PRIVATE_PORT",
Short: "Print the public port for a port binding",
Args: cobra.MinimumNArgs(2),
Use: "port [OPTIONS] SERVICE [PRIVATE_PORT]",
Short: "List port mappings or print the public port of a specific mapping for the service",
Args: cobra.RangeArgs(1, 2),
PreRunE: Adapt(func(ctx context.Context, args []string) error {
if len(args) == 1 {
opts.port = 0
return nil
}

port, err := strconv.ParseUint(args[1], 10, 16)
if err != nil {
return err
Expand All @@ -67,14 +72,18 @@ func runPort(ctx context.Context, dockerCli command.Cli, backend api.Service, op
if err != nil {
return err
}
ip, port, err := backend.Port(ctx, projectName, service, opts.port, api.PortOptions{

ports, err := backend.Port(ctx, projectName, service, opts.port, api.PortOptions{
Protocol: opts.protocol,
Index: opts.index,
})
if err != nil {
return err
}

fmt.Fprintf(dockerCli.Out(), "%s:%d\n", ip, port)
for _, port := range ports {
fmt.Fprintf(dockerCli.Out(), "%s\n", port.String())
}

return nil
}
4 changes: 2 additions & 2 deletions cmd/formatter/container.go
Original file line number Diff line number Diff line change
Expand Up @@ -216,8 +216,8 @@ func (c *ContainerContext) Ports() string {
for _, publisher := range c.c.Publishers {
ports = append(ports, types.Port{
IP: publisher.URL,
PrivatePort: uint16(publisher.TargetPort),
PublicPort: uint16(publisher.PublishedPort),
PrivatePort: publisher.TargetPort,
PublicPort: publisher.PublishedPort,
Type: publisher.Protocol,
})
}
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/compose.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ Define and run multi-container applications with Docker
| [`logs`](compose_logs.md) | View output from containers |
| [`ls`](compose_ls.md) | List running compose projects |
| [`pause`](compose_pause.md) | Pause services |
| [`port`](compose_port.md) | Print the public port for a port binding |
| [`port`](compose_port.md) | List port mappings or print the public port of a specific mapping for the service |
| [`ps`](compose_ps.md) | List containers |
| [`pull`](compose_pull.md) | Pull service images |
| [`push`](compose_push.md) | Push service images |
Expand Down
2 changes: 1 addition & 1 deletion docs/reference/compose_port.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# docker compose port

<!---MARKER_GEN_START-->
Print the public port for a port binding
List port mappings or print the public port of a specific mapping for the service

### Options

Expand Down
5 changes: 3 additions & 2 deletions docs/reference/docker_compose_port.yaml
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
command: docker compose port
short: Print the public port for a port binding
short: |
List port mappings or print the public port of a specific mapping for the service
long: Prints the public port for a port binding
usage: docker compose port [OPTIONS] SERVICE PRIVATE_PORT
usage: docker compose port [OPTIONS] SERVICE [PRIVATE_PORT]
pname: docker compose
plink: docker_compose.yaml
options:
Expand Down
12 changes: 9 additions & 3 deletions pkg/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package api
import (
"context"
"fmt"
"net"
"strconv"
"strings"
"time"

Expand Down Expand Up @@ -73,7 +75,7 @@ type Service interface {
// Events executes the equivalent to a `compose events`
Events(ctx context.Context, projectName string, options EventsOptions) error
// Port executes the equivalent to a `compose port`
Port(ctx context.Context, projectName string, service string, port uint16, options PortOptions) (string, int, error)
Port(ctx context.Context, projectName string, service string, port uint16, options PortOptions) (PortPublishers, error)
// Publish executes the equivalent to a `compose publish`
Publish(ctx context.Context, project *types.Project, repository string, options PublishOptions) error
// Images executes the equivalent of a `compose images`
Expand Down Expand Up @@ -448,8 +450,8 @@ type CopyOptions struct {
// PortPublisher hold status about published port
type PortPublisher struct {
URL string
TargetPort int
PublishedPort int
TargetPort uint16
PublishedPort uint16
Protocol string
}

Expand All @@ -476,6 +478,10 @@ type ContainerSummary struct {
LocalVolumes int
}

func (p *PortPublisher) String() string {
return fmt.Sprintf("%d/%s -> %s", p.TargetPort, p.Protocol, net.JoinHostPort(p.URL, strconv.Itoa(int(p.PublishedPort))))
}

// PortPublishers is a slice of PortPublisher
type PortPublishers []PortPublisher

Expand Down
33 changes: 33 additions & 0 deletions pkg/api/api_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,36 @@ func TestRunOptionsEnvironmentMap(t *testing.T) {
assert.Equal(t, *env["ZOT"], "")
assert.Check(t, env["QIX"] == nil)
}

func TestPortPublisherString(t *testing.T) {
tests := map[string]struct {
pub PortPublisher
expect string
}{
"ipv6_udp": {
pub: PortPublisher{
Protocol: "udp",
PublishedPort: 32769,
TargetPort: 5060,
URL: "::",
},
expect: "5060/udp -> [::]:32769",
},
"ipv4_tcp": {
pub: PortPublisher{
Protocol: "tcp",
PublishedPort: 5060,
TargetPort: 5060,
URL: "0.0.0.0",
},
expect: "5060/tcp -> 0.0.0.0:5060",
},
}

for name, tt := range tests {
t.Run(name, func(t *testing.T) {
got := tt.pub.String()
assert.Equal(t, tt.expect, got)
})
}
}
34 changes: 28 additions & 6 deletions pkg/compose/port.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,40 @@ import (
moby "github.com/docker/docker/api/types"
)

func (s *composeService) Port(ctx context.Context, projectName string, service string, port uint16, options api.PortOptions) (string, int, error) {
func (s *composeService) Port(ctx context.Context, projectName string, service string, port uint16, options api.PortOptions) (api.PortPublishers, error) {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as signature changes, and breaks compatibility, makes sense to also rename Ports(plural)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense to denote information about more than one port can be returned. However wouldn't it be weird since it breaks the pattern of backend methods having the same name as their CLI command? Also if we were to rename the method, I think we should also rename the file to ports.go?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@ndeloof Sorry to bother but do you have any thoughts on my comments? Should we still perform the renaming?

projectName = strings.ToLower(projectName)
container, err := s.getSpecifiedContainer(ctx, projectName, oneOffInclude, false, service, options.Index)
if err != nil {
return "", 0, err
return nil, err
}
for _, p := range container.Ports {
if p.PrivatePort == port && p.Type == options.Protocol {
return p.IP, int(p.PublicPort), nil

if port != 0 {
for _, p := range container.Ports {
if p.PrivatePort == port && p.Type == options.Protocol {
return api.PortPublishers{
api.PortPublisher{
URL: p.IP,
TargetPort: p.PrivatePort,
PublishedPort: p.PublicPort,
Protocol: p.Type,
},
}, nil
}
}
return nil, portNotFoundError(options.Protocol, port, container)
}
return "", 0, portNotFoundError(options.Protocol, port, container)

res := make(api.PortPublishers, len(container.Ports))
for idx, p := range container.Ports {
res[idx] = api.PortPublisher{
URL: p.IP,
TargetPort: p.PrivatePort,
PublishedPort: p.PublicPort,
Protocol: p.Type,
}
}

return res, nil
}

func portNotFoundError(protocol string, port uint16, ctr moby.Container) error {
Expand Down
4 changes: 2 additions & 2 deletions pkg/compose/ps.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,8 @@ func (s *composeService) Ps(ctx context.Context, projectName string, options api
for i, p := range container.Ports {
publishers[i] = api.PortPublisher{
URL: p.IP,
TargetPort: int(p.PrivatePort),
PublishedPort: int(p.PublicPort),
TargetPort: p.PrivatePort,
PublishedPort: p.PublicPort,
Protocol: p.Type,
}
}
Expand Down
9 changes: 4 additions & 5 deletions pkg/mocks/mock_docker_compose_api.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.