This commit is contained in:
Maxim Kozlov 2026-05-12 10:12:14 +00:00 committed by GitHub
commit 40428326da
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 172 additions and 5 deletions

View file

@ -69,19 +69,20 @@ func psCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Backend
opts := psOptions{
ProjectOptions: p,
}
psCmd := &cobra.Command{
var psCmd *cobra.Command
psCmd = &cobra.Command{
Use: "ps [OPTIONS] [SERVICE...]",
Short: "List containers",
PreRunE: func(cmd *cobra.Command, args []string) error {
return opts.parseFilter()
},
RunE: Adapt(func(ctx context.Context, args []string) error {
return runPs(ctx, dockerCli, backendOptions, args, opts)
return runPs(ctx, dockerCli, backendOptions, args, opts, psCmd)
}),
ValidArgsFunction: completeServiceNames(dockerCli, p),
}
flags := psCmd.Flags()
flags.StringVar(&opts.Format, "format", "table", cliflags.FormatHelp)
flags.StringVar(&opts.Format, "format", "", cliflags.FormatHelp)
flags.StringVar(&opts.Filter, "filter", "", "Filter services by a property (supported filters: status)")
flags.StringArrayVar(&opts.Status, "status", []string{}, "Filter services by status. Values: [paused | restarting | removing | running | dead | created | exited]")
flags.BoolVarP(&opts.Quiet, "quiet", "q", false, "Only display IDs")
@ -92,7 +93,7 @@ func psCommand(p *ProjectOptions, dockerCli command.Cli, backendOptions *Backend
return psCmd
}
func runPs(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, services []string, opts psOptions) error { //nolint:gocyclo
func runPs(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOptions, services []string, opts psOptions, cmd *cobra.Command) error { //nolint:gocyclo
project, name, err := opts.projectOrName(ctx, dockerCli, services...)
if err != nil {
return err
@ -152,9 +153,14 @@ func runPs(ctx context.Context, dockerCli command.Cli, backendOptions *BackendOp
return nil
}
if opts.Format == "" {
if len(opts.Format) == 0 {
opts.Format = dockerCli.ConfigFile().PsFormat
}
if len(opts.Format) == 0 {
opts.Format = "table"
} else if opts.Quiet && cmd.Flags().Changed("format") {
fmt.Fprintln(dockerCli.Err(), "WARNING: Ignoring custom format, because both --format and --quiet are set.")
}
containerCtx := cliformatter.Context{
Output: dockerCli.Out(),

View file

@ -0,0 +1,161 @@
/*
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 (
"testing"
"github.com/docker/cli/cli/config/configfile"
"github.com/docker/cli/cli/streams"
"go.uber.org/mock/gomock"
"gotest.tools/v3/assert"
"github.com/docker/compose/v5/pkg/mocks"
)
func TestPsCommandDefaultFormat(t *testing.T) {
// Test that the format flag has empty string as default
projectOpts := &ProjectOptions{}
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
cli := mocks.NewMockCli(mockCtrl)
cli.EXPECT().ConfigFile().Return(configfile.New("test")).AnyTimes()
cli.EXPECT().Out().Return(&streams.Out{}).AnyTimes()
cli.EXPECT().Err().Return(&streams.Out{}).AnyTimes()
backendOptions := &BackendOptions{}
cmd := psCommand(projectOpts, cli, backendOptions)
// Check default value of format flag
formatFlag := cmd.Flags().Lookup("format")
assert.Equal(t, formatFlag.DefValue, "")
}
func TestPsCommandUsesConfigFormat(t *testing.T) {
projectOpts := &ProjectOptions{}
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
cli := mocks.NewMockCli(mockCtrl)
config := configfile.New("test")
config.PsFormat = "table {{.Names}}\t{{.Image}}"
cli.EXPECT().ConfigFile().Return(config).AnyTimes()
out := &streams.Out{}
err := &streams.Out{}
cli.EXPECT().Out().Return(out).AnyTimes()
cli.EXPECT().Err().Return(err).AnyTimes()
backendOptions := &BackendOptions{}
cmd := psCommand(projectOpts, cli, backendOptions)
// Set args to trigger format resolution
cmd.SetArgs([]string{})
// Mock the backend to avoid actual container operations
// This test focuses on format flag logic, not full command execution
formatFlag := cmd.Flags().Lookup("format")
assert.Equal(t, formatFlag.DefValue, "")
}
func TestPsCommandQuietWithFormatFlag(t *testing.T) {
projectOpts := &ProjectOptions{}
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
cli := mocks.NewMockCli(mockCtrl)
config := configfile.New("test")
cli.EXPECT().ConfigFile().Return(config).AnyTimes()
out := &streams.Out{}
err := &streams.Out{}
cli.EXPECT().Out().Return(out).AnyTimes()
cli.EXPECT().Err().Return(err).AnyTimes()
backendOptions := &BackendOptions{}
cmd := psCommand(projectOpts, cli, backendOptions)
// Test that warning is shown when both --format and --quiet are explicitly set
errBuf := &streams.Out{}
cli.EXPECT().Err().Return(errBuf).AnyTimes()
// Simulate flag changes
cmd.SetArgs([]string{"--format", "table {{.Names}}", "--quiet"})
cmd.ParseFlags([]string{"--format", "table {{.Names}}", "--quiet"})
// The flag should be marked as changed
assert.Assert(t, cmd.Flags().Changed("format"))
assert.Assert(t, cmd.Flags().Changed("quiet"))
}
func TestPsCommandQuietWithConfigFormat(t *testing.T) {
projectOpts := &ProjectOptions{}
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
cli := mocks.NewMockCli(mockCtrl)
config := configfile.New("test")
config.PsFormat = "table {{.Names}}\t{{.Image}}"
cli.EXPECT().ConfigFile().Return(config).AnyTimes()
out := &streams.Out{}
err := &streams.Out{}
cli.EXPECT().Out().Return(out).AnyTimes()
cli.EXPECT().Err().Return(err).AnyTimes()
backendOptions := &BackendOptions{}
cmd := psCommand(projectOpts, cli, backendOptions)
// Test that no warning is shown when only --quiet is set (format from config)
errBuf := &streams.Out{}
cli.EXPECT().Err().Return(errBuf).AnyTimes()
// Simulate only quiet flag change
cmd.SetArgs([]string{"--quiet"})
cmd.ParseFlags([]string{"--quiet"})
// Only quiet flag should be changed, not format
assert.Assert(t, !cmd.Flags().Changed("format"))
assert.Assert(t, cmd.Flags().Changed("quiet"))
}
func TestPsCommandFormatFallback(t *testing.T) {
projectOpts := &ProjectOptions{}
mockCtrl := gomock.NewController(t)
defer mockCtrl.Finish()
cli := mocks.NewMockCli(mockCtrl)
config := configfile.New("test")
// No PsFormat set in config
cli.EXPECT().ConfigFile().Return(config).AnyTimes()
out := &streams.Out{}
err := &streams.Out{}
cli.EXPECT().Out().Return(out).AnyTimes()
cli.EXPECT().Err().Return(err).AnyTimes()
backendOptions := &BackendOptions{}
cmd := psCommand(projectOpts, cli, backendOptions)
// Test that format falls back to "table" when not set in flags or config
cmd.SetArgs([]string{})
cmd.ParseFlags([]string{})
// Should not have format flag changed
assert.Assert(t, !cmd.Flags().Changed("format"))
}