Merge pull request #1438 from docker/port

introduce `port` command for parity with docker-compose
This commit is contained in:
Guillaume Tardif 2021-03-19 15:08:09 +01:00 коммит произвёл GitHub
Родитель 2bf3e9d1de 8b38874aba
Коммит a86a4737da
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 167 добавлений и 12 удалений

Просмотреть файл

@ -237,3 +237,7 @@ func (cs *aciComposeService) Top(ctx context.Context, projectName string, servic
func (cs *aciComposeService) Events(ctx context.Context, project string, options compose.EventsOptions) error {
return errdefs.ErrNotImplemented
}
func (cs *aciComposeService) Port(ctx context.Context, project string, service string, port int, options compose.PortOptions) (string, int, error) {
return "", 0, errdefs.ErrNotImplemented
}

Просмотреть файл

@ -111,3 +111,7 @@ func (c *composeService) Top(ctx context.Context, projectName string, services [
func (c *composeService) Events(ctx context.Context, project string, options compose.EventsOptions) error {
return errdefs.ErrNotImplemented
}
func (c *composeService) Port(ctx context.Context, project string, service string, port int, options compose.PortOptions) (string, int, error) {
return "", 0, errdefs.ErrNotImplemented
}

Просмотреть файл

@ -70,6 +70,8 @@ type Service interface {
Top(ctx context.Context, projectName string, services []string) ([]ContainerProcSummary, error)
// Events executes the equivalent to a `compose events`
Events(ctx context.Context, project string, options EventsOptions) error
// Port executes the equivalent to a `compose port`
Port(ctx context.Context, project string, service string, port int, options PortOptions) (string, int, error)
}
// BuildOptions group options of the Build API
@ -207,6 +209,12 @@ type Event struct {
Attributes map[string]string
}
// PortOptions group options of the Port API
type PortOptions struct {
Protocol string
Index int
}
func (e Event) String() string {
t := e.Timestamp.Format("2006-01-02 15:04:05.000000")
var attr []string

Просмотреть файл

@ -142,6 +142,7 @@ func Command(contextType string) *cobra.Command {
unpauseCommand(&opts),
topCommand(&opts),
eventsCommand(&opts),
portCommand(&opts),
)
if contextType == store.LocalContextType || contextType == store.DefaultContextType {

77
cli/cmd/compose/port.go Normal file
Просмотреть файл

@ -0,0 +1,77 @@
/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose
import (
"context"
"fmt"
"strconv"
"github.com/spf13/cobra"
"github.com/docker/compose-cli/api/client"
"github.com/docker/compose-cli/api/compose"
)
type portOptions struct {
*projectOptions
protocol string
index int
}
func portCommand(p *projectOptions) *cobra.Command {
opts := portOptions{
projectOptions: p,
}
cmd := &cobra.Command{
Use: "port [options] [--] SERVICE PRIVATE_PORT",
Short: "Print the public port for a port binding.",
Args: cobra.MinimumNArgs(2),
RunE: func(cmd *cobra.Command, args []string) error {
port, err := strconv.Atoi(args[1])
if err != nil {
return err
}
return runPort(cmd.Context(), opts, args[0], port)
},
}
cmd.Flags().StringVar(&opts.protocol, "protocol", "tcp", "tcp or udp")
cmd.Flags().IntVar(&opts.index, "index", 1, "index of the container if service has multiple replicas")
return cmd
}
func runPort(ctx context.Context, opts portOptions, service string, port int) error {
c, err := client.New(ctx)
if err != nil {
return err
}
projectName, err := opts.toProjectName()
if err != nil {
return err
}
ip, port, err := c.ComposeService().Port(ctx, projectName, service, port, compose.PortOptions{
Protocol: opts.protocol,
Index: opts.index,
})
if err != nil {
return err
}
fmt.Printf("%s:%d\n", ip, port)
return nil
}

Просмотреть файл

@ -29,6 +29,7 @@ import (
"github.com/docker/compose-cli/api/client"
"github.com/docker/compose-cli/api/compose"
"github.com/docker/compose-cli/cli/formatter"
"github.com/docker/compose-cli/utils"
)
type psOptions struct {
@ -77,7 +78,7 @@ func runPs(ctx context.Context, opts psOptions) error {
if opts.Services {
services := []string{}
for _, s := range containers {
if !contains(services, s.Service) {
if !utils.StringContains(services, s.Service) {
services = append(services, s.Service)
}
}
@ -115,12 +116,3 @@ func runPs(ctx context.Context, opts psOptions) error {
},
"NAME", "SERVICE", "STATUS", "PORTS")
}
func contains(slice []string, item string) bool {
for _, v := range slice {
if v == item {
return true
}
}
return false
}

Просмотреть файл

@ -26,6 +26,7 @@ import (
"github.com/docker/compose-cli/api/client"
"github.com/docker/compose-cli/api/progress"
"github.com/docker/compose-cli/utils"
)
type pullOptions struct {
@ -78,7 +79,7 @@ func runPull(ctx context.Context, opts pullOptions, services []string) error {
return err
}
for _, s := range project.Services {
if !contains(services, s.Name) {
if !utils.StringContains(services, s.Name) {
project.DisabledServices = append(project.DisabledServices, s)
}
}

Просмотреть файл

@ -28,6 +28,7 @@ import (
"time"
"github.com/compose-spec/compose-go/types"
"github.com/docker/compose-cli/utils"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"golang.org/x/sync/errgroup"
@ -106,7 +107,7 @@ func (opts upOptions) apply(project *types.Project, services []string) error {
return err
}
for _, s := range project.Services {
if !contains(services, s.Name) {
if !utils.StringContains(services, s.Name) {
project.DisabledServices = append(project.DisabledServices, s)
}
}

Просмотреть файл

@ -203,3 +203,7 @@ func (e ecsLocalSimulation) Top(ctx context.Context, projectName string, service
func (e ecsLocalSimulation) Events(ctx context.Context, project string, options compose.EventsOptions) error {
return e.compose.Events(ctx, project, options)
}
func (e ecsLocalSimulation) Port(ctx context.Context, project string, service string, port int, options compose.PortOptions) (string, int, error) {
return "", 0, errdefs.ErrNotImplemented
}

Просмотреть файл

@ -71,6 +71,10 @@ func (b *ecsAPIService) Events(ctx context.Context, project string, options comp
return errdefs.ErrNotImplemented
}
func (b *ecsAPIService) Port(ctx context.Context, project string, service string, port int, options compose.PortOptions) (string, int, error) {
return "", 0, errdefs.ErrNotImplemented
}
func (b *ecsAPIService) Up(ctx context.Context, project *types.Project, options compose.UpOptions) error {
logrus.Debugf("deploying on AWS with region=%q", b.Region)
err := b.aws.CheckRequirements(ctx, b.Region)

Просмотреть файл

@ -267,3 +267,7 @@ func (s *composeService) Top(ctx context.Context, projectName string, services [
func (s *composeService) Events(ctx context.Context, project string, options compose.EventsOptions) error {
return errdefs.ErrNotImplemented
}
func (s *composeService) Port(ctx context.Context, project string, service string, port int, options compose.PortOptions) (string, int, error) {
return "", 0, errdefs.ErrNotImplemented
}

50
local/compose/port.go Normal file
Просмотреть файл

@ -0,0 +1,50 @@
/*
Copyright 2020 Docker Compose CLI authors
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package compose
import (
"context"
"fmt"
"github.com/docker/compose-cli/api/compose"
moby "github.com/docker/docker/api/types"
"github.com/docker/docker/api/types/filters"
)
func (s *composeService) Port(ctx context.Context, project string, service string, port int, options compose.PortOptions) (string, int, error) {
list, err := s.apiClient.ContainerList(ctx, moby.ContainerListOptions{
Filters: filters.NewArgs(
projectFilter(project),
serviceFilter(service),
filters.Arg("label", fmt.Sprintf("%s=%d", containerNumberLabel, options.Index)),
),
})
if err != nil {
return "", 0, err
}
if len(list) == 0 {
return "", 0, fmt.Errorf("no container found for %s_%d", service, options.Index)
}
container := list[0]
for _, p := range container.Ports {
if p.PrivatePort == uint16(port) && p.Type == options.Protocol {
return p.IP, int(p.PublicPort), nil
}
}
return "", 0, err
}

Просмотреть файл

@ -55,6 +55,11 @@ func TestNetworks(t *testing.T) {
res.Assert(t, icmd.Expected{Out: "microservices"})
})
t.Run("port", func(t *testing.T) {
res := c.RunDockerCmd("compose", "--project-name", projectName, "port", "words", "8080")
res.Assert(t, icmd.Expected{Out: `0.0.0.0:8080`})
})
t.Run("down", func(t *testing.T) {
_ = c.RunDockerCmd("compose", "--project-name", projectName, "down")
})