Skip to content

Commit 58bfaa2

Browse files
committed
crictl exec: add --quiet/-q, --ignore-error/-e and --parallel flags
The flags can be used to further manipulate on the exec behavior. Follow-up on: kubernetes-sigs#1603 (comment) Signed-off-by: Sascha Grunert <sgrunert@redhat.com>
1 parent 9413e9d commit 58bfaa2

File tree

1 file changed

+62
-14
lines changed

1 file changed

+62
-14
lines changed

cmd/crictl/exec.go

+62-14
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import (
2121
"errors"
2222
"fmt"
2323
"net/url"
24+
"runtime"
25+
"sync"
2426
"time"
2527

2628
mobyterm "github.com/moby/term"
@@ -109,6 +111,20 @@ var runtimeExecCommand = &cli.Command{
109111
Aliases: []string{"n"},
110112
Usage: "Exec command for all last n containers, set to 0 for unlimited",
111113
},
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+
},
112128
},
113129
Action: func(c *cli.Context) error {
114130
if c.NArg() < 1 {
@@ -137,7 +153,7 @@ var runtimeExecCommand = &cli.Command{
137153

138154
ids = []string{}
139155
cmd = c.Args().Slice()
140-
outputContainerID = true
156+
outputContainerID = !c.Bool("quiet") && !c.Bool("parallel")
141157

142158
opts := &listOptions{
143159
nameRegexp: c.String("name"),
@@ -178,37 +194,69 @@ var runtimeExecCommand = &cli.Command{
178194
transport: c.String(transportFlag),
179195
}
180196

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
183205

184206
if outputContainerID {
185207
fmt.Println(id + ":")
186208
}
187-
188209
if c.Bool("sync") {
189-
exitCode, err := ExecSync(runtimeClient, opts)
210+
exitCode, err := ExecSync(runtimeClient, optsCopy)
190211
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)
192213
}
193214
if exitCode != 0 {
194215
return cli.Exit("non-zero exit code", exitCode)
195216
}
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+
}
197224
}
198225

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+
})
205228

229+
if c.Bool("ignore-error") {
230+
return nil
206231
}
207232

208-
return nil
233+
return errors.Join(results...)
209234
},
210235
}
211236

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+
212260
// ExecSync sends an ExecSyncRequest to the server, and parses
213261
// the returned ExecSyncResponse. The function returns the corresponding exit
214262
// code beside an general error.

0 commit comments

Comments
 (0)