Completion for events --filter#5538
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #5538 +/- ##
==========================================
- Coverage 60.67% 59.64% -1.03%
==========================================
Files 345 346 +1
Lines 23491 29211 +5720
==========================================
+ Hits 14252 17422 +3170
- Misses 8266 10819 +2553
+ Partials 973 970 -3 |
2d60765 to
a09c204
Compare
thaJeztah
left a comment
There was a problem hiding this comment.
Thank you for working on this! It's really great to see you start doing so (and much appreciated!). I initially had some reservations on the Cobra completion (and the initial set of completion was fairly limited), but they start to shape up.
I like where this PR is going; I gave it a quick try, and it's already pretty nice;
root@docker-cli-dev$ docker events --filter
container= daemon= event= image= label= network= node= scope= type= volume=
root@docker-cli-dev$ docker events --filter container=
empty foo hello2 kind_wing loving_mccarthy
root@docker-cli-dev$ docker events --filter container=foo --filter network=
bridge docker_gwbridge host ingress none
root@docker-cli-dev$ docker events --filter container=foo --filter network=Some things that I didn't give much thought yet;
- (honestly) our filter flags are a bit messy and behavior not always well-defined (how do combinations of filters .. combine?)
- Also some quirks like some filters allowing "exclusions" (
foo!=bar) and others don't, and this may also differ between commands (:disappointed:) - ☝️ we need to spend time documenting all filters and options
Also "FYI" we were discussion potentially adding (an) API endpoint(s) dedicated to completion; nothing set in stone yet, and still to be discussed further, but the idea was that such an endpoint could move some of the heavy-lifting to the daemon, and provide a more optimized response for purpose of completion (e.g. if all we need is "names", there's no need to collect all the other bits). Such endpoint(s) could be more lightweight and perhaps more flexible / opinionated as their purpose is clear.
That discussion was somewhat related to #5528, which is a really nice feature, but also had some implications; it would mean the CLI now had to integrate with a Docker Hub specific API (the endpoints used are not part of the OCI distribution spec), but also could complicate situations where the daemon is configured to use a proxy, is airgapped, or is using a registry mirror. Having an (extensible) endpoint defined in the API could mean that such actions could be handled on the daemon-side (which on (e.g.) Docker Desktop could mean a service handling this).
cli/command/system/completion.go
Outdated
|
|
||
| var ( | ||
| eventFilters = []string{"container", "daemon", "event", "image", "label", "network", "node", "scope", "type", "volume"} | ||
| eventNames = []string{ |
There was a problem hiding this comment.
Oh! Perhaps we should have some utility for this; we define lists of these now, but they are strongly typed (which should still be fine, as they're just a string under the hood);
cli/vendor/github.com/docker/docker/api/types/events/events.go
Lines 7 to 100 in 3590f94
Considering some options for that;
We could create slice for all event.Type and event.Action
var Types = []Type{
ContainerEventType,
ImageEventType,
// ...
}
var Actions = []Action{
ActionCreate,
ActionStart,
// ...
}And/or create a struct with a slice of actions per type, e.g. something like;
var PerType = map[Type][]Action{
ContainerEventType: {
ActionCreate, ActionAttach, ActionDelete,
},
ImageEventType: {
ActionCreate, ActionDelete,
},
}Or a utility function for that can provide this;
func ForType(t Type) []Action {
if string(t) == "all" {
return AllActions
}
return PerType[t]
}There was a problem hiding this comment.
This would be added in the moby codebase?
There was a problem hiding this comment.
Yes, correct; we vendor those types from the Moby repo.
Converting to a string (or slice of strings) would then probably be left to the consumer (so that's something we can do in this repository).
There was a problem hiding this comment.
But before those changes are made there, we could implement them locally here as non-exported / private utilities (and see what a good "shape" would be), then make the change in moby, and once those are vendored here, we can swap the local ones; similar to how #5480 swapped a local construct to using the new module to provide those
There was a problem hiding this comment.
Great. I'll make a suggestion. This might take some time, though.
Thanks for your support.
There was a problem hiding this comment.
@thaJeztah I added completions for the remaining filters.
At that stage, the completeFilters was way to long and ugly, so I decided to introduce some helper functions.
These helper functions simplify error handling very much, as they just return empty arrays in case of API errors.
PTAL, IMHO the PR should be complete now.
cli/command/system/completion.go
Outdated
| if strings.HasPrefix(toComplete, "container=") { | ||
| // the pure container name list should be pulled out from ContainerNames. | ||
| names, _ := completion.ContainerNames(dockerCLI, true)(cmd, args, toComplete) | ||
| return prefixWith("container=", names), cobra.ShellCompDirectiveDefault | ||
| } |
There was a problem hiding this comment.
Nice!! I was wondering the other day if something like this would work. I wanted to experiment if completion would be possible for the "advanced" CSV flags, but didn't give it a try yet.
3306b98 to
3f3ff40
Compare
This comment was marked as outdated.
This comment was marked as outdated.
b650f81 to
95c88b2
Compare
|
@thaJeztah I added tests for those commands that call the API. |
e49d3a8 to
2926d0f
Compare
|
The PR is ready for review now. |
events --filter
thaJeztah
left a comment
There was a problem hiding this comment.
This looks great! Sorry for the delay; I apparently started a review, and didn't submit 🙈
I left some suggestions for consideration.
| // completeEventFilters provides completion for the filters that can be used with `--filter`. | ||
| func completeEventFilters(dockerCLI completion.APIClientProvider) completion.ValidArgsFn { | ||
| return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) { | ||
| if strings.HasPrefix(toComplete, "container=") { | ||
| return prefixWith("container=", containerNames(dockerCLI, cmd, args, toComplete)), cobra.ShellCompDirectiveNoFileComp | ||
| } | ||
| if strings.HasPrefix(toComplete, "daemon=") { | ||
| return prefixWith("daemon=", daemonNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp | ||
| } | ||
| if strings.HasPrefix(toComplete, "event=") { | ||
| return prefixWith("event=", validEventNames()), cobra.ShellCompDirectiveNoFileComp | ||
| } | ||
| if strings.HasPrefix(toComplete, "image=") { | ||
| return prefixWith("image=", imageNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp | ||
| } | ||
| if strings.HasPrefix(toComplete, "label=") { | ||
| return nil, cobra.ShellCompDirectiveNoFileComp | ||
| } | ||
| if strings.HasPrefix(toComplete, "network=") { | ||
| return prefixWith("network=", networkNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp | ||
| } | ||
| if strings.HasPrefix(toComplete, "node=") { | ||
| return prefixWith("node=", nodeNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp | ||
| } | ||
| if strings.HasPrefix(toComplete, "scope=") { | ||
| return prefixWith("scope=", []string{"local", "swarm"}), cobra.ShellCompDirectiveNoFileComp | ||
| } | ||
| if strings.HasPrefix(toComplete, "type=") { | ||
| return prefixWith("type=", eventTypeNames()), cobra.ShellCompDirectiveNoFileComp | ||
| } | ||
| if strings.HasPrefix(toComplete, "volume=") { | ||
| return prefixWith("volume=", volumeNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp | ||
| } | ||
|
|
||
| return postfixWith("=", eventFilters), cobra.ShellCompDirectiveNoSpace | ||
| } | ||
| } |
There was a problem hiding this comment.
We could probably optimize this to prevent matching prefix multiple times;
// completeEventFilters provides completion for the filters that can be used with `--filter`.
func completeEventFilters(dockerCLI completion.APIClientProvider) completion.ValidArgsFn {
return func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
key, _, ok := strings.Cut(toComplete, "=")
if !ok {
return postfixWith("=", eventFilters), cobra.ShellCompDirectiveNoSpace
}
switch key {
case "container":
return prefixWith("container=", containerNames(dockerCLI, cmd, args, toComplete)), cobra.ShellCompDirectiveNoFileComp
case "daemon":
return prefixWith("daemon=", daemonNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp
case "event":
return prefixWith("event=", validEventNames()), cobra.ShellCompDirectiveNoFileComp
case "image":
return prefixWith("image=", imageNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp
case "label":
return nil, cobra.ShellCompDirectiveNoFileComp
case "network":
return prefixWith("network=", networkNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp
case "node":
return prefixWith("node=", nodeNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp
case "scope":
return prefixWith("scope=", []string{"local", "swarm"}), cobra.ShellCompDirectiveNoFileComp
case "type":
return prefixWith("type=", eventTypeNames()), cobra.ShellCompDirectiveNoFileComp
case "volume":
return prefixWith("volume=", volumeNames(dockerCLI, cmd)), cobra.ShellCompDirectiveNoFileComp
default:
return postfixWith("=", eventFilters), cobra.ShellCompDirectiveNoSpace
}
}
}I was even contemplating if we should re-purpose the constants where suitable, but perhaps confusing, because not all filters refer to a type; i.e., was considering;
case string(events.ContainerEventType):But not sure if that's a good thing to do
There was a problem hiding this comment.
That's much better, thank you. I did not go for the constants because I think this produces asymmetry that makes the code harder to comprehend.
There was a problem hiding this comment.
Yes, makes sense; I started to replace the strings with the consts, then realised not all of them were event-types 😂
b141df5 to
ad9b4b8
Compare
|
@thaJeztah Thanks for the review, I applied all of your suggestions. PTAL. |
Thank you! Changes LGTM, but it's ok to squash my suggestions with the first commit (we don't need the history of the "review comments applied"). Could you squash the first and last commit? |
Signed-off-by: Harald Albers <github@albersweb.de>
Signed-off-by: Harald Albers <github@albersweb.de>
ad9b4b8 to
e1c5180
Compare
done. |
Adds completion for
docker events --filter.There are different types of filters, requiring different completion logic.
Some filters have static values, others require API lookups.
In some cases, trailing spaces of intermediate completions have to be suppressed.
The completion logic is complex because cobra does not offer support for compound flag values.
The handler for the
--filterflag serves as the central entrypoint to all completions.How to verify:
in a dev container, run
make completion, then trigger completions, e.g.The completions should correspond to those of the legacy bash completion.