@@ -21,6 +21,8 @@ import (
21
21
"errors"
22
22
"fmt"
23
23
"net/url"
24
+ "runtime"
25
+ "sync"
24
26
"time"
25
27
26
28
mobyterm "github.com/moby/term"
@@ -109,6 +111,20 @@ var runtimeExecCommand = &cli.Command{
109
111
Aliases : []string {"n" },
110
112
Usage : "Exec command for all last n containers, set to 0 for unlimited" ,
111
113
},
114
+ & cli.BoolFlag {
115
+ Name : "quiet" ,
116
+ Aliases : []string {"q" },
117
+ Usage : "Do not print the container ID if multiple containers are selected" ,
118
+ },
119
+ & cli.BoolFlag {
120
+ Name : "ignore-error" ,
121
+ Aliases : []string {"e" },
122
+ Usage : "Ignore any error during command execution" ,
123
+ },
124
+ & cli.BoolFlag {
125
+ Name : "parallel" ,
126
+ Usage : "Run the command in parallel if multiple containers are selected" ,
127
+ },
112
128
},
113
129
Action : func (c * cli.Context ) error {
114
130
if c .NArg () < 1 {
@@ -137,7 +153,7 @@ var runtimeExecCommand = &cli.Command{
137
153
138
154
ids = []string {}
139
155
cmd = c .Args ().Slice ()
140
- outputContainerID = true
156
+ outputContainerID = ! c . Bool ( "quiet" ) && ! c . Bool ( "parallel" )
141
157
142
158
opts := & listOptions {
143
159
nameRegexp : c .String ("name" ),
@@ -178,37 +194,69 @@ var runtimeExecCommand = &cli.Command{
178
194
transport : c .String (transportFlag ),
179
195
}
180
196
181
- for _ , id := range ids {
182
- opts .id = id
197
+ maxParallel := 1
198
+ if c .Bool ("parallel" ) {
199
+ maxParallel = runtime .NumCPU ()
200
+ }
201
+
202
+ results := mapParallel (ids , maxParallel , func (id string ) error {
203
+ optsCopy := * & opts
204
+ optsCopy .id = id
183
205
184
206
if outputContainerID {
185
207
fmt .Println (id + ":" )
186
208
}
187
-
188
209
if c .Bool ("sync" ) {
189
- exitCode , err := ExecSync (runtimeClient , opts )
210
+ exitCode , err := ExecSync (runtimeClient , optsCopy )
190
211
if err != nil {
191
- return fmt .Errorf ("execing command in container synchronously: %w" , err )
212
+ return fmt .Errorf ("execing command in container %s synchronously: %w" , id , err )
192
213
}
193
214
if exitCode != 0 {
194
215
return cli .Exit ("non-zero exit code" , exitCode )
195
216
}
196
- continue
217
+ } else {
218
+ ctx , cancel := context .WithCancel (c .Context )
219
+ defer cancel ()
220
+ err = Exec (ctx , runtimeClient , optsCopy )
221
+ if err != nil {
222
+ return fmt .Errorf ("execing command in container %s: %w" , id , err )
223
+ }
197
224
}
198
225
199
- ctx , cancel := context .WithCancel (c .Context )
200
- defer cancel ()
201
- err = Exec (ctx , runtimeClient , opts )
202
- if err != nil {
203
- return fmt .Errorf ("execing command in container: %w" , err )
204
- }
226
+ return nil
227
+ })
205
228
229
+ if c .Bool ("ignore-error" ) {
230
+ return nil
206
231
}
207
232
208
- return nil
233
+ return errors . Join ( results ... )
209
234
},
210
235
}
211
236
237
+ func mapParallel [T1 any , T2 any ](input []T1 , maxParallel int , fn func (T1 ) T2 ) []T2 {
238
+ wg := & sync.WaitGroup {}
239
+ wg .Add (len (input ))
240
+
241
+ results := make ([]T2 , len (input ))
242
+ maxParallelChan := make (chan struct {}, maxParallel )
243
+
244
+ for i := range input {
245
+ maxParallelChan <- struct {}{}
246
+ go func (index int , x T1 ) {
247
+ defer wg .Done ()
248
+
249
+ result := fn (x )
250
+ results [index ] = result
251
+
252
+ <- maxParallelChan
253
+ }(i , input [i ])
254
+ }
255
+
256
+ wg .Wait ()
257
+ return results
258
+ }
259
+
212
260
// ExecSync sends an ExecSyncRequest to the server, and parses
213
261
// the returned ExecSyncResponse. The function returns the corresponding exit
214
262
// code beside an general error.
0 commit comments