Signed-off-by: Victor Vieux <vieux@docker.com>
Signed-off-by: Victor Vieux <vieux@docker.com>
| ... | ... |
@@ -3,50 +3,57 @@ |
| 3 | 3 |
package plugin |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 |
- "encoding/json" |
|
| 7 | 6 |
"fmt" |
| 8 | 7 |
|
| 9 | 8 |
"github.com/docker/docker/api/client" |
| 9 |
+ "github.com/docker/docker/api/client/inspect" |
|
| 10 | 10 |
"github.com/docker/docker/cli" |
| 11 | 11 |
"github.com/docker/docker/reference" |
| 12 | 12 |
"github.com/spf13/cobra" |
| 13 | 13 |
"golang.org/x/net/context" |
| 14 | 14 |
) |
| 15 | 15 |
|
| 16 |
+type inspectOptions struct {
|
|
| 17 |
+ pluginNames []string |
|
| 18 |
+ format string |
|
| 19 |
+} |
|
| 20 |
+ |
|
| 16 | 21 |
func newInspectCommand(dockerCli *client.DockerCli) *cobra.Command {
|
| 22 |
+ var opts inspectOptions |
|
| 23 |
+ |
|
| 17 | 24 |
cmd := &cobra.Command{
|
| 18 | 25 |
Use: "inspect PLUGIN", |
| 19 | 26 |
Short: "Inspect a plugin", |
| 20 |
- Args: cli.ExactArgs(1), |
|
| 27 |
+ Args: cli.RequiresMinArgs(1), |
|
| 21 | 28 |
RunE: func(cmd *cobra.Command, args []string) error {
|
| 22 |
- return runInspect(dockerCli, args[0]) |
|
| 29 |
+ opts.pluginNames = args |
|
| 30 |
+ return runInspect(dockerCli, opts) |
|
| 23 | 31 |
}, |
| 24 | 32 |
} |
| 25 | 33 |
|
| 34 |
+ flags := cmd.Flags() |
|
| 35 |
+ flags.StringVarP(&opts.format, "format", "f", "", "Format the output using the given go template") |
|
| 26 | 36 |
return cmd |
| 27 | 37 |
} |
| 28 | 38 |
|
| 29 |
-func runInspect(dockerCli *client.DockerCli, name string) error {
|
|
| 30 |
- named, err := reference.ParseNamed(name) // FIXME: validate |
|
| 31 |
- if err != nil {
|
|
| 32 |
- return err |
|
| 33 |
- } |
|
| 34 |
- if reference.IsNameOnly(named) {
|
|
| 35 |
- named = reference.WithDefaultTag(named) |
|
| 36 |
- } |
|
| 37 |
- ref, ok := named.(reference.NamedTagged) |
|
| 38 |
- if !ok {
|
|
| 39 |
- return fmt.Errorf("invalid name: %s", named.String())
|
|
| 40 |
- } |
|
| 41 |
- p, err := dockerCli.Client().PluginInspect(context.Background(), ref.String()) |
|
| 42 |
- if err != nil {
|
|
| 43 |
- return err |
|
| 44 |
- } |
|
| 39 |
+func runInspect(dockerCli *client.DockerCli, opts inspectOptions) error {
|
|
| 40 |
+ client := dockerCli.Client() |
|
| 41 |
+ ctx := context.Background() |
|
| 42 |
+ getRef := func(name string) (interface{}, []byte, error) {
|
|
| 43 |
+ named, err := reference.ParseNamed(name) // FIXME: validate |
|
| 44 |
+ if err != nil {
|
|
| 45 |
+ return nil, nil, err |
|
| 46 |
+ } |
|
| 47 |
+ if reference.IsNameOnly(named) {
|
|
| 48 |
+ named = reference.WithDefaultTag(named) |
|
| 49 |
+ } |
|
| 50 |
+ ref, ok := named.(reference.NamedTagged) |
|
| 51 |
+ if !ok {
|
|
| 52 |
+ return nil, nil, fmt.Errorf("invalid name: %s", named.String())
|
|
| 53 |
+ } |
|
| 45 | 54 |
|
| 46 |
- b, err := json.MarshalIndent(p, "", "\t") |
|
| 47 |
- if err != nil {
|
|
| 48 |
- return err |
|
| 55 |
+ return client.PluginInspectWithRaw(ctx, ref.String()) |
|
| 49 | 56 |
} |
| 50 |
- _, err = dockerCli.Out().Write(b) |
|
| 51 |
- return err |
|
| 57 |
+ |
|
| 58 |
+ return inspect.Inspect(dockerCli.Out(), opts.pluginNames, opts.format, getRef) |
|
| 52 | 59 |
} |
| ... | ... |
@@ -17,7 +17,8 @@ Usage: docker plugin inspect PLUGIN |
| 17 | 17 |
Inspect a plugin |
| 18 | 18 |
|
| 19 | 19 |
Options: |
| 20 |
- --help Print usage |
|
| 20 |
+ -f, --format string Format the output using the given go template |
|
| 21 |
+ --help Print usage |
|
| 21 | 22 |
``` |
| 22 | 23 |
|
| 23 | 24 |
Returns information about a plugin. By default, this command renders all results |
| ... | ... |
@@ -138,6 +139,13 @@ $ docker plugin inspect tiborvass/no-remove:latest |
| 138 | 138 |
(output formatted for readability) |
| 139 | 139 |
|
| 140 | 140 |
|
| 141 |
+```bash |
|
| 142 |
+$ docker plugin inspect -f '{{.Id}}' tiborvass/no-remove:latest
|
|
| 143 |
+``` |
|
| 144 |
+``` |
|
| 145 |
+8c74c978c434745c3ade82f1bc0acf38d04990eaf494fa507c16d9f1daa99c21 |
|
| 146 |
+``` |
|
| 147 |
+ |
|
| 141 | 148 |
|
| 142 | 149 |
## Related information |
| 143 | 150 |
|
| ... | ... |
@@ -61,7 +61,7 @@ clone git golang.org/x/sys eb2c74142fd19a79b3f237334c7384d5167b1b46 https://gith |
| 61 | 61 |
clone git github.com/docker/go-units eb879ae3e2b84e2a142af415b679ddeda47ec71c |
| 62 | 62 |
clone git github.com/docker/go-connections fa2850ff103453a9ad190da0df0af134f0314b3d |
| 63 | 63 |
|
| 64 |
-clone git github.com/docker/engine-api 94a8f8f29307ab291abad6c6f2182d67089aae5d |
|
| 64 |
+clone git github.com/docker/engine-api 8d8fffdf863b12d03c76abf6ca1377e6f8f4e549 |
|
| 65 | 65 |
clone git github.com/RackSec/srslog 259aed10dfa74ea2961eddd1d9847619f6e98837 |
| 66 | 66 |
clone git github.com/imdario/mergo 0.2.1 |
| 67 | 67 |
|
| ... | ... |
@@ -4,9 +4,7 @@ import ( |
| 4 | 4 |
"github.com/docker/docker/pkg/integration/checker" |
| 5 | 5 |
"github.com/go-check/check" |
| 6 | 6 |
|
| 7 |
- "io/ioutil" |
|
| 8 | 7 |
"os" |
| 9 |
- "os/exec" |
|
| 10 | 8 |
"path/filepath" |
| 11 | 9 |
"strings" |
| 12 | 10 |
) |
| ... | ... |
@@ -28,17 +26,7 @@ func (s *DockerSuite) TestPluginBasicOps(c *check.C) {
|
| 28 | 28 |
c.Assert(out, checker.Contains, pTag) |
| 29 | 29 |
c.Assert(out, checker.Contains, "true") |
| 30 | 30 |
|
| 31 |
- out, _, err = dockerCmdWithError("plugin", "inspect", pNameWithTag)
|
|
| 32 |
- c.Assert(err, checker.IsNil) |
|
| 33 |
- tmpFile, err := ioutil.TempFile("", "inspect.json")
|
|
| 34 |
- c.Assert(err, checker.IsNil) |
|
| 35 |
- defer tmpFile.Close() |
|
| 36 |
- |
|
| 37 |
- if _, err := tmpFile.Write([]byte(out)); err != nil {
|
|
| 38 |
- c.Fatal(err) |
|
| 39 |
- } |
|
| 40 |
- // FIXME: When `docker plugin inspect` takes a format as input, jq can be replaced. |
|
| 41 |
- id, err := exec.Command("jq", ".Id", "--raw-output", tmpFile.Name()).CombinedOutput()
|
|
| 31 |
+ id, _, err := dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", pNameWithTag)
|
|
| 42 | 32 |
c.Assert(err, checker.IsNil) |
| 43 | 33 |
|
| 44 | 34 |
out, _, err = dockerCmdWithError("plugin", "remove", pNameWithTag)
|
| ... | ... |
@@ -51,7 +39,7 @@ func (s *DockerSuite) TestPluginBasicOps(c *check.C) {
|
| 51 | 51 |
c.Assert(err, checker.IsNil) |
| 52 | 52 |
c.Assert(out, checker.Contains, pNameWithTag) |
| 53 | 53 |
|
| 54 |
- _, err = os.Stat(filepath.Join(dockerBasePath, "plugins", string(id))) |
|
| 54 |
+ _, err = os.Stat(filepath.Join(dockerBasePath, "plugins", id)) |
|
| 55 | 55 |
if !os.IsNotExist(err) {
|
| 56 | 56 |
c.Fatal(err) |
| 57 | 57 |
} |
| ... | ... |
@@ -18,6 +18,8 @@ const DefaultVersion string = "1.23" |
| 18 | 18 |
// Client is the API client that performs all operations |
| 19 | 19 |
// against a docker server. |
| 20 | 20 |
type Client struct {
|
| 21 |
+ // host holds the server address to connect to |
|
| 22 |
+ host string |
|
| 21 | 23 |
// proto holds the client protocol i.e. unix. |
| 22 | 24 |
proto string |
| 23 | 25 |
// addr holds the client address. |
| ... | ... |
@@ -90,6 +92,7 @@ func NewClient(host string, version string, client *http.Client, httpHeaders map |
| 90 | 90 |
} |
| 91 | 91 |
|
| 92 | 92 |
return &Client{
|
| 93 |
+ host: host, |
|
| 93 | 94 |
proto: proto, |
| 94 | 95 |
addr: addr, |
| 95 | 96 |
basePath: basePath, |
| ... | ... |
@@ -8,6 +8,11 @@ import ( |
| 8 | 8 |
// ErrConnectionFailed is an error raised when the connection between the client and the server failed. |
| 9 | 9 |
var ErrConnectionFailed = errors.New("Cannot connect to the Docker daemon. Is the docker daemon running on this host?")
|
| 10 | 10 |
|
| 11 |
+// ErrorConnectionFailed returns an error with host in the error message when connection to docker daemon failed. |
|
| 12 |
+func ErrorConnectionFailed(host string) error {
|
|
| 13 |
+ return fmt.Errorf("Cannot connect to the Docker daemon at %s. Is the docker daemon running?", host)
|
|
| 14 |
+} |
|
| 15 |
+ |
|
| 11 | 16 |
type notFound interface {
|
| 12 | 17 |
error |
| 13 | 18 |
NotFound() bool // Is the error a NotFound error |
| ... | ... |
@@ -30,7 +30,7 @@ type PluginAPIClient interface {
|
| 30 | 30 |
PluginInstall(ctx context.Context, name string, options types.PluginInstallOptions) error |
| 31 | 31 |
PluginPush(ctx context.Context, name string, registryAuth string) error |
| 32 | 32 |
PluginSet(ctx context.Context, name string, args []string) error |
| 33 |
- PluginInspect(ctx context.Context, name string) (*types.Plugin, error) |
|
| 33 |
+ PluginInspectWithRaw(ctx context.Context, name string) (*types.Plugin, []byte, error) |
|
| 34 | 34 |
} |
| 35 | 35 |
|
| 36 | 36 |
// Ensure that Client always implements APIClient. |
| ... | ... |
@@ -3,20 +3,28 @@ |
| 3 | 3 |
package client |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 |
+ "bytes" |
|
| 6 | 7 |
"encoding/json" |
| 8 |
+ "io/ioutil" |
|
| 7 | 9 |
|
| 8 | 10 |
"github.com/docker/engine-api/types" |
| 9 | 11 |
"golang.org/x/net/context" |
| 10 | 12 |
) |
| 11 | 13 |
|
| 12 |
-// PluginInspect inspects an existing plugin |
|
| 13 |
-func (cli *Client) PluginInspect(ctx context.Context, name string) (*types.Plugin, error) {
|
|
| 14 |
- var p types.Plugin |
|
| 14 |
+// PluginInspectWithRaw inspects an existing plugin |
|
| 15 |
+func (cli *Client) PluginInspectWithRaw(ctx context.Context, name string) (*types.Plugin, []byte, error) {
|
|
| 15 | 16 |
resp, err := cli.get(ctx, "/plugins/"+name, nil, nil) |
| 16 | 17 |
if err != nil {
|
| 17 |
- return nil, err |
|
| 18 |
+ return nil, nil, err |
|
| 18 | 19 |
} |
| 19 |
- err = json.NewDecoder(resp.body).Decode(&p) |
|
| 20 |
- ensureReaderClosed(resp) |
|
| 21 |
- return &p, err |
|
| 20 |
+ |
|
| 21 |
+ defer ensureReaderClosed(resp) |
|
| 22 |
+ body, err := ioutil.ReadAll(resp.body) |
|
| 23 |
+ if err != nil {
|
|
| 24 |
+ return nil, nil, err |
|
| 25 |
+ } |
|
| 26 |
+ var p types.Plugin |
|
| 27 |
+ rdr := bytes.NewReader(body) |
|
| 28 |
+ err = json.NewDecoder(rdr).Decode(&p) |
|
| 29 |
+ return &p, body, err |
|
| 22 | 30 |
} |
| ... | ... |
@@ -123,11 +123,11 @@ func (cli *Client) sendClientRequest(ctx context.Context, method, path string, q |
| 123 | 123 |
|
| 124 | 124 |
if err, ok := err.(net.Error); ok {
|
| 125 | 125 |
if err.Timeout() {
|
| 126 |
- return serverResp, ErrConnectionFailed |
|
| 126 |
+ return serverResp, ErrorConnectionFailed(cli.host) |
|
| 127 | 127 |
} |
| 128 | 128 |
if !err.Temporary() {
|
| 129 | 129 |
if strings.Contains(err.Error(), "connection refused") || strings.Contains(err.Error(), "dial unix") {
|
| 130 |
- return serverResp, ErrConnectionFailed |
|
| 130 |
+ return serverResp, ErrorConnectionFailed(cli.host) |
|
| 131 | 131 |
} |
| 132 | 132 |
} |
| 133 | 133 |
} |