@@ -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-errors" ,
121
+ Aliases : []string {"e" },
122
+ Usage : "Ignore errors 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 {
@@ -124,6 +140,7 @@ var runtimeExecCommand = &cli.Command{
124
140
ids := []string {c .Args ().First ()}
125
141
cmd := c .Args ().Slice ()[1 :]
126
142
outputContainerID := false
143
+ quiet := c .Bool ("quiet" )
127
144
128
145
// If any of the filter flags are set, then we assume that no
129
146
// CONTAINER-ID is provided as CLI parameter.
@@ -137,7 +154,7 @@ var runtimeExecCommand = &cli.Command{
137
154
138
155
ids = []string {}
139
156
cmd = c .Args ().Slice ()
140
- outputContainerID = true
157
+ outputContainerID = ! quiet && ! c . Bool ( "parallel" )
141
158
142
159
opts := & listOptions {
143
160
nameRegexp : c .String ("name" ),
@@ -178,37 +195,74 @@ var runtimeExecCommand = &cli.Command{
178
195
transport : c .String (transportFlag ),
179
196
}
180
197
181
- for _ , id := range ids {
182
- opts .id = id
198
+ maxParallel := 1
199
+ if c .Bool ("parallel" ) {
200
+ maxParallel = runtime .NumCPU ()
201
+ }
202
+
203
+ results := mapParallel (ids , maxParallel , func (id string ) error {
204
+ optsCopy := * & opts
205
+ optsCopy .id = id
183
206
184
207
if outputContainerID {
185
208
fmt .Println (id + ":" )
186
209
}
187
-
188
210
if c .Bool ("sync" ) {
189
- exitCode , err := ExecSync (runtimeClient , opts )
211
+ exitCode , err := ExecSync (runtimeClient , optsCopy )
190
212
if err != nil {
191
- return fmt .Errorf ("execing command in container synchronously: %w" , err )
213
+ return fmt .Errorf ("execing command in container %s synchronously: %w" , id , err )
192
214
}
193
215
if exitCode != 0 {
194
216
return cli .Exit ("non-zero exit code" , exitCode )
195
217
}
196
- continue
218
+ } else {
219
+ ctx , cancel := context .WithCancel (c .Context )
220
+ defer cancel ()
221
+ err = Exec (ctx , runtimeClient , optsCopy )
222
+ if err != nil {
223
+ return fmt .Errorf ("execing command in container %s: %w" , id , err )
224
+ }
197
225
}
198
226
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
- }
227
+ return nil
228
+ })
229
+
230
+ errs := errors .Join (results ... )
205
231
232
+ if c .Bool ("ignore-errors" ) {
233
+ if ! quiet {
234
+ logrus .Warnf ("Ignoring errors: %v" , errs )
235
+ }
236
+ return nil
206
237
}
207
238
208
- return nil
239
+ return errs
209
240
},
210
241
}
211
242
243
+ func mapParallel [T1 any , T2 any ](input []T1 , maxParallel int , fn func (T1 ) T2 ) []T2 {
244
+ wg := & sync.WaitGroup {}
245
+ wg .Add (len (input ))
246
+
247
+ results := make ([]T2 , len (input ))
248
+ maxParallelChan := make (chan struct {}, maxParallel )
249
+
250
+ for i := range input {
251
+ maxParallelChan <- struct {}{}
252
+ go func (index int , x T1 ) {
253
+ defer wg .Done ()
254
+
255
+ result := fn (x )
256
+ results [index ] = result
257
+
258
+ <- maxParallelChan
259
+ }(i , input [i ])
260
+ }
261
+
262
+ wg .Wait ()
263
+ return results
264
+ }
265
+
212
266
// ExecSync sends an ExecSyncRequest to the server, and parses
213
267
// the returned ExecSyncResponse. The function returns the corresponding exit
214
268
// code beside an general error.
0 commit comments