Skip to content

Commit 6b93cf2

Browse files
committed
cli: Wrap Err stream
This wraps the cli stderr stream the same way as stdin and stdout, which extends the stream with TTY-related methods. Signed-off-by: Paweł Gronowski <pawel.gronowski@docker.com>
1 parent 52eddcf commit 6b93cf2

9 files changed

Lines changed: 25 additions & 24 deletions

File tree

cli/command/cli.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ const defaultInitTimeout = 2 * time.Second
4444
type Streams interface {
4545
In() *streams.In
4646
Out() *streams.Out
47-
Err() io.Writer
47+
Err() *streams.Out
4848
}
4949

5050
// Cli represents the docker command line client.
@@ -75,7 +75,7 @@ type DockerCli struct {
7575
options *cliflags.ClientOptions
7676
in *streams.In
7777
out *streams.Out
78-
err io.Writer
78+
err *streams.Out
7979
client client.APIClient
8080
serverInfo ServerInfo
8181
contentTrust bool
@@ -124,7 +124,7 @@ func (cli *DockerCli) Out() *streams.Out {
124124
}
125125

126126
// Err returns the writer used for stderr
127-
func (cli *DockerCli) Err() io.Writer {
127+
func (cli *DockerCli) Err() *streams.Out {
128128
return cli.err
129129
}
130130

cli/command/cli_options.go

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ func WithStandardStreams() CLIOption {
2323
stdin, stdout, stderr := term.StdStreams()
2424
cli.in = streams.NewIn(stdin)
2525
cli.out = streams.NewOut(stdout)
26-
cli.err = stderr
26+
cli.err = streams.NewOut(stderr)
2727
return nil
2828
}
2929
}
@@ -40,8 +40,9 @@ func WithBaseContext(ctx context.Context) CLIOption {
4040
// WithCombinedStreams uses the same stream for the output and error streams.
4141
func WithCombinedStreams(combined io.Writer) CLIOption {
4242
return func(cli *DockerCli) error {
43-
cli.out = streams.NewOut(combined)
44-
cli.err = combined
43+
s := streams.NewOut(combined)
44+
cli.out = s
45+
cli.err = s
4546
return nil
4647
}
4748
}
@@ -65,7 +66,7 @@ func WithOutputStream(out io.Writer) CLIOption {
6566
// WithErrorStream sets a cli error stream.
6667
func WithErrorStream(err io.Writer) CLIOption {
6768
return func(cli *DockerCli) error {
68-
cli.err = err
69+
cli.err = streams.NewOut(err)
6970
return nil
7071
}
7172
}

cli/command/cli_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import (
1818
"github.com/docker/cli/cli/config"
1919
"github.com/docker/cli/cli/config/configfile"
2020
"github.com/docker/cli/cli/flags"
21+
"github.com/docker/cli/cli/streams"
2122
"github.com/docker/docker/api"
2223
"github.com/docker/docker/api/types"
2324
"github.com/docker/docker/client"
@@ -253,7 +254,7 @@ func TestExperimentalCLI(t *testing.T) {
253254
},
254255
}
255256

256-
cli := &DockerCli{client: apiclient, err: os.Stderr}
257+
cli := &DockerCli{client: apiclient, err: streams.NewOut(os.Stderr)}
257258
config.SetDir(dir.Path())
258259
err := cli.Initialize(flags.NewClientOptions())
259260
assert.NilError(t, err)

cli/command/container/create.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,9 +130,9 @@ func pullImage(ctx context.Context, dockerCli command.Cli, img string, options *
130130

131131
out := dockerCli.Err()
132132
if options.quiet {
133-
out = io.Discard
133+
out = streams.NewOut(io.Discard)
134134
}
135-
return jsonmessage.DisplayJSONMessagesToStream(responseBody, streams.NewOut(out), nil)
135+
return jsonmessage.DisplayJSONMessagesToStream(responseBody, out, nil)
136136
}
137137

138138
type cidFile struct {

cli/command/image/push.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import (
1818
registrytypes "github.com/docker/docker/api/types/registry"
1919
"github.com/docker/docker/pkg/jsonmessage"
2020
"github.com/docker/docker/registry"
21-
"github.com/moby/term"
2221
"github.com/morikuni/aec"
2322
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
2423
"github.com/pkg/errors"
@@ -176,7 +175,7 @@ func handleAux(dockerCli command.Cli) func(jm jsonmessage.JSONMessage) {
176175
}
177176

178177
func printNote(dockerCli command.Cli, format string, args ...any) {
179-
if _, isTTY := term.GetFdInfo(dockerCli.Err()); isTTY {
178+
if dockerCli.Err().IsTerminal() {
180179
_, _ = fmt.Fprint(dockerCli.Err(), aec.WhiteF.Apply(aec.CyanB.Apply("[ NOTE ]"))+" ")
181180
} else {
182181
_, _ = fmt.Fprint(dockerCli.Err(), "[ NOTE ] ")

cli/command/registry/login_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"testing"
99

1010
configtypes "github.com/docker/cli/cli/config/types"
11+
"github.com/docker/cli/cli/streams"
1112
"github.com/docker/cli/internal/test"
1213
registrytypes "github.com/docker/docker/api/types/registry"
1314
"github.com/docker/docker/api/types/system"
@@ -69,7 +70,7 @@ func TestLoginWithCredStoreCreds(t *testing.T) {
6970
for _, tc := range testCases {
7071
cli := test.NewFakeCli(&fakeClient{})
7172
errBuf := new(bytes.Buffer)
72-
cli.SetErr(errBuf)
73+
cli.SetErr(streams.NewOut(errBuf))
7374
loginWithCredStoreCreds(ctx, cli, &tc.inputAuthConfig)
7475
outputString := cli.OutBuffer().String()
7576
assert.Check(t, is.Equal(tc.expectedMsg, outputString))

cli/command/telemetry_utils.go

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import (
77
"time"
88

99
"github.com/docker/cli/cli/version"
10-
"github.com/moby/term"
1110
"github.com/pkg/errors"
1211
"github.com/spf13/cobra"
1312
"go.opentelemetry.io/otel/attribute"
@@ -101,12 +100,10 @@ func startCobraCommandTimer(mp metric.MeterProvider, attrs []attribute.KeyValue)
101100
}
102101

103102
func stdioAttributes(streams Streams) []attribute.KeyValue {
104-
// we don't wrap stderr, but we do wrap in/out
105-
_, stderrTty := term.GetFdInfo(streams.Err())
106103
return []attribute.KeyValue{
107104
attribute.Bool("command.stdin.isatty", streams.In().IsTerminal()),
108105
attribute.Bool("command.stdout.isatty", streams.Out().IsTerminal()),
109-
attribute.Bool("command.stderr.isatty", stderrTty),
106+
attribute.Bool("command.stderr.isatty", streams.Err().IsTerminal()),
110107
}
111108
}
112109

cli/command/telemetry_utils_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ func TestStdioAttributes(t *testing.T) {
136136
cli := &DockerCli{
137137
in: streams.NewIn(io.NopCloser(strings.NewReader(""))),
138138
out: streams.NewOut(outBuffer),
139-
err: errBuffer,
139+
err: streams.NewOut(errBuffer),
140140
}
141141
cli.In().SetIsTerminal(tc.stdinTty)
142142
cli.Out().SetIsTerminal(tc.stdoutTty)

internal/test/cli.go

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ type FakeCli struct {
2828
configfile *configfile.ConfigFile
2929
out *streams.Out
3030
outBuffer *bytes.Buffer
31-
err *bytes.Buffer
31+
err *streams.Out
32+
errBuffer *bytes.Buffer
3233
in *streams.In
3334
server command.ServerInfo
3435
notaryClientFunc NotaryClientFuncType
@@ -48,7 +49,8 @@ func NewFakeCli(apiClient client.APIClient, opts ...func(*FakeCli)) *FakeCli {
4849
client: apiClient,
4950
out: streams.NewOut(outBuffer),
5051
outBuffer: outBuffer,
51-
err: errBuffer,
52+
err: streams.NewOut(errBuffer),
53+
errBuffer: errBuffer,
5254
in: streams.NewIn(io.NopCloser(strings.NewReader(""))),
5355
// Use an empty string for filename so that tests don't create configfiles
5456
// Set cli.ConfigFile().Filename to a tempfile to support Save.
@@ -67,7 +69,7 @@ func (c *FakeCli) SetIn(in *streams.In) {
6769
}
6870

6971
// SetErr sets the stderr stream for the cli to the specified io.Writer
70-
func (c *FakeCli) SetErr(err *bytes.Buffer) {
72+
func (c *FakeCli) SetErr(err *streams.Out) {
7173
c.err = err
7274
}
7375

@@ -112,7 +114,7 @@ func (c *FakeCli) Out() *streams.Out {
112114
}
113115

114116
// Err returns the output stream (stderr) the cli should write on
115-
func (c *FakeCli) Err() io.Writer {
117+
func (c *FakeCli) Err() *streams.Out {
116118
return c.err
117119
}
118120

@@ -153,13 +155,13 @@ func (c *FakeCli) OutBuffer() *bytes.Buffer {
153155

154156
// ErrBuffer Buffer returns the stderr buffer
155157
func (c *FakeCli) ErrBuffer() *bytes.Buffer {
156-
return c.err
158+
return c.errBuffer
157159
}
158160

159161
// ResetOutputBuffers resets the .OutBuffer() and.ErrBuffer() back to empty
160162
func (c *FakeCli) ResetOutputBuffers() {
161163
c.outBuffer.Reset()
162-
c.err.Reset()
164+
c.errBuffer.Reset()
163165
}
164166

165167
// SetNotaryClient sets the internal getter for retrieving a NotaryClient

0 commit comments

Comments
 (0)