Przeglądaj źródła

Merge pull request #28789 from yongtang/28735-plugin-inspect-id-or-name

Allow `docker plugin inspect` to search based on ID or name
Anusha Ragunathan 8 lat temu
rodzic
commit
57ace38103

+ 3 - 18
cli/command/plugin/inspect.go

@@ -1,12 +1,9 @@
 package plugin
 
 import (
-	"fmt"
-
 	"github.com/docker/docker/cli"
 	"github.com/docker/docker/cli/command"
 	"github.com/docker/docker/cli/command/inspect"
-	"github.com/docker/docker/reference"
 	"github.com/spf13/cobra"
 	"golang.org/x/net/context"
 )
@@ -20,7 +17,7 @@ func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
 	var opts inspectOptions
 
 	cmd := &cobra.Command{
-		Use:   "inspect [OPTIONS] PLUGIN [PLUGIN...]",
+		Use:   "inspect [OPTIONS] PLUGIN|ID [PLUGIN|ID...]",
 		Short: "Display detailed information on one or more plugins",
 		Args:  cli.RequiresMinArgs(1),
 		RunE: func(cmd *cobra.Command, args []string) error {
@@ -37,20 +34,8 @@ func newInspectCommand(dockerCli *command.DockerCli) *cobra.Command {
 func runInspect(dockerCli *command.DockerCli, opts inspectOptions) error {
 	client := dockerCli.Client()
 	ctx := context.Background()
-	getRef := func(name string) (interface{}, []byte, error) {
-		named, err := reference.ParseNamed(name) // FIXME: validate
-		if err != nil {
-			return nil, nil, err
-		}
-		if reference.IsNameOnly(named) {
-			named = reference.WithDefaultTag(named)
-		}
-		ref, ok := named.(reference.NamedTagged)
-		if !ok {
-			return nil, nil, fmt.Errorf("invalid name: %s", named.String())
-		}
-
-		return client.PluginInspectWithRaw(ctx, ref.String())
+	getRef := func(ref string) (interface{}, []byte, error) {
+		return client.PluginInspectWithRaw(ctx, ref)
 	}
 
 	return inspect.Inspect(dockerCli.Out(), opts.pluginNames, opts.format, getRef)

+ 3 - 3
docs/reference/commandline/plugin_inspect.md

@@ -16,13 +16,13 @@ keywords: "plugin, inspect"
 # plugin inspect
 
 ```markdown
-Usage:  docker plugin inspect [OPTIONS] PLUGIN [PLUGIN...]
+Usage:	docker plugin inspect [OPTIONS] PLUGIN|ID [PLUGIN|ID...]
 
 Display detailed information on one or more plugins
 
 Options:
-      -f, --format string   Format the output using the given Go template
-          --help            Print usage
+  -f, --format string   Format the output using the given Go template
+      --help            Print usage
 ```
 
 Returns information about a plugin. By default, this command renders all results

+ 49 - 0
integration-cli/docker_cli_plugins_test.go

@@ -201,3 +201,52 @@ func (s *DockerSuite) TestPluginCreate(c *check.C) {
 	// The output will consists of one HEADER line and one line of foo/bar-driver
 	c.Assert(len(strings.Split(strings.TrimSpace(out), "\n")), checker.Equals, 2)
 }
+
+func (s *DockerSuite) TestPluginInspect(c *check.C) {
+	testRequires(c, DaemonIsLinux, Network)
+	_, _, err := dockerCmdWithError("plugin", "install", "--grant-all-permissions", pNameWithTag)
+	c.Assert(err, checker.IsNil)
+
+	out, _, err := dockerCmdWithError("plugin", "ls")
+	c.Assert(err, checker.IsNil)
+	c.Assert(out, checker.Contains, pName)
+	c.Assert(out, checker.Contains, pTag)
+	c.Assert(out, checker.Contains, "true")
+
+	// Find the ID first
+	out, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", pNameWithTag)
+	c.Assert(err, checker.IsNil)
+	id := strings.TrimSpace(out)
+	c.Assert(id, checker.Not(checker.Equals), "")
+
+	// Long form
+	out, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", id)
+	c.Assert(err, checker.IsNil)
+	c.Assert(strings.TrimSpace(out), checker.Equals, id)
+
+	// Short form
+	out, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", id[:5])
+	c.Assert(err, checker.IsNil)
+	c.Assert(strings.TrimSpace(out), checker.Equals, id)
+
+	// Name with tag form
+	out, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", pNameWithTag)
+	c.Assert(err, checker.IsNil)
+	c.Assert(strings.TrimSpace(out), checker.Equals, id)
+
+	// Name without tag form
+	out, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", pName)
+	c.Assert(err, checker.IsNil)
+	c.Assert(strings.TrimSpace(out), checker.Equals, id)
+
+	_, _, err = dockerCmdWithError("plugin", "disable", pNameWithTag)
+	c.Assert(err, checker.IsNil)
+
+	out, _, err = dockerCmdWithError("plugin", "remove", pNameWithTag)
+	c.Assert(err, checker.IsNil)
+	c.Assert(out, checker.Contains, pNameWithTag)
+
+	// After remove nothing should be found
+	_, _, err = dockerCmdWithError("plugin", "inspect", "-f", "{{.Id}}", id[:5])
+	c.Assert(err, checker.NotNil)
+}

+ 45 - 4
plugin/backend_linux.go

@@ -11,6 +11,7 @@ import (
 	"net/http"
 	"os"
 	"path/filepath"
+	"regexp"
 
 	"github.com/Sirupsen/logrus"
 	"github.com/docker/docker/api/types"
@@ -23,6 +24,11 @@ import (
 	"golang.org/x/net/context"
 )
 
+var (
+	validFullID    = regexp.MustCompile(`^([a-f0-9]{64})$`)
+	validPartialID = regexp.MustCompile(`^([a-f0-9]{1,64})$`)
+)
+
 // Disable deactivates a plugin, which implies that they cannot be used by containers.
 func (pm *Manager) Disable(name string) error {
 	p, err := pm.pluginStore.GetByName(name)
@@ -53,12 +59,32 @@ func (pm *Manager) Enable(name string, config *types.PluginEnableConfig) error {
 }
 
 // Inspect examines a plugin config
-func (pm *Manager) Inspect(name string) (tp types.Plugin, err error) {
-	p, err := pm.pluginStore.GetByName(name)
-	if err != nil {
+func (pm *Manager) Inspect(refOrID string) (tp types.Plugin, err error) {
+	// Match on full ID
+	if validFullID.MatchString(refOrID) {
+		p, err := pm.pluginStore.GetByID(refOrID)
+		if err == nil {
+			return p.PluginObj, nil
+		}
+	}
+
+	// Match on full name
+	if pluginName, err := getPluginName(refOrID); err == nil {
+		if p, err := pm.pluginStore.GetByName(pluginName); err == nil {
+			return p.PluginObj, nil
+		}
+	}
+
+	// Match on partial ID
+	if validPartialID.MatchString(refOrID) {
+		p, err := pm.pluginStore.Search(refOrID)
+		if err == nil {
+			return p.PluginObj, nil
+		}
 		return tp, err
 	}
-	return p.PluginObj, nil
+
+	return tp, fmt.Errorf("no plugin name or ID associated with %q", refOrID)
 }
 
 func (pm *Manager) pull(ref reference.Named, metaHeader http.Header, authConfig *types.AuthConfig, pluginID string) (types.PluginPrivileges, error) {
@@ -244,3 +270,18 @@ func (pm *Manager) createFromContext(ctx context.Context, pluginID, pluginDir st
 
 	return nil
 }
+
+func getPluginName(name string) (string, error) {
+	named, err := reference.ParseNamed(name) // FIXME: validate
+	if err != nil {
+		return "", err
+	}
+	if reference.IsNameOnly(named) {
+		named = reference.WithDefaultTag(named)
+	}
+	ref, ok := named.(reference.NamedTagged)
+	if !ok {
+		return "", fmt.Errorf("invalid name: %s", named.String())
+	}
+	return ref.String(), nil
+}

+ 29 - 0
plugin/store/store.go

@@ -30,6 +30,13 @@ type ErrNotFound string
 
 func (name ErrNotFound) Error() string { return fmt.Sprintf("plugin %q not found", string(name)) }
 
+// ErrAmbiguous indicates that a plugin was not found locally.
+type ErrAmbiguous string
+
+func (name ErrAmbiguous) Error() string {
+	return fmt.Sprintf("multiple plugins found for %q", string(name))
+}
+
 // GetByName retreives a plugin by name.
 func (ps *Store) GetByName(name string) (*v2.Plugin, error) {
 	ps.RLock()
@@ -253,3 +260,25 @@ func (ps *Store) CallHandler(p *v2.Plugin) {
 		}
 	}
 }
+
+// Search retreives a plugin by ID Prefix
+// If no plugin is found, then ErrNotFound is returned
+// If multiple plugins are found, then ErrAmbiguous is returned
+func (ps *Store) Search(partialID string) (*v2.Plugin, error) {
+	ps.RLock()
+	defer ps.RUnlock()
+
+	var found *v2.Plugin
+	for id, p := range ps.plugins {
+		if strings.HasPrefix(id, partialID) {
+			if found != nil {
+				return nil, ErrAmbiguous(partialID)
+			}
+			found = p
+		}
+	}
+	if found == nil {
+		return nil, ErrNotFound(partialID)
+	}
+	return found, nil
+}