Browse code

Support plugins in `docker inspect`

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>

Yong Tang authored on 2016/11/30 10:31:29
Showing 6 changed files
... ...
@@ -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) {