port: improve error-handling if port not found (#10039)

This method looked slightly incomplete. If the port wasn't found,
it'd return `err`, but that was always `nil`, so we'd print out
`:0`.

Now, we construct a nice error message with the targeted port and
the ones we found.

The `--protocol` flag is also now case-insensitive to prevent any
weirdness/confusion there.

Co-authored-by: Nick Sieger <nicksieger@gmail.com>
Signed-off-by: Milas Bowman <milas.bowman@docker.com>
This commit is contained in:
Milas Bowman 2022-12-05 17:11:45 -05:00 committed by GitHub
parent 6ed9a7928f
commit 053f20edab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 27 additions and 10 deletions

View File

@ -77,6 +77,7 @@ build-and-e2e-compose-standalone: build e2e-compose-standalone ## Compile the co
.PHONY: mocks .PHONY: mocks
mocks: mocks:
mockgen --version >/dev/null 2>&1 || go install github.com/golang/mock/mockgen@v1.6.0
mockgen -destination pkg/mocks/mock_docker_cli.go -package mocks github.com/docker/cli/cli/command Cli mockgen -destination pkg/mocks/mock_docker_cli.go -package mocks github.com/docker/cli/cli/command Cli
mockgen -destination pkg/mocks/mock_docker_api.go -package mocks github.com/docker/docker/client APIClient mockgen -destination pkg/mocks/mock_docker_api.go -package mocks github.com/docker/docker/client APIClient
mockgen -destination pkg/mocks/mock_docker_compose_api.go -package mocks -source=./pkg/api/api.go Service mockgen -destination pkg/mocks/mock_docker_compose_api.go -package mocks -source=./pkg/api/api.go Service

View File

@ -20,6 +20,7 @@ import (
"context" "context"
"fmt" "fmt"
"strconv" "strconv"
"strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
@ -28,7 +29,7 @@ import (
type portOptions struct { type portOptions struct {
*projectOptions *projectOptions
port int port uint16
protocol string protocol string
index int index int
} }
@ -42,11 +43,12 @@ func portCommand(p *projectOptions, backend api.Service) *cobra.Command {
Short: "Print the public port for a port binding.", Short: "Print the public port for a port binding.",
Args: cobra.MinimumNArgs(2), Args: cobra.MinimumNArgs(2),
PreRunE: Adapt(func(ctx context.Context, args []string) error { PreRunE: Adapt(func(ctx context.Context, args []string) error {
port, err := strconv.Atoi(args[1]) port, err := strconv.ParseUint(args[1], 10, 16)
if err != nil { if err != nil {
return err return err
} }
opts.port = port opts.port = uint16(port)
opts.protocol = strings.ToLower(opts.protocol)
return nil return nil
}), }),
RunE: Adapt(func(ctx context.Context, args []string) error { RunE: Adapt(func(ctx context.Context, args []string) error {

View File

@ -72,7 +72,7 @@ type Service interface {
// Events executes the equivalent to a `compose events` // Events executes the equivalent to a `compose events`
Events(ctx context.Context, projectName string, options EventsOptions) error Events(ctx context.Context, projectName string, options EventsOptions) error
// Port executes the equivalent to a `compose port` // Port executes the equivalent to a `compose port`
Port(ctx context.Context, projectName string, service string, port int, options PortOptions) (string, int, error) Port(ctx context.Context, projectName string, service string, port uint16, options PortOptions) (string, int, error)
// Images executes the equivalent of a `compose images` // Images executes the equivalent of a `compose images`
Images(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error) Images(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error)
} }

View File

@ -48,7 +48,7 @@ type ServiceProxy struct {
UnPauseFn func(ctx context.Context, project string, options PauseOptions) error UnPauseFn func(ctx context.Context, project string, options PauseOptions) error
TopFn func(ctx context.Context, projectName string, services []string) ([]ContainerProcSummary, error) TopFn func(ctx context.Context, projectName string, services []string) ([]ContainerProcSummary, error)
EventsFn func(ctx context.Context, project string, options EventsOptions) error EventsFn func(ctx context.Context, project string, options EventsOptions) error
PortFn func(ctx context.Context, project string, service string, port int, options PortOptions) (string, int, error) PortFn func(ctx context.Context, project string, service string, port uint16, options PortOptions) (string, int, error)
ImagesFn func(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error) ImagesFn func(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error)
interceptors []Interceptor interceptors []Interceptor
} }
@ -294,7 +294,7 @@ func (s *ServiceProxy) Events(ctx context.Context, projectName string, options E
} }
// Port implements Service interface // Port implements Service interface
func (s *ServiceProxy) Port(ctx context.Context, projectName string, service string, port int, options PortOptions) (string, int, error) { func (s *ServiceProxy) Port(ctx context.Context, projectName string, service string, port uint16, options PortOptions) (string, int, error) {
if s.PortFn == nil { if s.PortFn == nil {
return "", 0, ErrNotImplemented return "", 0, ErrNotImplemented
} }

View File

@ -27,7 +27,7 @@ import (
"github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/filters"
) )
func (s *composeService) Port(ctx context.Context, projectName string, service string, port int, options api.PortOptions) (string, int, error) { func (s *composeService) Port(ctx context.Context, projectName string, service string, port uint16, options api.PortOptions) (string, int, error) {
projectName = strings.ToLower(projectName) projectName = strings.ToLower(projectName)
list, err := s.apiClient().ContainerList(ctx, moby.ContainerListOptions{ list, err := s.apiClient().ContainerList(ctx, moby.ContainerListOptions{
Filters: filters.NewArgs( Filters: filters.NewArgs(
@ -44,9 +44,23 @@ func (s *composeService) Port(ctx context.Context, projectName string, service s
} }
container := list[0] container := list[0]
for _, p := range container.Ports { for _, p := range container.Ports {
if p.PrivatePort == uint16(port) && p.Type == options.Protocol { if p.PrivatePort == port && p.Type == options.Protocol {
return p.IP, int(p.PublicPort), nil return p.IP, int(p.PublicPort), nil
} }
} }
return "", 0, err return "", 0, portNotFoundError(options.Protocol, port, container)
}
func portNotFoundError(protocol string, port uint16, ctr moby.Container) error {
formatPort := func(protocol string, port uint16) string {
return fmt.Sprintf("%d/%s", port, protocol)
}
var containerPorts []string
for _, p := range ctr.Ports {
containerPorts = append(containerPorts, formatPort(p.Type, p.PublicPort))
}
name := strings.TrimPrefix(ctr.Names[0], "/")
return fmt.Errorf("no port %s for container %s: %s", formatPort(protocol, port), name, strings.Join(containerPorts, ", "))
} }

View File

@ -209,7 +209,7 @@ func (mr *MockServiceMockRecorder) Pause(ctx, projectName, options interface{})
} }
// Port mocks base method. // Port mocks base method.
func (m *MockService) Port(ctx context.Context, projectName, service string, port int, options api.PortOptions) (string, int, error) { func (m *MockService) Port(ctx context.Context, projectName, service string, port uint16, options api.PortOptions) (string, int, error) {
m.ctrl.T.Helper() m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "Port", ctx, projectName, service, port, options) ret := m.ctrl.Call(m, "Port", ctx, projectName, service, port, options)
ret0, _ := ret[0].(string) ret0, _ := ret[0].(string)