mirror of https://github.com/docker/compose.git
Merge pull request #10210 from glours/dry-run-kill-support
Dry run kill support
This commit is contained in:
commit
a93f09efac
|
@ -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
|
||||
},
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
@ -61,7 +63,7 @@ func (d *DryRunClient) ContainerCreate(ctx context.Context, config *containerTyp
|
|||
}
|
||||
|
||||
func (d *DryRunClient) ContainerKill(ctx context.Context, container, signal string) error {
|
||||
return ErrNotImplemented
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *DryRunClient) ContainerPause(ctx context.Context, container string) error {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue