mirror of
https://github.com/docker/compose.git
synced 2026-05-13 13:58:02 +00:00
PRIVATE_PORT optional for docker compose port
Signed-off-by: Max Proske <max@mproske.com>
This commit is contained in:
parent
f9828dfab9
commit
c350344f46
8 changed files with 93 additions and 34 deletions
|
|
@ -19,6 +19,8 @@ package compose
|
|||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
|
|
@ -41,16 +43,20 @@ func portCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Backe
|
|||
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 {
|
||||
port, err := strconv.ParseUint(args[1], 10, 16)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opts.port = uint16(port)
|
||||
opts.protocol = strings.ToLower(opts.protocol)
|
||||
if len(args) > 1 {
|
||||
port, err := strconv.ParseUint(args[1], 10, 16)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opts.port = uint16(port)
|
||||
} else {
|
||||
opts.protocol = ""
|
||||
}
|
||||
return nil
|
||||
}),
|
||||
RunE: Adapt(func(ctx context.Context, args []string) error {
|
||||
|
|
@ -73,7 +79,7 @@ func runPort(ctx context.Context, dockerCli command.Cli, backendOptions *Backend
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
ip, port, err := backend.Port(ctx, projectName, service, opts.port, api.PortOptions{
|
||||
publishers, err := backend.Ports(ctx, projectName, service, opts.port, api.PortOptions{
|
||||
Protocol: opts.protocol,
|
||||
Index: opts.index,
|
||||
})
|
||||
|
|
@ -81,6 +87,14 @@ func runPort(ctx context.Context, dockerCli command.Cli, backendOptions *Backend
|
|||
return err
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(dockerCli.Out(), "%s:%d\n", ip, port)
|
||||
if opts.port != 0 && len(publishers) > 0 {
|
||||
p := publishers[0]
|
||||
_, _ = fmt.Fprintf(dockerCli.Out(), "%s\n", net.JoinHostPort(p.URL, strconv.Itoa(p.PublishedPort)))
|
||||
return nil
|
||||
}
|
||||
sort.Sort(publishers)
|
||||
for _, p := range publishers {
|
||||
_, _ = fmt.Fprintln(dockerCli.Out(), p.String())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -28,7 +28,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 |
|
||||
| [`publish`](compose_publish.md) | Publish compose application |
|
||||
| [`pull`](compose_pull.md) | Pull service images |
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
# docker compose port
|
||||
|
||||
<!---MARKER_GEN_START-->
|
||||
Prints the public port for a port binding
|
||||
List port mappings or print the public port of a specific mapping for the service
|
||||
|
||||
### Options
|
||||
|
||||
|
|
@ -16,4 +16,4 @@ Prints the public port for a port binding
|
|||
|
||||
## Description
|
||||
|
||||
Prints the public port for a port binding
|
||||
List port mappings or print the public port of a specific mapping for the service
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
command: docker compose port
|
||||
short: Print the public port for a port binding
|
||||
long: Prints the public port for a port binding
|
||||
usage: docker compose port [OPTIONS] SERVICE PRIVATE_PORT
|
||||
short: |
|
||||
List port mappings or print the public port of a specific mapping for the service
|
||||
long: |
|
||||
List port mappings or print the public port of a specific mapping for the service
|
||||
usage: docker compose port [OPTIONS] SERVICE [PRIVATE_PORT]
|
||||
pname: docker compose
|
||||
plink: docker_compose.yaml
|
||||
options:
|
||||
|
|
|
|||
|
|
@ -20,7 +20,9 @@ import (
|
|||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"slices"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
|
@ -125,8 +127,8 @@ type Compose interface {
|
|||
Top(ctx context.Context, projectName string, services []string) ([]ContainerProcSummary, error)
|
||||
// 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)
|
||||
// Ports executes the equivalent to a `compose port`
|
||||
Ports(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`
|
||||
|
|
@ -535,6 +537,10 @@ type PortPublisher struct {
|
|||
Protocol string
|
||||
}
|
||||
|
||||
func (p PortPublisher) String() string {
|
||||
return fmt.Sprintf("%d/%s -> %s", p.TargetPort, p.Protocol, net.JoinHostPort(p.URL, strconv.Itoa(p.PublishedPort)))
|
||||
}
|
||||
|
||||
// ContainerSummary hold high-level description of a container
|
||||
type ContainerSummary struct {
|
||||
ID string
|
||||
|
|
|
|||
|
|
@ -23,6 +23,22 @@ import (
|
|||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestPortPublisherString(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
pub PortPublisher
|
||||
want string
|
||||
}{
|
||||
{"ipv4", PortPublisher{URL: "0.0.0.0", TargetPort: 80, PublishedPort: 8080, Protocol: "tcp"}, "80/tcp -> 0.0.0.0:8080"},
|
||||
{"ipv6", PortPublisher{URL: "::", TargetPort: 5060, PublishedPort: 32769, Protocol: "udp"}, "5060/udp -> [::]:32769"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
assert.Equal(t, tt.pub.String(), tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRunOptionsEnvironmentMap(t *testing.T) {
|
||||
opts := RunOptions{
|
||||
Environment: []string{
|
||||
|
|
|
|||
|
|
@ -26,18 +26,40 @@ import (
|
|||
"github.com/docker/compose/v5/pkg/api"
|
||||
)
|
||||
|
||||
func (s *composeService) Port(ctx context.Context, projectName string, service string, port uint16, options api.PortOptions) (string, int, error) {
|
||||
func (s *composeService) Ports(ctx context.Context, projectName string, service string, port uint16, options api.PortOptions) (api.PortPublishers, error) {
|
||||
projectName = strings.ToLower(projectName)
|
||||
ctr, err := s.getSpecifiedContainer(ctx, projectName, oneOffInclude, false, service, options.Index)
|
||||
if err != nil {
|
||||
return "", 0, err
|
||||
return nil, err
|
||||
}
|
||||
for _, p := range ctr.Ports {
|
||||
if p.PrivatePort == port && p.Type == options.Protocol {
|
||||
return p.IP.String(), int(p.PublicPort), nil
|
||||
|
||||
if port != 0 {
|
||||
for _, p := range ctr.Ports {
|
||||
if p.PrivatePort == port && p.Type == options.Protocol {
|
||||
return api.PortPublishers{{
|
||||
URL: p.IP.String(),
|
||||
TargetPort: int(p.PrivatePort),
|
||||
PublishedPort: int(p.PublicPort),
|
||||
Protocol: p.Type,
|
||||
}}, nil
|
||||
}
|
||||
}
|
||||
return nil, portNotFoundError(options.Protocol, port, ctr)
|
||||
}
|
||||
return "", 0, portNotFoundError(options.Protocol, port, ctr)
|
||||
|
||||
var publishers api.PortPublishers
|
||||
for _, p := range ctr.Ports {
|
||||
if options.Protocol != "" && p.Type != options.Protocol {
|
||||
continue
|
||||
}
|
||||
publishers = append(publishers, api.PortPublisher{
|
||||
URL: p.IP.String(),
|
||||
TargetPort: int(p.PrivatePort),
|
||||
PublishedPort: int(p.PublicPort),
|
||||
Protocol: p.Type,
|
||||
})
|
||||
}
|
||||
return publishers, nil
|
||||
}
|
||||
|
||||
func portNotFoundError(protocol string, port uint16, ctr container.Summary) error {
|
||||
|
|
|
|||
|
|
@ -270,20 +270,19 @@ func (mr *MockComposeMockRecorder) Pause(ctx, projectName, options any) *gomock.
|
|||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Pause", reflect.TypeOf((*MockCompose)(nil).Pause), ctx, projectName, options)
|
||||
}
|
||||
|
||||
// Port mocks base method.
|
||||
func (m *MockCompose) Port(ctx context.Context, projectName, service string, port uint16, options api.PortOptions) (string, int, error) {
|
||||
// Ports mocks base method.
|
||||
func (m *MockCompose) Ports(ctx context.Context, projectName, service string, port uint16, options api.PortOptions) (api.PortPublishers, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "Port", ctx, projectName, service, port, options)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(int)
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
ret := m.ctrl.Call(m, "Ports", ctx, projectName, service, port, options)
|
||||
ret0, _ := ret[0].(api.PortPublishers)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// Port indicates an expected call of Port.
|
||||
func (mr *MockComposeMockRecorder) Port(ctx, projectName, service, port, options any) *gomock.Call {
|
||||
// Ports indicates an expected call of Ports.
|
||||
func (mr *MockComposeMockRecorder) Ports(ctx, projectName, service, port, options any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Port", reflect.TypeOf((*MockCompose)(nil).Port), ctx, projectName, service, port, options)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ports", reflect.TypeOf((*MockCompose)(nil).Ports), ctx, projectName, service, port, options)
|
||||
}
|
||||
|
||||
// Ps mocks base method.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue