@@ -19,123 +19,41 @@ package compose
19
19
import (
20
20
"context"
21
21
"fmt"
22
- "io"
23
22
23
+ "github.com/docker/cli/cli"
24
+ "github.com/docker/cli/cli/command/container"
25
+ "github.com/docker/compose/v2/pkg/api"
24
26
moby "github.com/docker/docker/api/types"
25
27
"github.com/docker/docker/api/types/filters"
26
- "github.com/docker/docker/pkg/stdcopy"
27
- "github.com/moby/term"
28
-
29
- "github.com/docker/compose/v2/pkg/api"
30
28
)
31
29
32
30
func (s * composeService ) Exec (ctx context.Context , project string , opts api.RunOptions ) (int , error ) {
33
- container , err := s .getExecTarget (ctx , project , opts )
34
- if err != nil {
35
- return 0 , err
36
- }
37
-
38
- exec , err := s .apiClient ().ContainerExecCreate (ctx , container .ID , moby.ExecConfig {
39
- Cmd : opts .Command ,
40
- Env : opts .Environment ,
41
- User : opts .User ,
42
- Privileged : opts .Privileged ,
43
- Tty : opts .Tty ,
44
- Detach : opts .Detach ,
45
- WorkingDir : opts .WorkingDir ,
46
-
47
- AttachStdin : true ,
48
- AttachStdout : true ,
49
- AttachStderr : true ,
50
- })
51
- if err != nil {
52
- return 0 , err
53
- }
54
-
55
- if opts .Detach {
56
- return 0 , s .apiClient ().ContainerExecStart (ctx , exec .ID , moby.ExecStartCheck {
57
- Detach : true ,
58
- Tty : opts .Tty ,
59
- })
60
- }
61
-
62
- resp , err := s .apiClient ().ContainerExecAttach (ctx , exec .ID , moby.ExecStartCheck {
63
- Tty : opts .Tty ,
64
- })
31
+ target , err := s .getExecTarget (ctx , project , opts )
65
32
if err != nil {
66
33
return 0 , err
67
34
}
68
- defer resp .Close () //nolint:errcheck
69
35
70
- if opts .Tty {
71
- s .monitorTTySize (ctx , exec .ID , s .apiClient ().ContainerExecResize )
36
+ exec := container .NewExecOptions ()
37
+ exec .Interactive = opts .Interactive
38
+ exec .TTY = opts .Tty
39
+ exec .Detach = opts .Detach
40
+ exec .User = opts .User
41
+ exec .Privileged = opts .Privileged
42
+ exec .Workdir = opts .WorkingDir
43
+ exec .Container = target .ID
44
+ exec .Command = opts .Command
45
+ for _ , v := range opts .Environment {
46
+ err := exec .Env .Set (v )
72
47
if err != nil {
73
48
return 0 , err
74
49
}
75
50
}
76
51
77
- err = s .interactiveExec (ctx , opts , resp )
78
- if err != nil {
79
- return 0 , err
80
- }
81
-
82
- return s .getExecExitStatus (ctx , exec .ID )
83
- }
84
-
85
- // inspired by https://github.com/docker/cli/blob/master/cli/command/container/exec.go#L116
86
- func (s * composeService ) interactiveExec (ctx context.Context , opts api.RunOptions , resp moby.HijackedResponse ) error {
87
- outputDone := make (chan error )
88
- inputDone := make (chan error )
89
-
90
- stdout := ContainerStdout {HijackedResponse : resp }
91
- stdin := ContainerStdin {HijackedResponse : resp }
92
- r , err := s .getEscapeKeyProxy (s .stdin (), opts .Tty )
93
- if err != nil {
94
- return err
95
- }
96
-
97
- in := s .stdin ()
98
- if in .IsTerminal () && opts .Tty {
99
- state , err := term .SetRawTerminal (in .FD ())
100
- if err != nil {
101
- return err
102
- }
103
- defer term .RestoreTerminal (in .FD (), state ) //nolint:errcheck
104
- }
105
-
106
- go func () {
107
- if opts .Tty {
108
- _ , err := io .Copy (s .stdout (), stdout )
109
- outputDone <- err
110
- } else {
111
- _ , err := stdcopy .StdCopy (s .stdout (), s .stderr (), stdout )
112
- outputDone <- err
113
- }
114
- stdout .Close () //nolint:errcheck
115
- }()
116
-
117
- go func () {
118
- _ , err := io .Copy (stdin , r )
119
- inputDone <- err
120
- stdin .Close () //nolint:errcheck
121
- }()
122
-
123
- for {
124
- select {
125
- case err := <- outputDone :
126
- return err
127
- case err := <- inputDone :
128
- if _ , ok := err .(term.EscapeError ); ok {
129
- return nil
130
- }
131
- if err != nil {
132
- return err
133
- }
134
- // Wait for output to complete streaming
135
- case <- ctx .Done ():
136
- return ctx .Err ()
137
- }
52
+ err = container .RunExec (s .dockerCli , exec )
53
+ if sterr , ok := err .(cli.StatusError ); ok {
54
+ return sterr .StatusCode , nil
138
55
}
56
+ return 0 , err
139
57
}
140
58
141
59
func (s * composeService ) getExecTarget (ctx context.Context , projectName string , opts api.RunOptions ) (moby.Container , error ) {
@@ -155,11 +73,3 @@ func (s *composeService) getExecTarget(ctx context.Context, projectName string,
155
73
container := containers [0 ]
156
74
return container , nil
157
75
}
158
-
159
- func (s * composeService ) getExecExitStatus (ctx context.Context , execID string ) (int , error ) {
160
- resp , err := s .apiClient ().ContainerExecInspect (ctx , execID )
161
- if err != nil {
162
- return 0 , err
163
- }
164
- return resp .ExitCode , nil
165
- }
0 commit comments