Skip to content

Commit 929e861

Browse files
authored
Merge pull request #5614 from thaJeztah/27.x_backport_completion-events--filter
2 parents 8be6bec + 8caf347 commit 929e861

File tree

4 files changed

+464
-8
lines changed

4 files changed

+464
-8
lines changed

cli/command/system/client_test.go

Lines changed: 60 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,39 @@ import (
77
"github.com/docker/docker/api/types/container"
88
"github.com/docker/docker/api/types/events"
99
"github.com/docker/docker/api/types/filters"
10+
"github.com/docker/docker/api/types/image"
1011
"github.com/docker/docker/api/types/network"
12+
"github.com/docker/docker/api/types/swarm"
13+
"github.com/docker/docker/api/types/system"
14+
"github.com/docker/docker/api/types/volume"
1115
"github.com/docker/docker/client"
1216
)
1317

1418
type fakeClient struct {
1519
client.Client
1620

1721
version string
18-
serverVersion func(ctx context.Context) (types.Version, error)
19-
eventsFn func(context.Context, events.ListOptions) (<-chan events.Message, <-chan error)
22+
containerListFunc func(context.Context, container.ListOptions) ([]types.Container, error)
2023
containerPruneFunc func(ctx context.Context, pruneFilters filters.Args) (container.PruneReport, error)
24+
eventsFn func(context.Context, events.ListOptions) (<-chan events.Message, <-chan error)
25+
imageListFunc func(ctx context.Context, options image.ListOptions) ([]image.Summary, error)
26+
infoFunc func(ctx context.Context) (system.Info, error)
27+
networkListFunc func(ctx context.Context, options network.ListOptions) ([]network.Summary, error)
2128
networkPruneFunc func(ctx context.Context, pruneFilter filters.Args) (network.PruneReport, error)
22-
}
23-
24-
func (cli *fakeClient) ServerVersion(ctx context.Context) (types.Version, error) {
25-
return cli.serverVersion(ctx)
29+
nodeListFunc func(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error)
30+
serverVersion func(ctx context.Context) (types.Version, error)
31+
volumeListFunc func(ctx context.Context, options volume.ListOptions) (volume.ListResponse, error)
2632
}
2733

2834
func (cli *fakeClient) ClientVersion() string {
2935
return cli.version
3036
}
3137

32-
func (cli *fakeClient) Events(ctx context.Context, opts events.ListOptions) (<-chan events.Message, <-chan error) {
33-
return cli.eventsFn(ctx, opts)
38+
func (cli *fakeClient) ContainerList(ctx context.Context, options container.ListOptions) ([]types.Container, error) {
39+
if cli.containerListFunc != nil {
40+
return cli.containerListFunc(ctx, options)
41+
}
42+
return []types.Container{}, nil
3443
}
3544

3645
func (cli *fakeClient) ContainersPrune(ctx context.Context, pruneFilters filters.Args) (container.PruneReport, error) {
@@ -40,9 +49,52 @@ func (cli *fakeClient) ContainersPrune(ctx context.Context, pruneFilters filters
4049
return container.PruneReport{}, nil
4150
}
4251

52+
func (cli *fakeClient) Events(ctx context.Context, opts events.ListOptions) (<-chan events.Message, <-chan error) {
53+
return cli.eventsFn(ctx, opts)
54+
}
55+
56+
func (cli *fakeClient) ImageList(ctx context.Context, options image.ListOptions) ([]image.Summary, error) {
57+
if cli.imageListFunc != nil {
58+
return cli.imageListFunc(ctx, options)
59+
}
60+
return []image.Summary{}, nil
61+
}
62+
63+
func (cli *fakeClient) Info(ctx context.Context) (system.Info, error) {
64+
if cli.infoFunc != nil {
65+
return cli.infoFunc(ctx)
66+
}
67+
return system.Info{}, nil
68+
}
69+
70+
func (cli *fakeClient) NetworkList(ctx context.Context, options network.ListOptions) ([]network.Summary, error) {
71+
if cli.networkListFunc != nil {
72+
return cli.networkListFunc(ctx, options)
73+
}
74+
return []network.Summary{}, nil
75+
}
76+
4377
func (cli *fakeClient) NetworksPrune(ctx context.Context, pruneFilter filters.Args) (network.PruneReport, error) {
4478
if cli.networkPruneFunc != nil {
4579
return cli.networkPruneFunc(ctx, pruneFilter)
4680
}
4781
return network.PruneReport{}, nil
4882
}
83+
84+
func (cli *fakeClient) NodeList(ctx context.Context, options types.NodeListOptions) ([]swarm.Node, error) {
85+
if cli.nodeListFunc != nil {
86+
return cli.nodeListFunc(ctx, options)
87+
}
88+
return []swarm.Node{}, nil
89+
}
90+
91+
func (cli *fakeClient) ServerVersion(ctx context.Context) (types.Version, error) {
92+
return cli.serverVersion(ctx)
93+
}
94+
95+
func (cli *fakeClient) VolumeList(ctx context.Context, options volume.ListOptions) (volume.ListResponse, error) {
96+
if cli.volumeListFunc != nil {
97+
return cli.volumeListFunc(ctx, options)
98+
}
99+
return volume.ListResponse{}, nil
100+
}

cli/command/system/completion.go

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
package system
2+
3+
import (
4+
"strings"
5+
6+
"github.com/docker/cli/cli/command/completion"
7+
"github.com/docker/docker/api/types"
8+
"github.com/docker/docker/api/types/events"
9+
"github.com/docker/docker/api/types/image"
10+
"github.com/docker/docker/api/types/network"
11+
"github.com/docker/docker/api/types/volume"
12+
"github.com/spf13/cobra"
13+
)
14+
15+
var (
16+
eventFilters = []string{"container", "daemon", "event", "image", "label", "network", "node", "scope", "type", "volume"}
17+
18+
// eventTypes is a list of all event types.
19+
// This should be moved to the moby codebase once its usage is consolidated here.
20+
eventTypes = []events.Type{
21+
events.BuilderEventType,
22+
events.ConfigEventType,
23+
events.ContainerEventType,
24+
events.DaemonEventType,
25+
events.ImageEventType,
26+
events.NetworkEventType,
27+
events.NodeEventType,
28+
events.PluginEventType,
29+
events.SecretEventType,
30+
events.ServiceEventType,
31+
events.VolumeEventType,
32+
}
33+
34+
// eventActions is a list of all event actions.
35+
// This should be moved to the moby codebase once its usage is consolidated here.
36+
eventActions = []events.Action{
37+
events.ActionCreate,
38+
events.ActionStart,
39+
events.ActionRestart,
40+
events.ActionStop,
41+
events.ActionCheckpoint,
42+
events.ActionPause,
43+
events.ActionUnPause,
44+
events.ActionAttach,
45+
events.ActionDetach,
46+
events.ActionResize,
47+
events.ActionUpdate,
48+
events.ActionRename,
49+
events.ActionKill,
50+
events.ActionDie,
51+
events.ActionOOM,
52+
events.ActionDestroy,
53+
events.ActionRemove,
54+
events.ActionCommit,
55+
events.ActionTop,
56+
events.ActionCopy,
57+
events.ActionArchivePath,
58+
events.ActionExtractToDir,
59+
events.ActionExport,
60+
events.ActionImport,
61+
events.ActionSave,
62+
events.ActionLoad,
63+
events.ActionTag,
64+
events.ActionUnTag,
65+
events.ActionPush,
66+
events.ActionPull,
67+
events.ActionPrune,
68+
events.ActionDelete,
69+
events.ActionEnable,
70+
events.ActionDisable,
71+
events.ActionConnect,
72+
events.ActionDisconnect,
73+
events.ActionReload,
74+
events.ActionMount,
75+
events.ActionUnmount,
76+
events.ActionExecCreate,
77+
events.ActionExecStart,
78+
events.ActionExecDie,
79+
events.ActionExecDetach,
80+
events.ActionHealthStatus,
81+
events.ActionHealthStatusRunning,
82+
events.ActionHealthStatusHealthy,
83+
events.ActionHealthStatusUnhealthy,
84+
}
85+
)
86+
87+
// completeEventFilters provides completion for the filters that can be used with `--filter`.
88+
func completeEventFilters(dockerCLI completion.APIClientProvider) completion.ValidArgsFn {
89+
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
90+
key, _, ok := strings.Cut(toComplete, "=")
91+
if !ok {
92+
return postfixWith("=", eventFilters), cobra.ShellCompDirectiveNoSpace
93+
}
94+
switch key {
95+
case "container":
96+
return prefixWith("container=", containerNames(dockerCLI, cmd, args, toComplete)), cobra.ShellCompDirectiveNoFileComp
97+
case "daemon":
98+
return prefixWith("daemon=", daemonNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp
99+
case "event":
100+
return prefixWith("event=", validEventNames()), cobra.ShellCompDirectiveNoFileComp
101+
case "image":
102+
return prefixWith("image=", imageNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp
103+
case "label":
104+
return nil, cobra.ShellCompDirectiveNoFileComp
105+
case "network":
106+
return prefixWith("network=", networkNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp
107+
case "node":
108+
return prefixWith("node=", nodeNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp
109+
case "scope":
110+
return prefixWith("scope=", []string{"local", "swarm"}), cobra.ShellCompDirectiveNoFileComp
111+
case "type":
112+
return prefixWith("type=", eventTypeNames()), cobra.ShellCompDirectiveNoFileComp
113+
case "volume":
114+
return prefixWith("volume=", volumeNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp
115+
default:
116+
return postfixWith("=", eventFilters), cobra.ShellCompDirectiveNoSpace | cobra.ShellCompDirectiveNoFileComp
117+
}
118+
}
119+
}
120+
121+
// prefixWith prefixes every element in the slice with the given prefix.
122+
func prefixWith(prefix string, values []string) []string {
123+
result := make([]string, len(values))
124+
for i, v := range values {
125+
result[i] = prefix + v
126+
}
127+
return result
128+
}
129+
130+
// postfixWith appends postfix to every element in the slice.
131+
func postfixWith(postfix string, values []string) []string {
132+
result := make([]string, len(values))
133+
for i, v := range values {
134+
result[i] = v + postfix
135+
}
136+
return result
137+
}
138+
139+
// eventTypeNames provides a list of all event types.
140+
// The list is derived from eventTypes.
141+
func eventTypeNames() []string {
142+
names := make([]string, len(eventTypes))
143+
for i, eventType := range eventTypes {
144+
names[i] = string(eventType)
145+
}
146+
return names
147+
}
148+
149+
// validEventNames provides a list of all event actions.
150+
// The list is derived from eventActions.
151+
// Actions that are not suitable for usage in completions are removed.
152+
func validEventNames() []string {
153+
names := make([]string, 0, len(eventActions))
154+
for _, eventAction := range eventActions {
155+
if strings.Contains(string(eventAction), " ") {
156+
continue
157+
}
158+
names = append(names, string(eventAction))
159+
}
160+
return names
161+
}
162+
163+
// containerNames contacts the API to get names and optionally IDs of containers.
164+
// In case of an error, an empty list is returned.
165+
func containerNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command, args []string, toComplete string) []string {
166+
names, _ := completion.ContainerNames(dockerCLI, true)(cmd, args, toComplete)
167+
if names == nil {
168+
return []string{}
169+
}
170+
return names
171+
}
172+
173+
// daemonNames contacts the API to get name and ID of the current docker daemon.
174+
// In case of an error, an empty list is returned.
175+
func daemonNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
176+
info, err := dockerCLI.Client().Info(cmd.Context())
177+
if err != nil {
178+
return []string{}
179+
}
180+
return []string{info.Name, info.ID}
181+
}
182+
183+
// imageNames contacts the API to get a list of image names.
184+
// In case of an error, an empty list is returned.
185+
func imageNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
186+
list, err := dockerCLI.Client().ImageList(cmd.Context(), image.ListOptions{})
187+
if err != nil {
188+
return []string{}
189+
}
190+
names := make([]string, 0, len(list))
191+
for _, img := range list {
192+
names = append(names, img.RepoTags...)
193+
}
194+
return names
195+
}
196+
197+
// networkNames contacts the API to get a list of network names.
198+
// In case of an error, an empty list is returned.
199+
func networkNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
200+
list, err := dockerCLI.Client().NetworkList(cmd.Context(), network.ListOptions{})
201+
if err != nil {
202+
return []string{}
203+
}
204+
names := make([]string, 0, len(list))
205+
for _, nw := range list {
206+
names = append(names, nw.Name)
207+
}
208+
return names
209+
}
210+
211+
// nodeNames contacts the API to get a list of node names.
212+
// In case of an error, an empty list is returned.
213+
func nodeNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
214+
list, err := dockerCLI.Client().NodeList(cmd.Context(), types.NodeListOptions{})
215+
if err != nil {
216+
return []string{}
217+
}
218+
names := make([]string, 0, len(list))
219+
for _, node := range list {
220+
names = append(names, node.Description.Hostname)
221+
}
222+
return names
223+
}
224+
225+
// volumeNames contacts the API to get a list of volume names.
226+
// In case of an error, an empty list is returned.
227+
func volumeNames(dockerCLI completion.APIClientProvider, cmd *cobra.Command) []string {
228+
list, err := dockerCLI.Client().VolumeList(cmd.Context(), volume.ListOptions{})
229+
if err != nil {
230+
return []string{}
231+
}
232+
names := make([]string, 0, len(list.Volumes))
233+
for _, v := range list.Volumes {
234+
names = append(names, v.Name)
235+
}
236+
return names
237+
}

0 commit comments

Comments
 (0)