This fix tries to address the proposal raised in 28946
to support plugins in `docker inspect`.
The command `docker inspect` already supports
"container", "image", "node", "network", "service", "volume", "task".
However, `--type plugin` is not supported yet at the moment.
This fix address this issue by adding the support of `--type plugin`
for `docker inspect`.
An additional integration test has been added to cover the changes.
This fix fixes 28946.
Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
| ... | ... |
@@ -45,7 +45,7 @@ func NewInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
|
| 45 | 45 |
func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
|
| 46 | 46 |
var elementSearcher inspect.GetRefFunc |
| 47 | 47 |
switch opts.inspectType {
|
| 48 |
- case "", "container", "image", "node", "network", "service", "volume", "task": |
|
| 48 |
+ case "", "container", "image", "node", "network", "service", "volume", "task", "plugin": |
|
| 49 | 49 |
elementSearcher = inspectAll(context.Background(), dockerCli, opts.size, opts.inspectType) |
| 50 | 50 |
default: |
| 51 | 51 |
return fmt.Errorf("%q is not a valid value for --type", opts.inspectType)
|
| ... | ... |
@@ -95,6 +95,12 @@ func inspectVolume(ctx context.Context, dockerCli *command.DockerCli) inspect.Ge |
| 95 | 95 |
} |
| 96 | 96 |
} |
| 97 | 97 |
|
| 98 |
+func inspectPlugin(ctx context.Context, dockerCli *command.DockerCli) inspect.GetRefFunc {
|
|
| 99 |
+ return func(ref string) (interface{}, []byte, error) {
|
|
| 100 |
+ return dockerCli.Client().PluginInspectWithRaw(ctx, ref) |
|
| 101 |
+ } |
|
| 102 |
+} |
|
| 103 |
+ |
|
| 98 | 104 |
func inspectAll(ctx context.Context, dockerCli *command.DockerCli, getSize bool, typeConstraint string) inspect.GetRefFunc {
|
| 99 | 105 |
var inspectAutodetect = []struct {
|
| 100 | 106 |
ObjectType string |
| ... | ... |
@@ -108,6 +114,7 @@ func inspectAll(ctx context.Context, dockerCli *command.DockerCli, getSize bool, |
| 108 | 108 |
{"service", false, inspectService(ctx, dockerCli)},
|
| 109 | 109 |
{"task", false, inspectTasks(ctx, dockerCli)},
|
| 110 | 110 |
{"node", false, inspectNode(ctx, dockerCli)},
|
| 111 |
+ {"plugin", false, inspectPlugin(ctx, dockerCli)},
|
|
| 111 | 112 |
} |
| 112 | 113 |
|
| 113 | 114 |
isErrNotSwarmManager := func(err error) bool {
|
| ... | ... |
@@ -255,3 +255,24 @@ func IsErrSecretNotFound(err error) bool {
|
| 255 | 255 |
_, ok := err.(secretNotFoundError) |
| 256 | 256 |
return ok |
| 257 | 257 |
} |
| 258 |
+ |
|
| 259 |
+// pluginNotFoundError implements an error returned when a plugin is not in the docker host. |
|
| 260 |
+type pluginNotFoundError struct {
|
|
| 261 |
+ name string |
|
| 262 |
+} |
|
| 263 |
+ |
|
| 264 |
+// NotFound indicates that this error type is of NotFound |
|
| 265 |
+func (e pluginNotFoundError) NotFound() bool {
|
|
| 266 |
+ return true |
|
| 267 |
+} |
|
| 268 |
+ |
|
| 269 |
+// Error returns a string representation of a pluginNotFoundError |
|
| 270 |
+func (e pluginNotFoundError) Error() string {
|
|
| 271 |
+ return fmt.Sprintf("Error: No such plugin: %s", e.name)
|
|
| 272 |
+} |
|
| 273 |
+ |
|
| 274 |
+// IsErrPluginNotFound returns true if the error is caused |
|
| 275 |
+// when a plugin is not found in the docker host. |
|
| 276 |
+func IsErrPluginNotFound(err error) bool {
|
|
| 277 |
+ return IsErrNotFound(err) |
|
| 278 |
+} |
| ... | ... |
@@ -4,6 +4,7 @@ import ( |
| 4 | 4 |
"bytes" |
| 5 | 5 |
"encoding/json" |
| 6 | 6 |
"io/ioutil" |
| 7 |
+ "net/http" |
|
| 7 | 8 |
|
| 8 | 9 |
"github.com/docker/docker/api/types" |
| 9 | 10 |
"golang.org/x/net/context" |
| ... | ... |
@@ -13,6 +14,9 @@ import ( |
| 13 | 13 |
func (cli *Client) PluginInspectWithRaw(ctx context.Context, name string) (*types.Plugin, []byte, error) {
|
| 14 | 14 |
resp, err := cli.get(ctx, "/plugins/"+name, nil, nil) |
| 15 | 15 |
if err != nil {
|
| 16 |
+ if resp.statusCode == http.StatusNotFound {
|
|
| 17 |
+ return nil, nil, pluginNotFoundError{name}
|
|
| 18 |
+ } |
|
| 16 | 19 |
return nil, nil, err |
| 17 | 20 |
} |
| 18 | 21 |
|
| ... | ... |
@@ -417,3 +417,33 @@ func (s *DockerSuite) TestInspectAmpersand(c *check.C) {
|
| 417 | 417 |
out, _ = dockerCmd(c, "inspect", name) |
| 418 | 418 |
c.Assert(out, checker.Contains, `soanni&rtr`) |
| 419 | 419 |
} |
| 420 |
+ |
|
| 421 |
+func (s *DockerSuite) TestInspectPlugin(c *check.C) {
|
|
| 422 |
+ testRequires(c, DaemonIsLinux, Network) |
|
| 423 |
+ _, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag)
|
|
| 424 |
+ c.Assert(err, checker.IsNil) |
|
| 425 |
+ |
|
| 426 |
+ out, _, err := dockerCmdWithError("inspect", "--type", "plugin", "--format", "{{.Name}}", pNameWithTag)
|
|
| 427 |
+ c.Assert(err, checker.IsNil) |
|
| 428 |
+ c.Assert(strings.TrimSpace(out), checker.Equals, pName) |
|
| 429 |
+ |
|
| 430 |
+ out, _, err = dockerCmdWithError("inspect", "--format", "{{.Name}}", pNameWithTag)
|
|
| 431 |
+ c.Assert(err, checker.IsNil) |
|
| 432 |
+ c.Assert(strings.TrimSpace(out), checker.Equals, pName) |
|
| 433 |
+ |
|
| 434 |
+ // Even without tag the inspect still work |
|
| 435 |
+ out, _, err = dockerCmdWithError("inspect", "--type", "plugin", "--format", "{{.Name}}", pName)
|
|
| 436 |
+ c.Assert(err, checker.IsNil) |
|
| 437 |
+ c.Assert(strings.TrimSpace(out), checker.Equals, pName) |
|
| 438 |
+ |
|
| 439 |
+ out, _, err = dockerCmdWithError("inspect", "--format", "{{.Name}}", pName)
|
|
| 440 |
+ c.Assert(err, checker.IsNil) |
|
| 441 |
+ c.Assert(strings.TrimSpace(out), checker.Equals, pName) |
|
| 442 |
+ |
|
| 443 |
+ _, _, err = dockerCmdWithError("plugin", "disable", pNameWithTag)
|
|
| 444 |
+ c.Assert(err, checker.IsNil) |
|
| 445 |
+ |
|
| 446 |
+ out, _, err = dockerCmdWithError("plugin", "remove", pNameWithTag)
|
|
| 447 |
+ c.Assert(err, checker.IsNil) |
|
| 448 |
+ c.Assert(out, checker.Contains, pNameWithTag) |
|
| 449 |
+} |
| ... | ... |
@@ -62,10 +62,10 @@ func (s *DockerSuite) TestRenameCheckNames(c *check.C) {
|
| 62 | 62 |
name := inspectField(c, newName, "Name") |
| 63 | 63 |
c.Assert(name, checker.Equals, "/"+newName, check.Commentf("Failed to rename container %s", name))
|
| 64 | 64 |
|
| 65 |
- result := dockerCmdWithResult("inspect", "-f={{.Name}}", "first_name")
|
|
| 65 |
+ result := dockerCmdWithResult("inspect", "-f={{.Name}}", "--type=container", "first_name")
|
|
| 66 | 66 |
c.Assert(result, icmd.Matches, icmd.Expected{
|
| 67 | 67 |
ExitCode: 1, |
| 68 |
- Err: "No such object: first_name", |
|
| 68 |
+ Err: "No such container: first_name", |
|
| 69 | 69 |
}) |
| 70 | 70 |
} |
| 71 | 71 |
|
| ... | ... |
@@ -84,7 +84,7 @@ func (pm *Manager) Inspect(refOrID string) (tp types.Plugin, err error) {
|
| 84 | 84 |
return tp, err |
| 85 | 85 |
} |
| 86 | 86 |
|
| 87 |
- return tp, fmt.Errorf("no plugin name or ID associated with %q", refOrID)
|
|
| 87 |
+ return tp, fmt.Errorf("no such plugin name or ID associated with %q", refOrID)
|
|
| 88 | 88 |
} |
| 89 | 89 |
|
| 90 | 90 |
func (pm *Manager) pull(ref reference.Named, metaHeader http.Header, authConfig *types.AuthConfig, pluginID string) (types.PluginPrivileges, error) {
|