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

Allow `docker plugin inspect` to search based on ID or name
Upstream-commit: 57ace38103e1c9fc13e40c57b0fc77361e389f54
Component: engine
This commit is contained in:
Anusha Ragunathan
2016-12-02 08:46:47 -08:00
committed by GitHub
5 changed files with 129 additions and 25 deletions
@@ -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)
@@ -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
@@ -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
View File
@@ -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
View File
@@ -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
}