Skip to content

Commit 86e91e0

Browse files
gloursndeloof
authored andcommitted
Add streamOverrideWrapper to intercepts command.Cli stream methods and transparently returns custom streams when provided via options
Add new GetConfiguredStreams function to Compose API definition Signed-off-by: Guillaume Lours <705411+glours@users.noreply.github.com>
1 parent e1678c5 commit 86e91e0

7 files changed

Lines changed: 72 additions & 19 deletions

File tree

cmd/compose/logs.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,12 @@ func runLogs(ctx context.Context, dockerCli command.Cli, backendOptions *Backend
8686
}
8787
}
8888

89-
consumer := formatter.NewLogConsumer(ctx, dockerCli.Out(), dockerCli.Err(), !opts.noColor, !opts.noPrefix, false)
9089
backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
9190
if err != nil {
9291
return err
9392
}
93+
outStream, errStream, _ := backend.GetConfiguredStreams()
94+
consumer := formatter.NewLogConsumer(ctx, outStream, errStream, !opts.noColor, !opts.noPrefix, false)
9495
return backend.Logs(ctx, name, consumer, api.LogOptions{
9596
Project: project,
9697
Services: services,

cmd/compose/up.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,8 @@ func runUp(
293293
var consumer api.LogConsumer
294294
var attach []string
295295
if !upOptions.Detach {
296-
consumer = formatter.NewLogConsumer(ctx, dockerCli.Out(), dockerCli.Err(), !upOptions.noColor, !upOptions.noPrefix, upOptions.timestamp)
296+
outStream, errStream, _ := backend.GetConfiguredStreams()
297+
consumer = formatter.NewLogConsumer(ctx, outStream, errStream, !upOptions.noColor, !upOptions.noPrefix, upOptions.timestamp)
297298

298299
var attachSet utils.Set[string]
299300
if len(upOptions.attach) != 0 {

cmd/compose/watch.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,11 +121,12 @@ func runWatch(ctx context.Context, dockerCli command.Cli, backendOptions *Backen
121121
}
122122
}
123123

124-
consumer := formatter.NewLogConsumer(ctx, dockerCli.Out(), dockerCli.Err(), false, false, false)
125124
backend, err := compose.NewComposeService(dockerCli, backendOptions.Options...)
126125
if err != nil {
127126
return err
128127
}
128+
outStream, errStream, _ := backend.GetConfiguredStreams()
129+
consumer := formatter.NewLogConsumer(ctx, outStream, errStream, false, false, false)
129130
return backend.Watch(ctx, project, api.WatchOptions{
130131
Build: &build,
131132
LogTo: consumer,

pkg/api/api.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,9 @@ type Compose interface {
9999
Generate(ctx context.Context, options GenerateOptions) (*types.Project, error)
100100
// Volumes executes the equivalent to a `docker volume ls`
101101
Volumes(ctx context.Context, project string, options VolumesOptions) ([]VolumesSummary, error)
102+
// GetConfiguredStreams returns the configured I/O streams (stdout, stderr, stdin).
103+
// If no custom streams were configured, it returns the dockerCli streams.
104+
GetConfiguredStreams() (stdout io.Writer, stderr io.Writer, stdin io.Reader)
102105
}
103106

104107
type VolumesOptions struct {

pkg/compose/build.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,7 @@ func (s *composeService) build(ctx context.Context, project *types.Project, opti
177177
if options.Progress == progress.ModeAuto {
178178
options.Progress = os.Getenv("BUILDKIT_PROGRESS")
179179
}
180-
w, err = xprogress.NewPrinter(progressCtx, os.Stdout, progressui.DisplayMode(options.Progress),
180+
w, err = xprogress.NewPrinter(progressCtx, s.stdout(), progressui.DisplayMode(options.Progress),
181181
xprogress.WithDesc(
182182
fmt.Sprintf("building with %q instance using %s driver", b.Name, b.Driver),
183183
fmt.Sprintf("%s:%s", b.Driver, b.Name),

pkg/compose/build_bake.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ func (s *composeService) doBuildBake(ctx context.Context, project *types.Project
142142
if displayMode == progress.ModeAuto && !s.stdout().IsTerminal() {
143143
displayMode = progressui.PlainMode
144144
}
145-
out = os.Stdout // should be s.stdout(), but NewDisplay require access to the underlying *File
145+
out = s.stdout()
146146
}
147147
display, err := progressui.NewDisplay(out, displayMode)
148148
if err != nil {

pkg/compose/compose.go

Lines changed: 61 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@ func NewComposeService(dockerCli command.Cli, options ...Option) (api.Compose, e
9494
return defaultValue, nil
9595
}
9696
}
97+
98+
// If custom streams were provided, wrap the Docker CLI to use them
99+
if s.outStream != nil || s.errStream != nil || s.inStream != nil {
100+
s.dockerCli = s.wrapDockerCliWithStreams(dockerCli)
101+
}
102+
97103
return s, nil
98104
}
99105

@@ -141,7 +147,7 @@ func WithContextInfo(info api.ContextInfo) Option {
141147

142148
// WithProxyConfig sets custom HTTP proxy configuration for builds
143149
func WithProxyConfig(config map[string]string) Option {
144-
return func(s *composeService) error{
150+
return func(s *composeService) error {
145151
s.proxyConfig = config
146152
return nil
147153
}
@@ -238,28 +244,15 @@ func (s *composeService) getProxyConfig() map[string]string {
238244
return storeutil.GetProxyConfig(s.dockerCli)
239245
}
240246

241-
242247
func (s *composeService) stdout() *streams.Out {
243-
// If stream overrides are provided, use them
244-
if s.outStream != nil {
245-
return streams.NewOut(s.outStream)
246-
}
247248
return s.dockerCli.Out()
248249
}
249250

250251
func (s *composeService) stdin() *streams.In {
251-
// If stream overrides are provided, use them
252-
if s.inStream != nil {
253-
return streams.NewIn(&readCloserAdapter{r: s.inStream})
254-
}
255252
return s.dockerCli.In()
256253
}
257254

258255
func (s *composeService) stderr() *streams.Out {
259-
// If stream overrides are provided, use them
260-
if s.errStream != nil {
261-
return streams.NewOut(s.errStream)
262-
}
263256
return s.dockerCli.Err()
264257
}
265258

@@ -270,6 +263,11 @@ func (s *composeService) stdinfo() *streams.Out {
270263
return s.stderr()
271264
}
272265

266+
// GetConfiguredStreams returns the configured I/O streams (implements api.Compose interface)
267+
func (s *composeService) GetConfiguredStreams() (io.Writer, io.Writer, io.Reader) {
268+
return s.stdout(), s.stderr(), s.stdin()
269+
}
270+
273271
// readCloserAdapter adapts io.Reader to io.ReadCloser
274272
type readCloserAdapter struct {
275273
r io.Reader
@@ -283,6 +281,55 @@ func (r *readCloserAdapter) Close() error {
283281
return nil
284282
}
285283

284+
// wrapDockerCliWithStreams wraps the Docker CLI to intercept and override stream methods
285+
func (s *composeService) wrapDockerCliWithStreams(baseCli command.Cli) command.Cli {
286+
wrapper := &streamOverrideWrapper{
287+
Cli: baseCli,
288+
}
289+
290+
// Wrap custom streams in Docker CLI's stream types
291+
if s.outStream != nil {
292+
wrapper.outStream = streams.NewOut(s.outStream)
293+
}
294+
if s.errStream != nil {
295+
wrapper.errStream = streams.NewOut(s.errStream)
296+
}
297+
if s.inStream != nil {
298+
wrapper.inStream = streams.NewIn(&readCloserAdapter{r: s.inStream})
299+
}
300+
301+
return wrapper
302+
}
303+
304+
// streamOverrideWrapper wraps command.Cli to override streams with custom implementations
305+
type streamOverrideWrapper struct {
306+
command.Cli
307+
outStream *streams.Out
308+
errStream *streams.Out
309+
inStream *streams.In
310+
}
311+
312+
func (w *streamOverrideWrapper) Out() *streams.Out {
313+
if w.outStream != nil {
314+
return w.outStream
315+
}
316+
return w.Cli.Out()
317+
}
318+
319+
func (w *streamOverrideWrapper) Err() *streams.Out {
320+
if w.errStream != nil {
321+
return w.errStream
322+
}
323+
return w.Cli.Err()
324+
}
325+
326+
func (w *streamOverrideWrapper) In() *streams.In {
327+
if w.inStream != nil {
328+
return w.inStream
329+
}
330+
return w.Cli.In()
331+
}
332+
286333
func getCanonicalContainerName(c container.Summary) string {
287334
if len(c.Names) == 0 {
288335
// corner case, sometime happens on removal. return short ID as a safeguard value

0 commit comments

Comments
 (0)