Просмотр исходного кода

update tty and plain text writers to support dry run mode

Signed-off-by: Guillaume Lours <[email protected]>
Guillaume Lours 2 лет назад
Родитель
Сommit
790712fa92

+ 6 - 1
cmd/compose/compose.go

@@ -338,7 +338,12 @@ func RootCommand(streams command.Cli, backend api.Service) *cobra.Command { //no
 			if parallel > 0 {
 				backend.MaxConcurrency(parallel)
 			}
-			return backend.DryRunMode(dryRun)
+			ctx, err := backend.DryRunMode(cmd.Context(), dryRun)
+			if err != nil {
+				return err
+			}
+			cmd.SetContext(ctx)
+			return nil
 		},
 	}
 

+ 1 - 1
pkg/api/api.go

@@ -78,7 +78,7 @@ type Service interface {
 	// MaxConcurrency defines upper limit for concurrent operations against engine API
 	MaxConcurrency(parallel int)
 	// DryRunMode defines if dry run applies to the command
-	DryRunMode(dryRun bool) error
+	DryRunMode(ctx context.Context, dryRun bool) (context.Context, error)
 	// Watch services' development context and sync/notify/rebuild/restart on changes
 	Watch(ctx context.Context, project *types.Project, services []string, options WatchOptions) error
 }

+ 2 - 0
pkg/api/dryrunclient.go

@@ -37,6 +37,8 @@ import (
 
 var _ client.APIClient = &DryRunClient{}
 
+type DryRunKey struct{}
+
 // DryRunClient implements APIClient by delegating to implementation functions. This allows lazy init and per-method overrides
 type DryRunClient struct {
 	apiClient client.APIClient

+ 3 - 3
pkg/api/proxy.go

@@ -52,7 +52,7 @@ type ServiceProxy struct {
 	ImagesFn             func(ctx context.Context, projectName string, options ImagesOptions) ([]ImageSummary, error)
 	WatchFn              func(ctx context.Context, project *types.Project, services []string, options WatchOptions) error
 	MaxConcurrencyFn     func(parallel int)
-	DryRunModeFn         func(dryRun bool) error
+	DryRunModeFn         func(ctx context.Context, dryRun bool) (context.Context, error)
 	interceptors         []Interceptor
 }
 
@@ -327,6 +327,6 @@ func (s *ServiceProxy) MaxConcurrency(i int) {
 	s.MaxConcurrencyFn(i)
 }
 
-func (s *ServiceProxy) DryRunMode(dryRun bool) error {
-	return s.DryRunModeFn(dryRun)
+func (s *ServiceProxy) DryRunMode(ctx context.Context, dryRun bool) (context.Context, error) {
+	return s.DryRunModeFn(ctx, dryRun)
 }

+ 4 - 4
pkg/compose/compose.go

@@ -66,22 +66,22 @@ func (s *composeService) MaxConcurrency(i int) {
 	s.maxConcurrency = i
 }
 
-func (s *composeService) DryRunMode(dryRun bool) error {
+func (s *composeService) DryRunMode(ctx context.Context, dryRun bool) (context.Context, error) {
 	if dryRun {
 		cli, err := command.NewDockerCli()
 		if err != nil {
-			return err
+			return ctx, err
 		}
 		err = cli.Initialize(flags.NewClientOptions(), command.WithInitializeClient(func(cli *command.DockerCli) (client.APIClient, error) {
 			dryRunClient := api.NewDryRunClient(s.apiClient())
 			return dryRunClient, nil
 		}))
 		if err != nil {
-			return err
+			return ctx, err
 		}
 		s.dockerCli = cli
 	}
-	return nil
+	return context.WithValue(ctx, api.DryRunKey{}, dryRun), nil
 }
 
 func (s *composeService) stdout() *streams.Out {

+ 7 - 6
pkg/mocks/mock_docker_compose_api.go

@@ -108,17 +108,18 @@ func (mr *MockServiceMockRecorder) Down(ctx, projectName, options interface{}) *
 }
 
 // DryRunMode mocks base method.
-func (m *MockService) DryRunMode(dryRun bool) error {
+func (m *MockService) DryRunMode(ctx context.Context, dryRun bool) (context.Context, error) {
 	m.ctrl.T.Helper()
-	ret := m.ctrl.Call(m, "DryRunMode", dryRun)
-	ret0, _ := ret[0].(error)
-	return ret0
+	ret := m.ctrl.Call(m, "DryRunMode", ctx, dryRun)
+	ret0, _ := ret[0].(context.Context)
+	ret1, _ := ret[1].(error)
+	return ret0, ret1
 }
 
 // DryRunMode indicates an expected call of DryRunMode.
-func (mr *MockServiceMockRecorder) DryRunMode(dryRun interface{}) *gomock.Call {
+func (mr *MockServiceMockRecorder) DryRunMode(ctx, dryRun interface{}) *gomock.Call {
 	mr.mock.ctrl.T.Helper()
-	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DryRunMode", reflect.TypeOf((*MockService)(nil).DryRunMode), dryRun)
+	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DryRunMode", reflect.TypeOf((*MockService)(nil).DryRunMode), ctx, dryRun)
 }
 
 // Events mocks base method.

+ 13 - 4
pkg/progress/plain.go

@@ -23,8 +23,9 @@ import (
 )
 
 type plainWriter struct {
-	out  io.Writer
-	done chan bool
+	out    io.Writer
+	done   chan bool
+	dryRun bool
 }
 
 func (p *plainWriter) Start(ctx context.Context) error {
@@ -37,7 +38,11 @@ func (p *plainWriter) Start(ctx context.Context) error {
 }
 
 func (p *plainWriter) Event(e Event) {
-	fmt.Fprintln(p.out, e.ID, e.Text, e.StatusText)
+	prefix := ""
+	if p.dryRun {
+		prefix = "DRY RUN MODE - "
+	}
+	fmt.Fprintln(p.out, prefix, e.ID, e.Text, e.StatusText)
 }
 
 func (p *plainWriter) Events(events []Event) {
@@ -47,7 +52,11 @@ func (p *plainWriter) Events(events []Event) {
 }
 
 func (p *plainWriter) TailMsgf(m string, args ...interface{}) {
-	fmt.Fprintln(p.out, append([]interface{}{m}, args...)...)
+	prefix := ""
+	if p.dryRun {
+		prefix = DRYRUN_PREFIX
+	}
+	fmt.Fprintln(p.out, append([]interface{}{prefix, m}, args...)...)
 }
 
 func (p *plainWriter) Stop() {

+ 15 - 5
pkg/progress/tty.go

@@ -40,6 +40,7 @@ type ttyWriter struct {
 	done       chan bool
 	mtx        *sync.Mutex
 	tailEvents []string
+	dryRun     bool
 }
 
 func (w *ttyWriter) Start(ctx context.Context) error {
@@ -107,7 +108,11 @@ func (w *ttyWriter) Events(events []Event) {
 func (w *ttyWriter) TailMsgf(msg string, args ...interface{}) {
 	w.mtx.Lock()
 	defer w.mtx.Unlock()
-	w.tailEvents = append(w.tailEvents, fmt.Sprintf(msg, args...))
+	msgWithPrefix := msg
+	if w.dryRun {
+		msgWithPrefix = strings.TrimSpace(DRYRUN_PREFIX + msg)
+	}
+	w.tailEvents = append(w.tailEvents, fmt.Sprintf(msgWithPrefix, args...))
 }
 
 func (w *ttyWriter) printTailEvents() {
@@ -167,7 +172,7 @@ func (w *ttyWriter) print() { //nolint:gocyclo
 		if event.ParentID != "" {
 			continue
 		}
-		line := lineText(event, "", terminalWidth, statusPadding, runtime.GOOS != "windows")
+		line := lineText(event, "", terminalWidth, statusPadding, runtime.GOOS != "windows", w.dryRun)
 		fmt.Fprint(w.out, line)
 		numLines++
 		for _, v := range w.eventIDs {
@@ -176,7 +181,7 @@ func (w *ttyWriter) print() { //nolint:gocyclo
 				if skipChildEvents {
 					continue
 				}
-				line := lineText(ev, "  ", terminalWidth, statusPadding, runtime.GOOS != "windows")
+				line := lineText(ev, "  ", terminalWidth, statusPadding, runtime.GOOS != "windows", w.dryRun)
 				fmt.Fprint(w.out, line)
 				numLines++
 			}
@@ -191,7 +196,7 @@ func (w *ttyWriter) print() { //nolint:gocyclo
 	w.numLines = numLines
 }
 
-func lineText(event Event, pad string, terminalWidth, statusPadding int, color bool) string {
+func lineText(event Event, pad string, terminalWidth, statusPadding int, color bool, dryRun bool) string {
 	endTime := time.Now()
 	if event.Status != Working {
 		endTime = event.startTime
@@ -199,6 +204,10 @@ func lineText(event Event, pad string, terminalWidth, statusPadding int, color b
 			endTime = event.endTime
 		}
 	}
+	prefix := ""
+	if dryRun {
+		prefix = DRYRUN_PREFIX
+	}
 
 	elapsed := endTime.Sub(event.startTime).Seconds()
 
@@ -215,9 +224,10 @@ func lineText(event Event, pad string, terminalWidth, statusPadding int, color b
 	if maxStatusLen > 0 && len(status) > maxStatusLen {
 		status = status[:maxStatusLen] + "..."
 	}
-	text := fmt.Sprintf("%s %s %s %s%s %s",
+	text := fmt.Sprintf("%s %s%s %s %s%s %s",
 		pad,
 		event.spinner.String(),
+		prefix,
 		event.ID,
 		event.Text,
 		strings.Repeat(" ", padding),

+ 6 - 6
pkg/progress/tty_test.go

@@ -41,22 +41,22 @@ func TestLineText(t *testing.T) {
 
 	lineWidth := len(fmt.Sprintf("%s %s", ev.ID, ev.Text))
 
-	out := lineText(ev, "", 50, lineWidth, true)
+	out := lineText(ev, "", 50, lineWidth, true, false)
 	assert.Equal(t, out, "\x1b[37m . id Text Status                            0.0s\n\x1b[0m")
 
-	out = lineText(ev, "", 50, lineWidth, false)
+	out = lineText(ev, "", 50, lineWidth, false, false)
 	assert.Equal(t, out, " . id Text Status                            0.0s\n")
 
 	ev.Status = Done
-	out = lineText(ev, "", 50, lineWidth, true)
+	out = lineText(ev, "", 50, lineWidth, true, false)
 	assert.Equal(t, out, "\x1b[34m . id Text Status                            0.0s\n\x1b[0m")
 
 	ev.Status = Error
-	out = lineText(ev, "", 50, lineWidth, true)
+	out = lineText(ev, "", 50, lineWidth, true, false)
 	assert.Equal(t, out, "\x1b[31m . id Text Status                            0.0s\n\x1b[0m")
 
 	ev.Status = Warning
-	out = lineText(ev, "", 50, lineWidth, true)
+	out = lineText(ev, "", 50, lineWidth, true, false)
 	assert.Equal(t, out, "\x1b[33m . id Text Status                            0.0s\n\x1b[0m")
 }
 
@@ -75,7 +75,7 @@ func TestLineTextSingleEvent(t *testing.T) {
 
 	lineWidth := len(fmt.Sprintf("%s %s", ev.ID, ev.Text))
 
-	out := lineText(ev, "", 50, lineWidth, true)
+	out := lineText(ev, "", 50, lineWidth, true, false)
 	assert.Equal(t, out, "\x1b[34m . id Text Status                            0.0s\n\x1b[0m")
 }
 

+ 19 - 7
pkg/progress/writer.go

@@ -21,11 +21,17 @@ import (
 	"os"
 	"sync"
 
+	"github.com/docker/compose/v2/pkg/api"
+
 	"github.com/containerd/console"
 	"github.com/moby/term"
 	"golang.org/x/sync/errgroup"
 )
 
+const (
+	DRYRUN_PREFIX = " DRY-RUN MODE - "
+)
+
 // Writer can write multiple progress events
 type Writer interface {
 	Start(context.Context) error
@@ -66,7 +72,7 @@ func Run(ctx context.Context, pf progressFunc) error {
 // RunWithStatus will run a writer and the progress function in parallel and return a status
 func RunWithStatus(ctx context.Context, pf progressFuncWithStatus) (string, error) {
 	eg, _ := errgroup.WithContext(ctx)
-	w, err := NewWriter(os.Stderr)
+	w, err := NewWriter(ctx, os.Stderr)
 	var result string
 	if err != nil {
 		return "", err
@@ -103,21 +109,26 @@ const (
 var Mode = ModeAuto
 
 // NewWriter returns a new multi-progress writer
-func NewWriter(out console.File) (Writer, error) {
+func NewWriter(ctx context.Context, out console.File) (Writer, error) {
 	_, isTerminal := term.GetFdInfo(out)
+	dryRun, ok := ctx.Value(api.DryRunKey{}).(bool)
+	if !ok {
+		dryRun = false
+	}
 	if Mode == ModeAuto && isTerminal {
-		return newTTYWriter(out)
+		return newTTYWriter(out, dryRun)
 	}
 	if Mode == ModeTTY {
-		return newTTYWriter(out)
+		return newTTYWriter(out, dryRun)
 	}
 	return &plainWriter{
-		out:  out,
-		done: make(chan bool),
+		out:    out,
+		done:   make(chan bool),
+		dryRun: dryRun,
 	}, nil
 }
 
-func newTTYWriter(out console.File) (Writer, error) {
+func newTTYWriter(out console.File, dryRun bool) (Writer, error) {
 	con, err := console.ConsoleFromFile(out)
 	if err != nil {
 		return nil, err
@@ -130,5 +141,6 @@ func newTTYWriter(out console.File) (Writer, error) {
 		repeated: false,
 		done:     make(chan bool),
 		mtx:      &sync.Mutex{},
+		dryRun:   dryRun,
 	}, nil
 }