Skip to content

Commit eb48cad

Browse files
authored
Merge pull request #5848 from thaJeztah/improve_swarm_completion
completion: fix / add completion for service names and node-names
2 parents 2493a96 + 8f55738 commit eb48cad

File tree

16 files changed

+174
-9
lines changed

16 files changed

+174
-9
lines changed

cli/command/node/completion.go

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
package node
2+
3+
import (
4+
"os"
5+
6+
"github.com/docker/cli/cli/command/completion"
7+
"github.com/docker/docker/api/types"
8+
"github.com/spf13/cobra"
9+
)
10+
11+
// completeNodeNames offers completion for swarm node (host)names and optional IDs.
12+
// By default, only names are returned.
13+
// Set DOCKER_COMPLETION_SHOW_NODE_IDS=yes to also complete IDs.
14+
//
15+
// TODO(thaJeztah): add support for filters.
16+
func completeNodeNames(dockerCLI completion.APIClientProvider) completion.ValidArgsFn {
17+
// https://github.com/docker/cli/blob/f9ced58158d5e0b358052432244b483774a1983d/contrib/completion/bash/docker#L41-L43
18+
showIDs := os.Getenv("DOCKER_COMPLETION_SHOW_NODE_IDS") == "yes"
19+
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
20+
list, err := dockerCLI.Client().NodeList(cmd.Context(), types.NodeListOptions{})
21+
if err != nil {
22+
return nil, cobra.ShellCompDirectiveError
23+
}
24+
25+
names := make([]string, 0, len(list)+1)
26+
for _, node := range list {
27+
if showIDs {
28+
names = append(names, node.Description.Hostname, node.ID)
29+
} else {
30+
names = append(names, node.Description.Hostname)
31+
}
32+
}
33+
// Nodes allow "self" as magic word for the current node.
34+
names = append(names, "self")
35+
return names, cobra.ShellCompDirectiveNoFileComp
36+
}
37+
}

cli/command/node/demote.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ func newDemoteCommand(dockerCli command.Cli) *cobra.Command {
1818
RunE: func(cmd *cobra.Command, args []string) error {
1919
return runDemote(cmd.Context(), dockerCli, args)
2020
},
21+
ValidArgsFunction: completeNodeNames(dockerCli),
2122
}
2223
}
2324

cli/command/node/inspect.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ func newInspectCommand(dockerCli command.Cli) *cobra.Command {
3232
opts.nodeIds = args
3333
return runInspect(cmd.Context(), dockerCli, opts)
3434
},
35+
ValidArgsFunction: completeNodeNames(dockerCli),
3536
}
3637

3738
flags := cmd.Flags()

cli/command/node/list.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/docker/docker/api/types/system"
1515
"github.com/fvbommel/sortorder"
1616
"github.com/spf13/cobra"
17+
"github.com/spf13/pflag"
1718
)
1819

1920
type listOptions struct {
@@ -40,6 +41,12 @@ func newListCommand(dockerCli command.Cli) *cobra.Command {
4041
flags.StringVar(&options.format, "format", "", flagsHelper.FormatHelp)
4142
flags.VarP(&options.filter, "filter", "f", "Filter output based on conditions provided")
4243

44+
flags.VisitAll(func(flag *pflag.Flag) {
45+
// Set a default completion function if none was set. We don't look
46+
// up if it does already have one set, because Cobra does this for
47+
// us, and returns an error (which we ignore for this reason).
48+
_ = cmd.RegisterFlagCompletionFunc(flag.Name, completion.NoComplete)
49+
})
4350
return cmd
4451
}
4552

cli/command/node/promote.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ func newPromoteCommand(dockerCli command.Cli) *cobra.Command {
1818
RunE: func(cmd *cobra.Command, args []string) error {
1919
return runPromote(cmd.Context(), dockerCli, args)
2020
},
21+
ValidArgsFunction: completeNodeNames(dockerCli),
2122
}
2223
}
2324

cli/command/node/ps.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import (
1414
"github.com/docker/docker/api/types/swarm"
1515
"github.com/pkg/errors"
1616
"github.com/spf13/cobra"
17+
"github.com/spf13/pflag"
1718
)
1819

1920
type psOptions struct {
@@ -41,7 +42,7 @@ func newPsCommand(dockerCli command.Cli) *cobra.Command {
4142

4243
return runPs(cmd.Context(), dockerCli, options)
4344
},
44-
ValidArgsFunction: completion.NoComplete,
45+
ValidArgsFunction: completeNodeNames(dockerCli),
4546
}
4647
flags := cmd.Flags()
4748
flags.BoolVar(&options.noTrunc, "no-trunc", false, "Do not truncate output")
@@ -50,6 +51,12 @@ func newPsCommand(dockerCli command.Cli) *cobra.Command {
5051
flags.StringVar(&options.format, "format", "", "Pretty-print tasks using a Go template")
5152
flags.BoolVarP(&options.quiet, "quiet", "q", false, "Only display task IDs")
5253

54+
flags.VisitAll(func(flag *pflag.Flag) {
55+
// Set a default completion function if none was set. We don't look
56+
// up if it does already have one set, because Cobra does this for
57+
// us, and returns an error (which we ignore for this reason).
58+
_ = cmd.RegisterFlagCompletionFunc(flag.Name, completion.NoComplete)
59+
})
5360
return cmd
5461
}
5562

cli/command/node/remove.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ func newRemoveCommand(dockerCli command.Cli) *cobra.Command {
2626
RunE: func(cmd *cobra.Command, args []string) error {
2727
return runRemove(cmd.Context(), dockerCli, args, opts)
2828
},
29+
ValidArgsFunction: completeNodeNames(dockerCli),
2930
}
3031
flags := cmd.Flags()
3132
flags.BoolVarP(&opts.force, "force", "f", false, "Force remove a node from the swarm")

cli/command/node/update.go

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/docker/cli/cli"
88
"github.com/docker/cli/cli/command"
9+
"github.com/docker/cli/cli/command/completion"
910
"github.com/docker/cli/opts"
1011
"github.com/docker/docker/api/types/swarm"
1112
"github.com/pkg/errors"
@@ -25,6 +26,7 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
2526
RunE: func(cmd *cobra.Command, args []string) error {
2627
return runUpdate(cmd.Context(), dockerCli, cmd.Flags(), args[0])
2728
},
29+
ValidArgsFunction: completeNodeNames(dockerCli),
2830
}
2931

3032
flags := cmd.Flags()
@@ -33,6 +35,15 @@ func newUpdateCommand(dockerCli command.Cli) *cobra.Command {
3335
flags.Var(&options.annotations.labels, flagLabelAdd, `Add or update a node label ("key=value")`)
3436
labelKeys := opts.NewListOpts(nil)
3537
flags.Var(&labelKeys, flagLabelRemove, "Remove a node label if exists")
38+
39+
_ = cmd.RegisterFlagCompletionFunc(flagRole, completion.FromList("worker", "manager"))
40+
_ = cmd.RegisterFlagCompletionFunc(flagAvailability, completion.FromList("active", "pause", "drain"))
41+
flags.VisitAll(func(flag *pflag.Flag) {
42+
// Set a default completion function if none was set. We don't look
43+
// up if it does already have one set, because Cobra does this for
44+
// us, and returns an error (which we ignore for this reason).
45+
_ = cmd.RegisterFlagCompletionFunc(flag.Name, completion.NoComplete)
46+
})
3647
return cmd
3748
}
3849

cli/command/service/cmd.go

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package service
22

33
import (
4+
"os"
5+
46
"github.com/docker/cli/cli"
57
"github.com/docker/cli/cli/command"
68
"github.com/docker/cli/cli/command/completion"
@@ -34,16 +36,25 @@ func NewServiceCommand(dockerCli command.Cli) *cobra.Command {
3436
return cmd
3537
}
3638

37-
// CompletionFn offers completion for swarm services
39+
// CompletionFn offers completion for swarm service names and optional IDs.
40+
// By default, only names are returned.
41+
// Set DOCKER_COMPLETION_SHOW_SERVICE_IDS=yes to also complete IDs.
3842
func CompletionFn(dockerCLI completion.APIClientProvider) completion.ValidArgsFn {
43+
// https://github.com/docker/cli/blob/f9ced58158d5e0b358052432244b483774a1983d/contrib/completion/bash/docker#L41-L43
44+
showIDs := os.Getenv("DOCKER_COMPLETION_SHOW_SERVICE_IDS") == "yes"
3945
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
4046
list, err := dockerCLI.Client().ServiceList(cmd.Context(), types.ServiceListOptions{})
4147
if err != nil {
4248
return nil, cobra.ShellCompDirectiveError
4349
}
44-
var names []string
50+
51+
names := make([]string, 0, len(list))
4552
for _, service := range list {
46-
names = append(names, service.ID)
53+
if showIDs {
54+
names = append(names, service.Spec.Name, service.ID)
55+
} else {
56+
names = append(names, service.Spec.Name)
57+
}
4758
}
4859
return names, cobra.ShellCompDirectiveNoFileComp
4960
}

cli/command/service/create.go

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import (
1616
"github.com/spf13/pflag"
1717
)
1818

19-
func newCreateCommand(dockerCli command.Cli) *cobra.Command {
19+
func newCreateCommand(dockerCLI command.Cli) *cobra.Command {
2020
opts := newServiceOptions()
2121

2222
cmd := &cobra.Command{
@@ -28,7 +28,7 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
2828
if len(args) > 1 {
2929
opts.args = args[1:]
3030
}
31-
return runCreate(cmd.Context(), dockerCli, cmd.Flags(), opts)
31+
return runCreate(cmd.Context(), dockerCLI, cmd.Flags(), opts)
3232
},
3333
ValidArgsFunction: completion.NoComplete,
3434
}
@@ -75,6 +75,28 @@ func newCreateCommand(dockerCli command.Cli) *cobra.Command {
7575
flags.SetAnnotation(flagHostAdd, "version", []string{"1.32"})
7676

7777
flags.SetInterspersed(false)
78+
79+
// TODO(thaJeztah): add completion for capabilities, stop-signal (currently non-exported in container package)
80+
// _ = cmd.RegisterFlagCompletionFunc(flagCapAdd, completeLinuxCapabilityNames)
81+
// _ = cmd.RegisterFlagCompletionFunc(flagCapDrop, completeLinuxCapabilityNames)
82+
// _ = cmd.RegisterFlagCompletionFunc(flagStopSignal, completeSignals)
83+
84+
_ = cmd.RegisterFlagCompletionFunc(flagMode, completion.FromList("replicated", "global", "replicated-job", "global-job"))
85+
_ = cmd.RegisterFlagCompletionFunc(flagEnv, completion.EnvVarNames) // TODO(thaJeztah): flagEnvRemove (needs to read current env-vars on the service)
86+
_ = cmd.RegisterFlagCompletionFunc(flagEnvFile, completion.FileNames)
87+
_ = cmd.RegisterFlagCompletionFunc(flagNetwork, completion.NetworkNames(dockerCLI))
88+
_ = cmd.RegisterFlagCompletionFunc(flagRestartCondition, completion.FromList("none", "on-failure", "any"))
89+
_ = cmd.RegisterFlagCompletionFunc(flagRollbackOrder, completion.FromList("start-first", "stop-first"))
90+
_ = cmd.RegisterFlagCompletionFunc(flagRollbackFailureAction, completion.FromList("pause", "continue"))
91+
_ = cmd.RegisterFlagCompletionFunc(flagUpdateOrder, completion.FromList("start-first", "stop-first"))
92+
_ = cmd.RegisterFlagCompletionFunc(flagUpdateFailureAction, completion.FromList("pause", "continue", "rollback"))
93+
94+
flags.VisitAll(func(flag *pflag.Flag) {
95+
// Set a default completion function if none was set. We don't look
96+
// up if it does already have one set, because Cobra does this for
97+
// us, and returns an error (which we ignore for this reason).
98+
_ = cmd.RegisterFlagCompletionFunc(flag.Name, completion.NoComplete)
99+
})
78100
return cmd
79101
}
80102

0 commit comments

Comments
 (0)