Skip to content

Commit 6f4cc16

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 6f4cc16

File tree

1 file changed

+68
-14
lines changed

1 file changed

+68
-14
lines changed

cmd/crictl/exec.go

+68-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-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+
},
112128
},
113129
Action: func(c *cli.Context) error {
114130
if c.NArg() < 1 {
@@ -124,6 +140,7 @@ var runtimeExecCommand = &cli.Command{
124140
ids := []string{c.Args().First()}
125141
cmd := c.Args().Slice()[1:]
126142
outputContainerID := false
143+
quiet := c.Bool("quiet")
127144

128145
// If any of the filter flags are set, then we assume that no
129146
// CONTAINER-ID is provided as CLI parameter.
@@ -137,7 +154,7 @@ var runtimeExecCommand = &cli.Command{
137154

138155
ids = []string{}
139156
cmd = c.Args().Slice()
140-
outputContainerID = true
157+
outputContainerID = !quiet && !c.Bool("parallel")
141158

142159
opts := &listOptions{
143160
nameRegexp: c.String("name"),
@@ -178,37 +195,74 @@ var runtimeExecCommand = &cli.Command{
178195
transport: c.String(transportFlag),
179196
}
180197

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
183206

184207
if outputContainerID {
185208
fmt.Println(id + ":")
186209
}
187-
188210
if c.Bool("sync") {
189-
exitCode, err := ExecSync(runtimeClient, opts)
211+
exitCode, err := ExecSync(runtimeClient, optsCopy)
190212
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)
192214
}
193215
if exitCode != 0 {
194216
return cli.Exit("non-zero exit code", exitCode)
195217
}
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+
}
197225
}
198226

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...)
205231

232+
if c.Bool("ignore-errors") {
233+
if !quiet {
234+
logrus.Warnf("Ignoring errors: %v", errs)
235+
}
236+
return nil
206237
}
207238

208-
return nil
239+
return errs
209240
},
210241
}
211242

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

0 commit comments

Comments
 (0)