@@ -163,8 +163,11 @@ func newDockerCommand(dockerCli *command.DockerCli) *cli.TopLevelCommand {
163
163
cmd .SetOut (dockerCli .Out ())
164
164
commands .AddCommands (cmd , dockerCli )
165
165
166
- cli .DisableFlagsInUseLine (cmd )
167
- setValidateArgs (dockerCli , cmd )
166
+ visitAll (cmd ,
167
+ setValidateArgs (dockerCli ),
168
+ // prevent adding "[flags]" to the end of the usage line.
169
+ func (c * cobra.Command ) { c .DisableFlagsInUseLine = true },
170
+ )
168
171
169
172
// flags must be the top-level command flags, not cmd.Flags()
170
173
return cli .NewTopLevelCommand (cmd , dockerCli , opts , cmd .Flags ())
@@ -265,14 +268,29 @@ func setHelpFunc(dockerCli command.Cli, cmd *cobra.Command) {
265
268
})
266
269
}
267
270
268
- func setValidateArgs (dockerCli command.Cli , cmd * cobra.Command ) {
269
- // The Args is handled by ValidateArgs in cobra, which does not allows a pre-hook.
270
- // As a result, here we replace the existing Args validation func to a wrapper,
271
- // where the wrapper will check to see if the feature is supported or not.
272
- // The Args validation error will only be returned if the feature is supported.
273
- cli .VisitAll (cmd , func (ccmd * cobra.Command ) {
271
+ // visitAll traverses all commands from the root.
272
+ func visitAll (root * cobra.Command , fns ... func (* cobra.Command )) {
273
+ for _ , cmd := range root .Commands () {
274
+ visitAll (cmd , fns ... )
275
+ }
276
+ for _ , fn := range fns {
277
+ fn (root )
278
+ }
279
+ }
280
+
281
+ // The Args is handled by ValidateArgs in cobra, which does not allows a pre-hook.
282
+ // As a result, here we replace the existing Args validation func to a wrapper,
283
+ // where the wrapper will check to see if the feature is supported or not.
284
+ // The Args validation error will only be returned if the feature is supported.
285
+ func setValidateArgs (dockerCLI versionDetails ) func (* cobra.Command ) {
286
+ return func (ccmd * cobra.Command ) {
274
287
// if there is no tags for a command or any of its parent,
275
288
// there is no need to wrap the Args validation.
289
+ //
290
+ // FIXME(thaJeztah): can we memoize properties of the parent?
291
+ // visitAll traverses root -> all childcommands, and hasTags
292
+ // goes the reverse (cmd -> visit all parents), so we may
293
+ // end traversing two directions.
276
294
if ! hasTags (ccmd ) {
277
295
return
278
296
}
@@ -283,12 +301,12 @@ func setValidateArgs(dockerCli command.Cli, cmd *cobra.Command) {
283
301
284
302
cmdArgs := ccmd .Args
285
303
ccmd .Args = func (cmd * cobra.Command , args []string ) error {
286
- if err := isSupported (cmd , dockerCli ); err != nil {
304
+ if err := isSupported (cmd , dockerCLI ); err != nil {
287
305
return err
288
306
}
289
307
return cmdArgs (cmd , args )
290
308
}
291
- })
309
+ }
292
310
}
293
311
294
312
func tryPluginRun (ctx context.Context , dockerCli command.Cli , cmd * cobra.Command , subcommand string , envs []string ) error {
@@ -321,19 +339,19 @@ func tryPluginRun(ctx context.Context, dockerCli command.Cli, cmd *cobra.Command
321
339
// signals to the subprocess because the shared
322
340
// pgid makes the TTY a controlling terminal.
323
341
//
324
- // The plugin should have it's own copy of this
342
+ // The plugin should have its own copy of this
325
343
// termination logic, and exit after 3 retries
326
- // on it's own.
344
+ // on its own.
327
345
if dockerCli .Out ().IsTerminal () {
328
346
return
329
347
}
330
348
331
- // Terminate the plugin server, which will
332
- // close all connections with plugin
333
- // subprocesses, and signal them to exit.
349
+ // Terminate the plugin server, which closes
350
+ // all connections with plugin subprocesses,
351
+ // and signal them to exit.
334
352
//
335
- // Repeated invocations will result in EINVAL,
336
- // or EBADF; but that is fine for our purposes.
353
+ // Repeated invocations result in EINVAL or EBADF ,
354
+ // but that is fine for our purposes.
337
355
if srv != nil {
338
356
_ = srv .Close ()
339
357
}
@@ -351,15 +369,15 @@ func tryPluginRun(ctx context.Context, dockerCli command.Cli, cmd *cobra.Command
351
369
352
370
go func () {
353
371
retries := 0
354
- force := false
355
372
// catch the first signal through context cancellation
356
373
<- ctx .Done ()
357
- tryTerminatePlugin (force )
374
+ tryTerminatePlugin (false )
358
375
359
376
// register subsequent signals
360
377
signals := make (chan os.Signal , exitLimit )
361
378
signal .Notify (signals , platformsignals .TerminationSignals ... )
362
379
380
+ force := false
363
381
for range signals {
364
382
retries ++
365
383
// If we're still running after 3 interruptions
@@ -440,7 +458,7 @@ func runDocker(ctx context.Context, dockerCli *command.DockerCli) error {
440
458
}
441
459
}()
442
460
} else {
443
- fmt .Fprint (dockerCli .Err (), "Warning: Unexpected OTEL error, metrics may not be flushed" )
461
+ _ , _ = fmt .Fprint (dockerCli .Err (), "Warning: Unexpected OTEL error, metrics may not be flushed" )
444
462
}
445
463
446
464
dockerCli .InstrumentCobraCommands (ctx , cmd )
@@ -451,12 +469,11 @@ func runDocker(ctx context.Context, dockerCli *command.DockerCli) error {
451
469
return err
452
470
}
453
471
454
- if cli . HasCompletionArg (args ) {
472
+ if hasCompletionArg (args ) {
455
473
// We add plugin command stubs early only for completion. We don't
456
474
// want to add them for normal command execution as it would cause
457
475
// a significant performance hit.
458
- err = pluginmanager .AddPluginCommandStubs (dockerCli , cmd )
459
- if err != nil {
476
+ if err := pluginmanager .AddPluginCommandStubs (dockerCli , cmd ); err != nil {
460
477
return err
461
478
}
462
479
}
@@ -504,6 +521,16 @@ func runDocker(ctx context.Context, dockerCli *command.DockerCli) error {
504
521
return err
505
522
}
506
523
524
+ // hasCompletionArg returns true if a cobra completion arg request is found.
525
+ func hasCompletionArg (args []string ) bool {
526
+ for _ , arg := range args {
527
+ if arg == cobra .ShellCompRequestCmd || arg == cobra .ShellCompNoDescRequestCmd {
528
+ return true
529
+ }
530
+ }
531
+ return false
532
+ }
533
+
507
534
type versionDetails interface {
508
535
CurrentVersion () string
509
536
ServerInfo () command.ServerInfo
0 commit comments