Skip to content

Commit 8bb5595

Browse files
committed
cli/command/system: add shell completion for "docker inspect"
The "docker inspect" command can inspect any type of object, which would require all possible endpoints to be contacted. By default, we don't provide completion, but if a `--type` is passed, we provide completion for the given type. For example, `docker inspect --type container` will complete container names, `docker inspect --type volume` will complete volume names and so on. Signed-off-by: Sebastiaan van Stijn <[email protected]>
1 parent f10041c commit 8bb5595

File tree

2 files changed

+121
-1
lines changed

2 files changed

+121
-1
lines changed

cli/command/system/completion.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package system
22

33
import (
4+
"fmt"
45
"strings"
56

67
"github.com/docker/cli/cli/command/completion"
8+
"github.com/docker/cli/cli/command/idresolver"
79
"github.com/moby/moby/api/types/events"
10+
"github.com/moby/moby/api/types/filters"
11+
"github.com/moby/moby/api/types/swarm"
812
"github.com/moby/moby/client"
913
"github.com/spf13/cobra"
1014
)
@@ -157,6 +161,20 @@ func validEventNames() []string {
157161
return names
158162
}
159163

164+
// configNames contacts the API to get a list of config names.
165+
// In case of an error, an empty list is returned.
166+
func configNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
167+
list, err := dockerCLI.Client().ConfigList(cmd.Context(), client.ConfigListOptions{})
168+
if err != nil {
169+
return []string{}
170+
}
171+
names := make([]string, 0, len(list))
172+
for _, v := range list {
173+
names = append(names, v.Spec.Name)
174+
}
175+
return names
176+
}
177+
160178
// containerNames contacts the API to get names and optionally IDs of containers.
161179
// In case of an error, an empty list is returned.
162180
func containerNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command, args []string, toComplete string) []string {
@@ -219,6 +237,72 @@ func nodeNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []str
219237
return names
220238
}
221239

240+
// pluginNames contacts the API to get a list of plugin names.
241+
// In case of an error, an empty list is returned.
242+
func pluginNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
243+
list, err := dockerCLI.Client().PluginList(cmd.Context(), filters.Args{})
244+
if err != nil {
245+
return []string{}
246+
}
247+
names := make([]string, 0, len(list))
248+
for _, v := range list {
249+
names = append(names, v.Name)
250+
}
251+
return names
252+
}
253+
254+
// secretNames contacts the API to get a list of secret names.
255+
// In case of an error, an empty list is returned.
256+
func secretNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
257+
list, err := dockerCLI.Client().SecretList(cmd.Context(), client.SecretListOptions{})
258+
if err != nil {
259+
return []string{}
260+
}
261+
names := make([]string, 0, len(list))
262+
for _, v := range list {
263+
names = append(names, v.Spec.Name)
264+
}
265+
return names
266+
}
267+
268+
// serviceNames contacts the API to get a list of service names.
269+
// In case of an error, an empty list is returned.
270+
func serviceNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
271+
list, err := dockerCLI.Client().ServiceList(cmd.Context(), client.ServiceListOptions{})
272+
if err != nil {
273+
return []string{}
274+
}
275+
names := make([]string, 0, len(list))
276+
for _, v := range list {
277+
names = append(names, v.Spec.Name)
278+
}
279+
return names
280+
}
281+
282+
// taskNames contacts the API to get a list of service names.
283+
// In case of an error, an empty list is returned.
284+
func taskNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
285+
list, err := dockerCLI.Client().TaskList(cmd.Context(), client.TaskListOptions{})
286+
if err != nil || len(list) == 0 {
287+
return []string{}
288+
}
289+
290+
resolver := idresolver.New(dockerCLI.Client(), false)
291+
names := make([]string, 0, len(list))
292+
for _, task := range list {
293+
serviceName, err := resolver.Resolve(cmd.Context(), swarm.Service{}, task.ServiceID)
294+
if err != nil {
295+
continue
296+
}
297+
if task.Slot != 0 {
298+
names = append(names, fmt.Sprintf("%v.%v", serviceName, task.Slot))
299+
} else {
300+
names = append(names, fmt.Sprintf("%v.%v", serviceName, task.NodeID))
301+
}
302+
}
303+
return names
304+
}
305+
222306
// volumeNames contacts the API to get a list of volume names.
223307
// In case of an error, an empty list is returned.
224308
func volumeNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
@@ -232,3 +316,39 @@ func volumeNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []s
232316
}
233317
return names
234318
}
319+
320+
// completeObjectNames completes names of objects based on the "--type" flag
321+
//
322+
// TODO(thaJeztah): completion functions in this package don't remove names that have already been completed
323+
// this causes completion to continue even if a given name was already completed.
324+
func completeObjectNames(dockerCLI completion.APIClientProvider) cobra.CompletionFunc {
325+
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
326+
if f := cmd.Flags().Lookup("type"); f != nil && f.Changed {
327+
switch f.Value.String() {
328+
case typeConfig:
329+
return configNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
330+
case typeContainer:
331+
return containerNames(dockerCLI, cmd, args, toComplete), cobra.ShellCompDirectiveNoFileComp
332+
case typeImage:
333+
return imageNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
334+
case typeNetwork:
335+
return networkNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
336+
case typeNode:
337+
return nodeNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
338+
case typePlugin:
339+
return pluginNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
340+
case typeSecret:
341+
return secretNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
342+
case typeService:
343+
return serviceNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
344+
case typeTask:
345+
return taskNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
346+
case typeVolume:
347+
return volumeNames(dockerCLI, cmd), cobra.ShellCompDirectiveNoFileComp
348+
default:
349+
return nil, cobra.ShellCompDirectiveNoFileComp
350+
}
351+
}
352+
return nil, cobra.ShellCompDirectiveNoFileComp
353+
}
354+
}

cli/command/system/inspect.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ func newInspectCommand(dockerCLI command.Cli) *cobra.Command {
7373
return runInspect(cmd.Context(), dockerCLI, opts)
7474
},
7575
// TODO(thaJeztah): should we consider adding completion for common object-types? (images, containers?)
76-
ValidArgsFunction: cobra.NoFileCompletions,
76+
ValidArgsFunction: completeObjectNames(dockerCLI),
7777
DisableFlagsInUseLine: true,
7878
}
7979

0 commit comments

Comments
 (0)