Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Basic framework for writing and running CLI plugins #1564

Merged
merged 23 commits into from
Jan 31, 2019

Conversation

ijc
Copy link
Contributor

@ijc ijc commented Dec 12, 2018

- What I did

This is a small part of #1534, I wanted to get the basic framework out so that potential consumers could start to play with it and give feedback on how it works for them as well as getting some early feedback on the code.

NB this also includes the changes for #1558 since I was working on top of that.

There is still lots to do:

  • I've left off the docker cli-plugin * stuff since it is still being discussed (although some bits of the internal scaffolding here for listing and finding plugins are more related to that than the actual functionality added here, but I didn't tease those apart). Not doing this any more, at least not in this round (CLI Plugins Design #1534 (comment)).
  • List broken plugins as a new section in docker help.
  • There are no docs;
  • Although there are some here there are insufficient tests (both unit & e2e);
  • docker someplugin --help produces the top-level help instead of calling the plugin for some reason (this means one of the new e2e tests currently fails), looks like docker someplugin --version has the same problem, I'm investigating.
  • Expose plugins in docker info
  • Plugin search path configurable via ~/.docker/config.json
  • Apply the package naming the maintainers decide upon.
  • All the stuff around exposing configuration files, credential handling, table formatting, using dial-stdio etc isn't here (Some of those are nops in practice and just need checking that they are usable externally, otherwise I plan to do these bits in one or more followups, somewhat based on/prioritise by the experiences of folks trying to use this). edit: to clarify, I don't intend on doing this bit here but rather in a followup.

- How to verify it

The unit and e2e tests cover some of it.

Plugin path can be configured with the "cliPluginsExtraDirs" key in ~/.docker/config.json so for having a play (using a local config dir for isolation) you can also make -f docker.Makefile build plugins then things like:

$ mkdir .docker-plugin-test
$ echo "{\"cliPluginsExtraDirs\": [\"`pwd`/build/plugins-linux-amd64/\"]}" > .docker-plugin-test/config.json
$ $ jq . .docker-plugin-test/config.json
{
  "cliPluginsExtraDirs": [
    ".../gocode/src/github.com/docker/cli/build/plugins-linux-amd64"
  ]
}
$ ./build/docker --config=.docker-plugin-test helloworld

to have a play.

- Description for the changelog

  • Basic support for writing and running external CLI commands

@ijc
Copy link
Contributor Author

ijc commented Dec 12, 2018

I was expecting e2e to fail but looks like I forgot to shellcheck and lint too, will sort those out.

@codecov-io
Copy link

codecov-io commented Dec 12, 2018

Codecov Report

Merging #1564 into master will decrease coverage by 0.06%.
The diff coverage is 51.52%.

@@            Coverage Diff             @@
##           master    #1564      +/-   ##
==========================================
- Coverage    56.1%   56.04%   -0.07%     
==========================================
  Files         300      306       +6     
  Lines       20601    20848     +247     
==========================================
+ Hits        11559    11685     +126     
- Misses       8209     8324     +115     
- Partials      833      839       +6

Copy link
Collaborator

@vdemeester vdemeester left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Couple of comment/questions:

  • why not github.com/docker/cli/plugins as a package name ?
  • could additionnal lookup path be available through the user configuration (aka $HOME/.docker/config.json) ?

cli-plugins/manager/candidate_test.go Outdated Show resolved Hide resolved
cli-plugins/manager/plugin.go Outdated Show resolved Hide resolved
cmd/docker/docker.go Outdated Show resolved Hide resolved
@guillaumerose
Copy link

Just a comment related to the Docker Desktop integration:
Docker Desktop doesn't change env variables, it's too hard (multiple shells possible, etc.). Would it be possible to also have a parameter representing DOCKER_CLI_PLUGIN_EXTRA_DIRS in ~/.docker/config.json ?

@ijc
Copy link
Contributor Author

ijc commented Dec 13, 2018

@vdemeester

why not github.com/docker/cli/plugins as a package name ?

To avoid confusion with docker plugins which is about daemon side plugins. Happy to take suggestions for better names though.

could additionnal lookup path be available through the user configuration (aka $HOME/.docker/config.json) ?

Sure.

@guillaumerose WRT that setting and Desktop, we should try hard to avoid needing Desktop to set anything like this (whether in a file or the env) by making sure the default set of paths is sensible for the Desktop OSes such that desktop can install the base set of plugins in the right place without any special overrides.

@vdemeester
Copy link
Collaborator

To avoid confusion with docker plugins which is about daemon side plugins. Happy to take suggestions for better names though.

@ijc the docker plugins "code" is in github.com/docker/cli/cli/command/plugin, so I don't really think there would be any confusion (especially if we add a README.md in github.com/docker/cli/plugins to make it clear). cc @thaJeztah for thoughs

@ijc ijc force-pushed the plugins branch 2 times, most recently from 3aeb78c to 8858366 Compare December 13, 2018 16:06
ijc pushed a commit to ijc/docker-app that referenced this pull request Dec 13, 2018
docker/cli#1564

Since this pushes the actual command down to `docker-app app`, adjust the e2e
tests with:

    $ sed -Eie 's/icmd\.RunCommand\(dockerApp,/icmd.RunCommand(dockerApp, "app",/g' e2e/*.go
    $ sed -Eie 's/exec.Command\(dockerApp,/exec.Command(dockerApp, "app",/g' e2e/*.go
    $ sed -Eie 's/\[\]string\{dockerApp,/\[\]string\{dockerApp, "app",/g' e2e/*.go

(which might be precisely equivalent to `sed -Eie 's/dockerApp,/dockerApp, "app",/g' e2e/*.go`)

Finally, the idiom in docker/cli (for better or worse) is to include an "Error:
" prefix in the error string itself, rather than injecting it when printing.
Since CLI plugins follow the behaviour of the CLI here it is necessary to
prepend "Error: " to some messages. I've only done exactly those necessary to
pass the e2e tests, a fuller audit is very likely required.

Signed-off-by: Ian Campbell <ijc@docker.com>
ijc pushed a commit to ijc/docker-app that referenced this pull request Dec 13, 2018
docker/cli#1564

Since this pushes the actual command down to `docker-app app`, adjust the e2e
tests with:

    $ sed -Eie 's/icmd\.RunCommand\(dockerApp,/icmd.RunCommand(dockerApp, "app",/g' e2e/*.go
    $ sed -Eie 's/exec.Command\(dockerApp,/exec.Command(dockerApp, "app",/g' e2e/*.go
    $ sed -Eie 's/\[\]string\{dockerApp,/\[\]string\{dockerApp, "app",/g' e2e/*.go

(which might be precisely equivalent to `sed -Eie 's/dockerApp,/dockerApp, "app",/g' e2e/*.go`)

Finally, the idiom in docker/cli (for better or worse) is to include an "Error:
" prefix in the error string itself, rather than injecting it when printing.
Since CLI plugins follow the behaviour of the CLI here it is necessary to
prepend "Error: " to some messages. I've only done exactly those necessary to
pass the e2e tests, a fuller audit is very likely required.

Signed-off-by: Ian Campbell <ijc@docker.com>
@ijc
Copy link
Contributor Author

ijc commented Dec 13, 2018

I did a quick port of docker app to use the framework, mostly to serve as an example of how this might be used: docker/app#433.

cli-plugins/plugin/plugin.go Outdated Show resolved Hide resolved
cli-plugins/plugin/plugin.go Outdated Show resolved Hide resolved
e2e/cli-plugins/run_test.go Outdated Show resolved Hide resolved
@ijc ijc force-pushed the plugins branch 3 times, most recently from 5a7f245 to bebdb71 Compare December 18, 2018 11:29
@ijc
Copy link
Contributor Author

ijc commented Dec 18, 2018

I've force pushed quite a big update, rebasing on the latest code (so #1558 is no longer included since it is merged), sorted out the issues around --help, tidied and refactored a bunch of stuff, etc.

I'm going to update the todo in the first comment to reflect what's done and add some new entries people requested.

ijc pushed a commit to ijc/docker-app that referenced this pull request Dec 18, 2018
docker/cli#1564

Since this pushes the actual command down to `docker-app app`, adjust the e2e
tests with:

    $ sed -Eie 's/icmd\.RunCommand\(dockerApp,/icmd.RunCommand(dockerApp, "app",/g' e2e/*.go
    $ sed -Eie 's/exec.Command\(dockerApp,/exec.Command(dockerApp, "app",/g' e2e/*.go
    $ sed -Eie 's/\[\]string\{dockerApp,/\[\]string\{dockerApp, "app",/g' e2e/*.go

(which might be precisely equivalent to `sed -Eie 's/dockerApp,/dockerApp, "app",/g' e2e/*.go`)

Finally, the idiom in docker/cli (for better or worse) is to include an "Error:
" prefix in the error string itself, rather than injecting it when printing.
Since CLI plugins follow the behaviour of the CLI here it is necessary to
prepend "Error: " to some messages. I've only done exactly those necessary to
pass the e2e tests, a fuller audit is very likely required.

Signed-off-by: Ian Campbell <ijc@docker.com>
ijc pushed a commit to ijc/docker-app that referenced this pull request Dec 18, 2018
docker/cli#1564

Since this pushes the actual command down to `docker-app app`, adjust the e2e
tests with:

    $ sed -Eie 's/icmd\.RunCommand\(dockerApp,/icmd.RunCommand(dockerApp, "app",/g' e2e/*.go
    $ sed -Eie 's/exec.Command\(dockerApp,/exec.Command(dockerApp, "app",/g' e2e/*.go
    $ sed -Eie 's/\[\]string\{dockerApp,/\[\]string\{dockerApp, "app",/g' e2e/*.go

(which might be precisely equivalent to `sed -Eie 's/dockerApp,/dockerApp, "app",/g' e2e/*.go`)

Finally, the idiom in docker/cli (for better or worse) is to include an "Error:
" prefix in the error string itself, rather than injecting it when printing.
Since CLI plugins follow the behaviour of the CLI here it is necessary to
prepend "Error: " to some messages. I've only done exactly those necessary to
pass the e2e tests, a fuller audit is very likely required.

Signed-off-by: Ian Campbell <ijc@docker.com>
ijc pushed a commit to ijc/docker-app that referenced this pull request Dec 18, 2018
docker/cli#1564

Since this pushes the actual command down to `docker-app app`, adjust the e2e
tests with:

    $ sed -Eie 's/icmd\.RunCommand\(dockerApp,/icmd.RunCommand(dockerApp, "app",/g' e2e/*.go
    $ sed -Eie 's/exec.Command\(dockerApp,/exec.Command(dockerApp, "app",/g' e2e/*.go
    $ sed -Eie 's/\[\]string\{dockerApp,/\[\]string\{dockerApp, "app",/g' e2e/*.go

(which might be precisely equivalent to `sed -Eie 's/dockerApp,/dockerApp, "app",/g' e2e/*.go`)

Finally, the idiom in docker/cli (for better or worse) is to include an "Error:
" prefix in the error string itself, rather than injecting it when printing.
Since CLI plugins follow the behaviour of the CLI here it is necessary to
prepend "Error: " to some messages. I've only done exactly those necessary to
pass the e2e tests, a fuller audit is very likely required.

Signed-off-by: Ian Campbell <ijc@docker.com>
Makefile Outdated Show resolved Hide resolved
Ian Campbell added 4 commits January 30, 2019 13:45
Signed-off-by: Ian Campbell <ijc@docker.com>
On Windows `foo.exe`, `foo.eXe` and `foo.EXE` are equally executable.

Signed-off-by: Ian Campbell <ijc@docker.com>
This allows passing argument to plugins, otherwise they are caught by the parse
loop, since cobra does not know about each plugin at this stage (to avoid
having to always scan for all plugins) this means that e.g. `docker plugin
--foo` would accumulate `plugin` as an arg to the `docker` command, then choke
on the unknown `--foo`.

This allows unknown global args only, unknown arguments on subcommands (e.g.
`docker ps --foo`) are still correctly caught.

Add an e2e test covering this case.

Signed-off-by: Ian Campbell <ijc@docker.com>
A static global initialiser happens before the arguments are parsed, so we need
to calculate the path later.

Signed-off-by: Ian Campbell <ijc@docker.com>
They were listed twice in `docker --help` (but not `docker help`), since the
stubs were added in both `tryRunPluginHelp` and the `setHelpFunc` closure.

Calling `AddPluginStubCommands` earlier in `setHelpFunc` before the call to
`tryRunPluginHelp` is sufficient. Also it is no longer necessary to add just
valid plugins (`tryRunPluginHelp` handles invalid plugins correctly) so remove
that logic (which was in any case broken for e.g. `docker --help`).

Update the e2e test to check for duplicate entries and also to test `docker
--help` which was previously missed.

Signed-off-by: Ian Campbell <ijc@docker.com>
@thaJeztah
Copy link
Member

Summarizing things that I think can be handled in follow-ups (but some of these should be done before GA);

Future follow-ups (not discussed here);

@thaJeztah
Copy link
Member

@docker/core-cli-maintainers looking at the list above, I'm inclined to move this forward, and take care of the listed issues separate; WDYT?

@thaJeztah thaJeztah added this to the 19.03.0 milestone Jan 30, 2019
@silvin-lubecki
Copy link
Contributor

@thaJeztah I'm ok with merging this first plugin base and iterate with follow-ups on the issues you listed 👍

@vdemeester
Copy link
Collaborator

Same as @silvin-lubecki given we do those follow-up before next release 👼

@ijc
Copy link
Contributor Author

ijc commented Jan 31, 2019

This WFM (of course) and I will commit to addressing the bullets in #1564 next (my first act would be to transliterate that comment into an issue I think).

@thaJeztah I've looked over the outstanding comments and I think your list covers them all. Have I missed anything which you don't want to defer?

Copy link
Member

@thaJeztah thaJeztah left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Copy link
Contributor

@silvin-lubecki silvin-lubecki left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!!! Thank you @ijc for this awesome work!

@silvin-lubecki silvin-lubecki merged commit 2e5639d into docker:master Jan 31, 2019
@ijc ijc deleted the plugins branch January 31, 2019 17:19
@ijc
Copy link
Contributor Author

ijc commented Feb 1, 2019

@albers do you have any advice on how we might go about integrating completion for plugins? I was wondering perhaps if contrib/completion/bash/docker could maybe iterate over some set of plugin_completions.d where plugins could drop their snippets?

WDYT?

@albers
Copy link
Collaborator

albers commented Mar 8, 2019

do you have any advice on how we might go about integrating completion for plugins?

@ijc I think this is possible in the way you outlined.
I'll give it a try.

@ijc
Copy link
Contributor Author

ijc commented Mar 11, 2019

Awesome, thanks @albers! Please give a shout if there is any way I can help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants