From 3599fc853346779910d68c52d9d3ca1b4cc25637 Mon Sep 17 00:00:00 2001 From: Nick Sieger Date: Tue, 7 Jun 2022 17:09:06 -0500 Subject: [PATCH 1/2] mocks: create mocks for compose api.Service Signed-off-by: Nick Sieger --- Makefile | 1 + pkg/mocks/mock_docker_compose_api.go | 441 +++++++++++++++++++++++++++ 2 files changed, 442 insertions(+) create mode 100644 pkg/mocks/mock_docker_compose_api.go diff --git a/Makefile b/Makefile index 836f3b178..0b36672c6 100644 --- a/Makefile +++ b/Makefile @@ -55,6 +55,7 @@ e2e-compose-standalone: ## Run End to end local tests in standalone mode. Set E2 mocks: 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_compose_api.go -package mocks -source=./pkg/api/api.go Service .PHONY: e2e e2e: e2e-compose e2e-compose-standalone ## Run end to end local tests in both modes. Set E2E_TEST=TestName to run a single test diff --git a/pkg/mocks/mock_docker_compose_api.go b/pkg/mocks/mock_docker_compose_api.go new file mode 100644 index 000000000..c33c1140b --- /dev/null +++ b/pkg/mocks/mock_docker_compose_api.go @@ -0,0 +1,441 @@ +// Code generated by MockGen. DO NOT EDIT. +// Source: ./pkg/api/api.go + +// Package mocks is a generated GoMock package. +package mocks + +import ( + context "context" + reflect "reflect" + + types "github.com/compose-spec/compose-go/types" + api "github.com/docker/compose/v2/pkg/api" + gomock "github.com/golang/mock/gomock" +) + +// MockService is a mock of Service interface. +type MockService struct { + ctrl *gomock.Controller + recorder *MockServiceMockRecorder +} + +// MockServiceMockRecorder is the mock recorder for MockService. +type MockServiceMockRecorder struct { + mock *MockService +} + +// NewMockService creates a new mock instance. +func NewMockService(ctrl *gomock.Controller) *MockService { + mock := &MockService{ctrl: ctrl} + mock.recorder = &MockServiceMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockService) EXPECT() *MockServiceMockRecorder { + return m.recorder +} + +// Build mocks base method. +func (m *MockService) Build(ctx context.Context, project *types.Project, options api.BuildOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Build", ctx, project, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// Build indicates an expected call of Build. +func (mr *MockServiceMockRecorder) Build(ctx, project, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Build", reflect.TypeOf((*MockService)(nil).Build), ctx, project, options) +} + +// Convert mocks base method. +func (m *MockService) Convert(ctx context.Context, project *types.Project, options api.ConvertOptions) ([]byte, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Convert", ctx, project, options) + ret0, _ := ret[0].([]byte) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Convert indicates an expected call of Convert. +func (mr *MockServiceMockRecorder) Convert(ctx, project, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Convert", reflect.TypeOf((*MockService)(nil).Convert), ctx, project, options) +} + +// Copy mocks base method. +func (m *MockService) Copy(ctx context.Context, projectName string, options api.CopyOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Copy", ctx, projectName, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// Copy indicates an expected call of Copy. +func (mr *MockServiceMockRecorder) Copy(ctx, projectName, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Copy", reflect.TypeOf((*MockService)(nil).Copy), ctx, projectName, options) +} + +// Create mocks base method. +func (m *MockService) Create(ctx context.Context, project *types.Project, options api.CreateOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Create", ctx, project, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// Create indicates an expected call of Create. +func (mr *MockServiceMockRecorder) Create(ctx, project, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Create", reflect.TypeOf((*MockService)(nil).Create), ctx, project, options) +} + +// Down mocks base method. +func (m *MockService) Down(ctx context.Context, projectName string, options api.DownOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Down", ctx, projectName, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// Down indicates an expected call of Down. +func (mr *MockServiceMockRecorder) Down(ctx, projectName, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Down", reflect.TypeOf((*MockService)(nil).Down), ctx, projectName, options) +} + +// Events mocks base method. +func (m *MockService) Events(ctx context.Context, projectName string, options api.EventsOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Events", ctx, projectName, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// Events indicates an expected call of Events. +func (mr *MockServiceMockRecorder) Events(ctx, projectName, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Events", reflect.TypeOf((*MockService)(nil).Events), ctx, projectName, options) +} + +// Exec mocks base method. +func (m *MockService) Exec(ctx context.Context, projectName string, options api.RunOptions) (int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Exec", ctx, projectName, options) + ret0, _ := ret[0].(int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Exec indicates an expected call of Exec. +func (mr *MockServiceMockRecorder) Exec(ctx, projectName, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exec", reflect.TypeOf((*MockService)(nil).Exec), ctx, projectName, options) +} + +// Images mocks base method. +func (m *MockService) Images(ctx context.Context, projectName string, options api.ImagesOptions) ([]api.ImageSummary, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Images", ctx, projectName, options) + ret0, _ := ret[0].([]api.ImageSummary) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Images indicates an expected call of Images. +func (mr *MockServiceMockRecorder) Images(ctx, projectName, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Images", reflect.TypeOf((*MockService)(nil).Images), ctx, projectName, options) +} + +// Kill mocks base method. +func (m *MockService) Kill(ctx context.Context, projectName string, options api.KillOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Kill", ctx, projectName, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// Kill indicates an expected call of Kill. +func (mr *MockServiceMockRecorder) Kill(ctx, projectName, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Kill", reflect.TypeOf((*MockService)(nil).Kill), ctx, projectName, options) +} + +// List mocks base method. +func (m *MockService) List(ctx context.Context, options api.ListOptions) ([]api.Stack, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "List", ctx, options) + ret0, _ := ret[0].([]api.Stack) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// List indicates an expected call of List. +func (mr *MockServiceMockRecorder) List(ctx, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "List", reflect.TypeOf((*MockService)(nil).List), ctx, options) +} + +// Logs mocks base method. +func (m *MockService) Logs(ctx context.Context, projectName string, consumer api.LogConsumer, options api.LogOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Logs", ctx, projectName, consumer, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// Logs indicates an expected call of Logs. +func (mr *MockServiceMockRecorder) Logs(ctx, projectName, consumer, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Logs", reflect.TypeOf((*MockService)(nil).Logs), ctx, projectName, consumer, options) +} + +// Pause mocks base method. +func (m *MockService) Pause(ctx context.Context, projectName string, options api.PauseOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Pause", ctx, projectName, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// Pause indicates an expected call of Pause. +func (mr *MockServiceMockRecorder) Pause(ctx, projectName, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Pause", reflect.TypeOf((*MockService)(nil).Pause), ctx, projectName, options) +} + +// Port mocks base method. +func (m *MockService) Port(ctx context.Context, projectName, service string, port int, options api.PortOptions) (string, int, 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 +} + +// Port indicates an expected call of Port. +func (mr *MockServiceMockRecorder) Port(ctx, projectName, service, port, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Port", reflect.TypeOf((*MockService)(nil).Port), ctx, projectName, service, port, options) +} + +// Ps mocks base method. +func (m *MockService) Ps(ctx context.Context, projectName string, options api.PsOptions) ([]api.ContainerSummary, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Ps", ctx, projectName, options) + ret0, _ := ret[0].([]api.ContainerSummary) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Ps indicates an expected call of Ps. +func (mr *MockServiceMockRecorder) Ps(ctx, projectName, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ps", reflect.TypeOf((*MockService)(nil).Ps), ctx, projectName, options) +} + +// Pull mocks base method. +func (m *MockService) Pull(ctx context.Context, project *types.Project, options api.PullOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Pull", ctx, project, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// Pull indicates an expected call of Pull. +func (mr *MockServiceMockRecorder) Pull(ctx, project, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Pull", reflect.TypeOf((*MockService)(nil).Pull), ctx, project, options) +} + +// Push mocks base method. +func (m *MockService) Push(ctx context.Context, project *types.Project, options api.PushOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Push", ctx, project, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// Push indicates an expected call of Push. +func (mr *MockServiceMockRecorder) Push(ctx, project, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Push", reflect.TypeOf((*MockService)(nil).Push), ctx, project, options) +} + +// Remove mocks base method. +func (m *MockService) Remove(ctx context.Context, projectName string, options api.RemoveOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Remove", ctx, projectName, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// Remove indicates an expected call of Remove. +func (mr *MockServiceMockRecorder) Remove(ctx, projectName, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockService)(nil).Remove), ctx, projectName, options) +} + +// Restart mocks base method. +func (m *MockService) Restart(ctx context.Context, projectName string, options api.RestartOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Restart", ctx, projectName, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// Restart indicates an expected call of Restart. +func (mr *MockServiceMockRecorder) Restart(ctx, projectName, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Restart", reflect.TypeOf((*MockService)(nil).Restart), ctx, projectName, options) +} + +// RunOneOffContainer mocks base method. +func (m *MockService) RunOneOffContainer(ctx context.Context, project *types.Project, opts api.RunOptions) (int, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "RunOneOffContainer", ctx, project, opts) + ret0, _ := ret[0].(int) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// RunOneOffContainer indicates an expected call of RunOneOffContainer. +func (mr *MockServiceMockRecorder) RunOneOffContainer(ctx, project, opts interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RunOneOffContainer", reflect.TypeOf((*MockService)(nil).RunOneOffContainer), ctx, project, opts) +} + +// Start mocks base method. +func (m *MockService) Start(ctx context.Context, projectName string, options api.StartOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Start", ctx, projectName, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// Start indicates an expected call of Start. +func (mr *MockServiceMockRecorder) Start(ctx, projectName, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Start", reflect.TypeOf((*MockService)(nil).Start), ctx, projectName, options) +} + +// Stop mocks base method. +func (m *MockService) Stop(ctx context.Context, projectName string, options api.StopOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Stop", ctx, projectName, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// Stop indicates an expected call of Stop. +func (mr *MockServiceMockRecorder) Stop(ctx, projectName, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Stop", reflect.TypeOf((*MockService)(nil).Stop), ctx, projectName, options) +} + +// Top mocks base method. +func (m *MockService) Top(ctx context.Context, projectName string, services []string) ([]api.ContainerProcSummary, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Top", ctx, projectName, services) + ret0, _ := ret[0].([]api.ContainerProcSummary) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Top indicates an expected call of Top. +func (mr *MockServiceMockRecorder) Top(ctx, projectName, services interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Top", reflect.TypeOf((*MockService)(nil).Top), ctx, projectName, services) +} + +// UnPause mocks base method. +func (m *MockService) UnPause(ctx context.Context, projectName string, options api.PauseOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "UnPause", ctx, projectName, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// UnPause indicates an expected call of UnPause. +func (mr *MockServiceMockRecorder) UnPause(ctx, projectName, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UnPause", reflect.TypeOf((*MockService)(nil).UnPause), ctx, projectName, options) +} + +// Up mocks base method. +func (m *MockService) Up(ctx context.Context, project *types.Project, options api.UpOptions) error { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Up", ctx, project, options) + ret0, _ := ret[0].(error) + return ret0 +} + +// Up indicates an expected call of Up. +func (mr *MockServiceMockRecorder) Up(ctx, project, options interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Up", reflect.TypeOf((*MockService)(nil).Up), ctx, project, options) +} + +// MockLogConsumer is a mock of LogConsumer interface. +type MockLogConsumer struct { + ctrl *gomock.Controller + recorder *MockLogConsumerMockRecorder +} + +// MockLogConsumerMockRecorder is the mock recorder for MockLogConsumer. +type MockLogConsumerMockRecorder struct { + mock *MockLogConsumer +} + +// NewMockLogConsumer creates a new mock instance. +func NewMockLogConsumer(ctrl *gomock.Controller) *MockLogConsumer { + mock := &MockLogConsumer{ctrl: ctrl} + mock.recorder = &MockLogConsumerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockLogConsumer) EXPECT() *MockLogConsumerMockRecorder { + return m.recorder +} + +// Log mocks base method. +func (m *MockLogConsumer) Log(service, container, message string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Log", service, container, message) +} + +// Log indicates an expected call of Log. +func (mr *MockLogConsumerMockRecorder) Log(service, container, message interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Log", reflect.TypeOf((*MockLogConsumer)(nil).Log), service, container, message) +} + +// Register mocks base method. +func (m *MockLogConsumer) Register(container string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Register", container) +} + +// Register indicates an expected call of Register. +func (mr *MockLogConsumerMockRecorder) Register(container interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Register", reflect.TypeOf((*MockLogConsumer)(nil).Register), container) +} + +// Status mocks base method. +func (m *MockLogConsumer) Status(container, msg string) { + m.ctrl.T.Helper() + m.ctrl.Call(m, "Status", container, msg) +} + +// Status indicates an expected call of Status. +func (mr *MockLogConsumerMockRecorder) Status(container, msg interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Status", reflect.TypeOf((*MockLogConsumer)(nil).Status), container, msg) +} From b2c0d250050cdf5a04ba0430d016e02998b62117 Mon Sep 17 00:00:00 2001 From: Nick Sieger Date: Tue, 7 Jun 2022 17:11:16 -0500 Subject: [PATCH 2/2] ps: use DisplayablePorts from docker/cli Fixes #9527. Signed-off-by: Nick Sieger --- cmd/compose/ps.go | 75 ++++++------------------------------- cmd/compose/ps_test.go | 84 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 63 deletions(-) create mode 100644 cmd/compose/ps_test.go diff --git a/cmd/compose/ps.go b/cmd/compose/ps.go index c395241ad..9eed19145 100644 --- a/cmd/compose/ps.go +++ b/cmd/compose/ps.go @@ -27,6 +27,7 @@ import ( "github.com/docker/compose/v2/cmd/formatter" "github.com/docker/compose/v2/pkg/utils" + "github.com/docker/docker/api/types" formatter2 "github.com/docker/cli/cli/command/formatter" "github.com/pkg/errors" @@ -146,7 +147,7 @@ SERVICES: func writter(containers []api.ContainerSummary) func(w io.Writer) { return func(w io.Writer) { for _, container := range containers { - ports := DisplayablePorts(container) + ports := displayablePorts(container) status := container.State if status == "running" && container.Health != "" { status = fmt.Sprintf("%s (%s)", container.State, container.Health) @@ -178,72 +179,20 @@ func hasStatus(c api.ContainerSummary, statuses []string) bool { return false } -type portRange struct { - pStart int - pEnd int - tStart int - tEnd int - IP string - protocol string -} - -func (pr portRange) String() string { - var ( - pub string - tgt string - ) - - if pr.pEnd > pr.pStart { - pub = fmt.Sprintf("%s:%d-%d->", pr.IP, pr.pStart, pr.pEnd) - } else if pr.pStart > 0 { - pub = fmt.Sprintf("%s:%d->", pr.IP, pr.pStart) - } - if pr.tEnd > pr.tStart { - tgt = fmt.Sprintf("%d-%d", pr.tStart, pr.tEnd) - } else { - tgt = fmt.Sprintf("%d", pr.tStart) - } - return fmt.Sprintf("%s%s/%s", pub, tgt, pr.protocol) -} - -// DisplayablePorts is copy pasted from https://github.com/docker/cli/pull/581/files -func DisplayablePorts(c api.ContainerSummary) string { +func displayablePorts(c api.ContainerSummary) string { if c.Publishers == nil { return "" } - sort.Sort(c.Publishers) - - pr := portRange{} - ports := []string{} - for _, p := range c.Publishers { - prIsRange := pr.tEnd != pr.tStart - tOverlaps := p.TargetPort <= pr.tEnd - - // Start a new port-range if: - // - the protocol is different from the current port-range - // - published or target port are not consecutive to the current port-range - // - the current port-range is a _range_, and the target port overlaps with the current range's target-ports - if p.Protocol != pr.protocol || p.URL != pr.IP || p.PublishedPort-pr.pEnd > 1 || p.TargetPort-pr.tEnd > 1 || prIsRange && tOverlaps { - // start a new port-range, and print the previous port-range (if any) - if pr.pStart > 0 { - ports = append(ports, pr.String()) - } - pr = portRange{ - pStart: p.PublishedPort, - pEnd: p.PublishedPort, - tStart: p.TargetPort, - tEnd: p.TargetPort, - protocol: p.Protocol, - IP: p.URL, - } - continue + ports := make([]types.Port, len(c.Publishers)) + for i, pub := range c.Publishers { + ports[i] = types.Port{ + IP: pub.URL, + PrivatePort: uint16(pub.TargetPort), + PublicPort: uint16(pub.PublishedPort), + Type: pub.Protocol, } - pr.pEnd = p.PublishedPort - pr.tEnd = p.TargetPort } - if pr.tStart > 0 { - ports = append(ports, pr.String()) - } - return strings.Join(ports, ", ") + + return formatter2.DisplayablePorts(ports) } diff --git a/cmd/compose/ps_test.go b/cmd/compose/ps_test.go new file mode 100644 index 000000000..2a6d6bb6a --- /dev/null +++ b/cmd/compose/ps_test.go @@ -0,0 +1,84 @@ +/* + 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" + "os" + "path/filepath" + "testing" + + "github.com/docker/compose/v2/pkg/api" + "github.com/docker/compose/v2/pkg/mocks" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/assert" +) + +func TestPsPretty(t *testing.T) { + ctx := context.Background() + origStdout := os.Stdout + t.Cleanup(func() { + os.Stdout = origStdout + }) + dir := t.TempDir() + f, err := os.Create(filepath.Join(dir, "output.txt")) + if err != nil { + t.Fatal("could not create output file") + } + defer func() { _ = f.Close() }() + + os.Stdout = f + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + backend := mocks.NewMockService(ctrl) + backend.EXPECT(). + Ps(gomock.Eq(ctx), gomock.Any(), gomock.Any()). + DoAndReturn(func(ctx context.Context, projectName string, options api.PsOptions) ([]api.ContainerSummary, error) { + return []api.ContainerSummary{ + { + ID: "abc123", + Name: "ABC", + Publishers: api.PortPublishers{ + { + TargetPort: 8080, + PublishedPort: 8080, + Protocol: "tcp", + }, + { + TargetPort: 8443, + PublishedPort: 8443, + Protocol: "tcp", + }, + }, + }, + }, nil + }).AnyTimes() + + opts := psOptions{projectOptions: &projectOptions{ProjectName: "test"}} + err = runPs(ctx, backend, nil, opts) + assert.NoError(t, err) + + _, err = f.Seek(0, 0) + assert.NoError(t, err) + + output := make([]byte, 256) + _, err = f.Read(output) + assert.NoError(t, err) + + assert.Contains(t, string(output), "8080/tcp, 8443/tcp") +}