Browse Source

Merge pull request #26645 from anusha-ragunathan/use-pluginv2-graph

Make graphdrivers work with pluginv2.
Anusha Ragunathan 8 years ago
parent
commit
ef728a1641

+ 0 - 2
api/types/plugin.go

@@ -1,5 +1,3 @@
-// +build experimental
-
 package types
 
 import (

+ 8 - 0
daemon/daemon.go

@@ -47,6 +47,7 @@ import (
 	"github.com/docker/docker/pkg/sysinfo"
 	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/pkg/truncindex"
+	pluginstore "github.com/docker/docker/plugin/store"
 	"github.com/docker/docker/reference"
 	"github.com/docker/docker/registry"
 	"github.com/docker/docker/runconfig"
@@ -94,6 +95,7 @@ type Daemon struct {
 	gidMaps                   []idtools.IDMap
 	layerStore                layer.Store
 	imageStore                image.Store
+	pluginStore               *pluginstore.Store
 	nameIndex                 *registrar.Registrar
 	linkIndex                 *linkIndex
 	containerd                libcontainerd.Client
@@ -548,6 +550,9 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
 	if driverName == "" {
 		driverName = config.GraphDriver
 	}
+
+	d.pluginStore = pluginstore.NewStore(config.Root)
+
 	d.layerStore, err = layer.NewStoreFromOptions(layer.StoreOptions{
 		StorePath:                 config.Root,
 		MetadataStorePathTemplate: filepath.Join(config.Root, "image", "%s", "layerdb"),
@@ -555,6 +560,7 @@ func NewDaemon(config *Config, registryService registry.Service, containerdRemot
 		GraphDriverOptions:        config.GraphOptions,
 		UIDMaps:                   uidMaps,
 		GIDMaps:                   gidMaps,
+		PluginGetter:              d.pluginStore,
 	})
 	if err != nil {
 		return nil, err
@@ -911,6 +917,8 @@ func (daemon *Daemon) configureVolumes(rootUID, rootGID int) (*store.VolumeStore
 		return nil, err
 	}
 
+	volumedrivers.RegisterPluginGetter(daemon.pluginStore)
+
 	if !volumedrivers.Register(volumesDriver, volumesDriver.Name()) {
 		return nil, fmt.Errorf("local volume driver could not be registered")
 	}

+ 1 - 1
daemon/daemon_experimental.go

@@ -13,7 +13,7 @@ func (daemon *Daemon) verifyExperimentalContainerSettings(hostConfig *container.
 }
 
 func pluginInit(d *Daemon, cfg *Config, remote libcontainerd.Remote) error {
-	return plugin.Init(cfg.Root, remote, d.RegistryService, cfg.LiveRestoreEnabled, d.LogPluginEvent)
+	return plugin.Init(cfg.Root, d.pluginStore, remote, d.RegistryService, cfg.LiveRestoreEnabled, d.LogPluginEvent)
 }
 
 func pluginShutdown() {

+ 5 - 4
daemon/graphdriver/driver.go

@@ -12,6 +12,7 @@ import (
 
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/idtools"
+	"github.com/docker/docker/plugin/getter"
 )
 
 // FsMagic unsigned id of the filesystem in use.
@@ -134,11 +135,11 @@ func Register(name string, initFunc InitFunc) error {
 }
 
 // GetDriver initializes and returns the registered driver
-func GetDriver(name, home string, options []string, uidMaps, gidMaps []idtools.IDMap) (Driver, error) {
+func GetDriver(name, home string, options []string, uidMaps, gidMaps []idtools.IDMap, plugingetter getter.PluginGetter) (Driver, error) {
 	if initFunc, exists := drivers[name]; exists {
 		return initFunc(filepath.Join(home, name), options, uidMaps, gidMaps)
 	}
-	if pluginDriver, err := lookupPlugin(name, home, options); err == nil {
+	if pluginDriver, err := lookupPlugin(name, home, options, plugingetter); err == nil {
 		return pluginDriver, nil
 	}
 	logrus.Errorf("Failed to GetDriver graph %s %s", name, home)
@@ -155,10 +156,10 @@ func getBuiltinDriver(name, home string, options []string, uidMaps, gidMaps []id
 }
 
 // New creates the driver and initializes it at the specified root.
-func New(root string, name string, options []string, uidMaps, gidMaps []idtools.IDMap) (Driver, error) {
+func New(root string, name string, options []string, uidMaps, gidMaps []idtools.IDMap, plugingetter getter.PluginGetter) (Driver, error) {
 	if name != "" {
 		logrus.Debugf("[graphdriver] trying provided driver: %s", name) // so the logs show specified driver
-		return GetDriver(name, root, options, uidMaps, gidMaps)
+		return GetDriver(name, root, options, uidMaps, gidMaps, plugingetter)
 	}
 
 	// Guess for prior driver

+ 1 - 1
daemon/graphdriver/graphtest/graphtest_unix.go

@@ -41,7 +41,7 @@ func newDriver(t testing.TB, name string, options []string) *Driver {
 		t.Fatal(err)
 	}
 
-	d, err := graphdriver.GetDriver(name, root, options, nil, nil)
+	d, err := graphdriver.GetDriver(name, root, options, nil, nil, nil)
 	if err != nil {
 		t.Logf("graphdriver: %v\n", err)
 		if err == graphdriver.ErrNotSupported || err == graphdriver.ErrPrerequisites || err == graphdriver.ErrIncompatibleFS {

+ 3 - 3
daemon/graphdriver/plugin.go

@@ -6,7 +6,7 @@ import (
 	"fmt"
 	"io"
 
-	"github.com/docker/docker/pkg/plugins"
+	"github.com/docker/docker/plugin/getter"
 )
 
 type pluginClient interface {
@@ -18,8 +18,8 @@ type pluginClient interface {
 	SendFile(string, io.Reader, interface{}) error
 }
 
-func lookupPlugin(name, home string, opts []string) (Driver, error) {
-	pl, err := plugins.Get(name, "GraphDriver")
+func lookupPlugin(name, home string, opts []string, pluginGetter getter.PluginGetter) (Driver, error) {
+	pl, err := pluginGetter.Get(name, "GraphDriver", getter.LOOKUP)
 	if err != nil {
 		return nil, fmt.Errorf("Error looking up graphdriver plugin %s: %v", name, err)
 	}

+ 3 - 1
daemon/graphdriver/plugin_unsupported.go

@@ -2,6 +2,8 @@
 
 package graphdriver
 
-func lookupPlugin(name, home string, opts []string) (Driver, error) {
+import "github.com/docker/docker/plugin/getter"
+
+func lookupPlugin(name, home string, opts []string, plugingetter getter.PluginGetter) (Driver, error) {
 	return nil, ErrNotSupported
 }

+ 4 - 1
layer/layer_store.go

@@ -14,6 +14,7 @@ import (
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/stringid"
+	"github.com/docker/docker/plugin/getter"
 	"github.com/vbatts/tar-split/tar/asm"
 	"github.com/vbatts/tar-split/tar/storage"
 )
@@ -44,6 +45,7 @@ type StoreOptions struct {
 	GraphDriverOptions        []string
 	UIDMaps                   []idtools.IDMap
 	GIDMaps                   []idtools.IDMap
+	PluginGetter              getter.PluginGetter
 }
 
 // NewStoreFromOptions creates a new Store instance
@@ -53,7 +55,8 @@ func NewStoreFromOptions(options StoreOptions) (Store, error) {
 		options.GraphDriver,
 		options.GraphDriverOptions,
 		options.UIDMaps,
-		options.GIDMaps)
+		options.GIDMaps,
+		options.PluginGetter)
 	if err != nil {
 		return nil, fmt.Errorf("error initializing graphdriver: %v", err)
 	}

+ 1 - 1
layer/layer_test.go

@@ -39,7 +39,7 @@ func newVFSGraphDriver(td string) (graphdriver.Driver, error) {
 		},
 	}
 
-	return graphdriver.GetDriver("vfs", td, nil, uidMap, gidMap)
+	return graphdriver.GetDriver("vfs", td, nil, uidMap, gidMap, nil)
 }
 
 func newTestGraphDriver(t *testing.T) (graphdriver.Driver, func()) {

+ 2 - 2
pkg/plugins/plugins.go

@@ -83,8 +83,8 @@ func (p *Plugin) Client() *Client {
 	return p.client
 }
 
-// IsLegacy returns true for legacy plugins and false otherwise.
-func (p *Plugin) IsLegacy() bool {
+// IsV1 returns true for V1 plugins and false otherwise.
+func (p *Plugin) IsV1() bool {
 	return true
 }
 

+ 4 - 8
plugin/backend.go

@@ -17,7 +17,6 @@ import (
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/plugin/distribution"
 	"github.com/docker/docker/plugin/v2"
-	"github.com/docker/docker/reference"
 )
 
 // Disable deactivates a plugin, which implies that they cannot be used by containers.
@@ -57,9 +56,9 @@ func (pm *Manager) Inspect(name string) (tp types.Plugin, err error) {
 
 // Pull pulls a plugin and computes the privileges required to install it.
 func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.AuthConfig) (types.PluginPrivileges, error) {
-	ref, err := reference.ParseNamed(name)
+	ref, err := distribution.GetRef(name)
 	if err != nil {
-		logrus.Debugf("error in reference.ParseNamed: %v", err)
+		logrus.Debugf("error in distribution.GetRef: %v", err)
 		return nil, err
 	}
 	name = ref.String()
@@ -76,7 +75,7 @@ func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.A
 		return nil, err
 	}
 
-	pd, err := distribution.Pull(name, pm.registryService, metaHeader, authConfig)
+	pd, err := distribution.Pull(ref, pm.registryService, metaHeader, authConfig)
 	if err != nil {
 		logrus.Debugf("error in distribution.Pull(): %v", err)
 		return nil, err
@@ -87,10 +86,7 @@ func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.A
 		return nil, err
 	}
 
-	var tag string
-	if ref, ok := ref.(reference.NamedTagged); ok {
-		tag = ref.Tag()
-	}
+	tag := distribution.GetTag(ref)
 	p := v2.NewPlugin(ref.Name(), pluginID, pm.runRoot, tag)
 	if err := p.InitPlugin(pm.libRoot); err != nil {
 		return nil, err

+ 15 - 3
plugin/distribution/pull.go

@@ -62,14 +62,26 @@ func (pd *pullData) Layer() (io.ReadCloser, error) {
 	return rsc, nil
 }
 
-// Pull downloads the plugin from Store
-func Pull(name string, rs registry.Service, metaheader http.Header, authConfig *types.AuthConfig) (PullData, error) {
+// GetRef returns the distribution reference for a given name.
+func GetRef(name string) (reference.Named, error) {
 	ref, err := reference.ParseNamed(name)
 	if err != nil {
-		logrus.Debugf("pull.go: error in ParseNamed: %v", err)
 		return nil, err
 	}
+	return ref, nil
+}
 
+// GetTag returns the tag associated with the given reference name.
+func GetTag(ref reference.Named) string {
+	tag := DefaultTag
+	if ref, ok := ref.(reference.NamedTagged); ok {
+		tag = ref.Tag()
+	}
+	return tag
+}
+
+// Pull downloads the plugin from Store
+func Pull(ref reference.Named, rs registry.Service, metaheader http.Header, authConfig *types.AuthConfig) (PullData, error) {
 	repoInfo, err := rs.ResolveRepository(ref)
 	if err != nil {
 		logrus.Debugf("pull.go: error in ResolveRepository: %v", err)

+ 25 - 0
plugin/getter/interface.go

@@ -0,0 +1,25 @@
+package getter
+
+import "github.com/docker/docker/pkg/plugins"
+
+const (
+	// LOOKUP doesn't update RefCount
+	LOOKUP = 0
+	// CREATE increments RefCount
+	CREATE = 1
+	// REMOVE decrements RefCount
+	REMOVE = -1
+)
+
+// CompatPlugin is a abstraction to handle both v2(new) and v1(legacy) plugins.
+type CompatPlugin interface {
+	Client() *plugins.Client
+	Name() string
+	IsV1() bool
+}
+
+// PluginGetter is the interface implemented by Store
+type PluginGetter interface {
+	Get(name, capability string, mode int) (CompatPlugin, error)
+	GetAllByCap(capability string) ([]CompatPlugin, error)
+}

+ 3 - 3
plugin/manager.go

@@ -35,7 +35,7 @@ type Manager struct {
 	sync.RWMutex
 	libRoot           string
 	runRoot           string
-	pluginStore       *store.PluginStore
+	pluginStore       *store.Store
 	containerdClient  libcontainerd.Client
 	registryService   registry.Service
 	liveRestore       bool
@@ -50,7 +50,7 @@ func GetManager() *Manager {
 
 // Init (was NewManager) instantiates the singleton Manager.
 // TODO: revert this to NewManager once we get rid of all the singletons.
-func Init(root string, remote libcontainerd.Remote, rs registry.Service, liveRestore bool, evL eventLogger) (err error) {
+func Init(root string, ps *store.Store, remote libcontainerd.Remote, rs registry.Service, liveRestore bool, evL eventLogger) (err error) {
 	if manager != nil {
 		return nil
 	}
@@ -59,7 +59,7 @@ func Init(root string, remote libcontainerd.Remote, rs registry.Service, liveRes
 	manager = &Manager{
 		libRoot:           root,
 		runRoot:           "/run/docker",
-		pluginStore:       store.NewPluginStore(root),
+		pluginStore:       ps,
 		registryService:   rs,
 		liveRestore:       liveRestore,
 		pluginEventLogger: evL,

+ 31 - 0
plugin/store/defs.go

@@ -0,0 +1,31 @@
+package store
+
+import (
+	"path/filepath"
+	"sync"
+
+	"github.com/docker/docker/pkg/plugins"
+	"github.com/docker/docker/plugin/v2"
+)
+
+// Store manages the plugin inventory in memory and on-disk
+type Store struct {
+	sync.RWMutex
+	plugins map[string]*v2.Plugin
+	/* handlers are necessary for transition path of legacy plugins
+	 * to the new model. Legacy plugins use Handle() for registering an
+	 * activation callback.*/
+	handlers map[string]func(string, *plugins.Client)
+	nameToID map[string]string
+	plugindb string
+}
+
+// NewStore creates a Store.
+func NewStore(libRoot string) *Store {
+	return &Store{
+		plugins:  make(map[string]*v2.Plugin),
+		handlers: make(map[string]func(string, *plugins.Client)),
+		nameToID: make(map[string]string),
+		plugindb: filepath.Join(libRoot, "plugins", "plugins.json"),
+	}
+}

+ 0 - 19
plugin/store/interface.go

@@ -1,19 +0,0 @@
-package store
-
-import "github.com/docker/docker/pkg/plugins"
-
-const (
-	// LOOKUP doesn't update RefCount
-	LOOKUP = 0
-	// CREATE increments RefCount
-	CREATE = 1
-	// REMOVE decrements RefCount
-	REMOVE = -1
-)
-
-// CompatPlugin is an abstraction to handle both new and legacy (v1) plugins.
-type CompatPlugin interface {
-	Client() *plugins.Client
-	Name() string
-	IsLegacy() bool
-}

+ 0 - 25
plugin/store/legacy.go

@@ -1,25 +0,0 @@
-// +build !experimental
-
-package store
-
-import (
-	"github.com/docker/docker/pkg/plugins"
-)
-
-// FindWithCapability returns a list of plugins matching the given capability.
-func FindWithCapability(capability string) ([]CompatPlugin, error) {
-	pl, err := plugins.GetAll(capability)
-	if err != nil {
-		return nil, err
-	}
-	result := make([]CompatPlugin, len(pl))
-	for i, p := range pl {
-		result[i] = p
-	}
-	return result, nil
-}
-
-// LookupWithCapability returns a plugin matching the given name and capability.
-func LookupWithCapability(name, capability string, _ int) (CompatPlugin, error) {
-	return plugins.Get(name, capability)
-}

+ 11 - 242
plugin/store/store.go

@@ -1,257 +1,26 @@
-// +build experimental
+// +build !experimental
 
 package store
 
 import (
-	"encoding/json"
-	"fmt"
-	"path/filepath"
-	"strings"
-	"sync"
-
-	"github.com/Sirupsen/logrus"
-	"github.com/docker/docker/pkg/ioutils"
 	"github.com/docker/docker/pkg/plugins"
-	"github.com/docker/docker/plugin/v2"
-	"github.com/docker/docker/reference"
-)
-
-var (
-	store *PluginStore
-	/* allowV1PluginsFallback determines daemon's support for V1 plugins.
-	 * When the time comes to remove support for V1 plugins, flipping
-	 * this bool is all that will be needed.
-	 */
-	allowV1PluginsFallback = true
+	"github.com/docker/docker/plugin/getter"
 )
 
-// ErrNotFound indicates that a plugin was not found locally.
-type ErrNotFound string
-
-func (name ErrNotFound) Error() string { return fmt.Sprintf("plugin %q not found", string(name)) }
-
-// PluginStore manages the plugin inventory in memory and on-disk
-type PluginStore struct {
-	sync.RWMutex
-	plugins map[string]*v2.Plugin
-	/* handlers are necessary for transition path of legacy plugins
-	 * to the new model. Legacy plugins use Handle() for registering an
-	 * activation callback.*/
-	handlers map[string]func(string, *plugins.Client)
-	nameToID map[string]string
-	plugindb string
-}
-
-// NewPluginStore creates a PluginStore.
-func NewPluginStore(libRoot string) *PluginStore {
-	store = &PluginStore{
-		plugins:  make(map[string]*v2.Plugin),
-		handlers: make(map[string]func(string, *plugins.Client)),
-		nameToID: make(map[string]string),
-		plugindb: filepath.Join(libRoot, "plugins.json"),
-	}
-	return store
-}
-
-// GetByName retreives a plugin by name.
-func (ps *PluginStore) GetByName(name string) (*v2.Plugin, error) {
-	ps.RLock()
-	defer ps.RUnlock()
-
-	id, nameOk := ps.nameToID[name]
-	if !nameOk {
-		return nil, ErrNotFound(name)
-	}
-
-	p, idOk := ps.plugins[id]
-	if !idOk {
-		return nil, ErrNotFound(id)
-	}
-	return p, nil
-}
-
-// GetByID retreives a plugin by ID.
-func (ps *PluginStore) GetByID(id string) (*v2.Plugin, error) {
-	ps.RLock()
-	defer ps.RUnlock()
-
-	p, idOk := ps.plugins[id]
-	if !idOk {
-		return nil, ErrNotFound(id)
-	}
-	return p, nil
-}
-
-// GetAll retreives all plugins.
-func (ps *PluginStore) GetAll() map[string]*v2.Plugin {
-	ps.RLock()
-	defer ps.RUnlock()
-	return ps.plugins
-}
-
-// SetAll initialized plugins during daemon restore.
-func (ps *PluginStore) SetAll(plugins map[string]*v2.Plugin) {
-	ps.Lock()
-	defer ps.Unlock()
-	ps.plugins = plugins
-}
-
-func (ps *PluginStore) getByCap(name string, capability string) (*v2.Plugin, error) {
-	ps.RLock()
-	defer ps.RUnlock()
-
-	p, err := ps.GetByName(name)
+// GetAllByCap returns a list of plugins matching the given capability.
+func (ps Store) GetAllByCap(capability string) ([]getter.CompatPlugin, error) {
+	pl, err := plugins.GetAll(capability)
 	if err != nil {
 		return nil, err
 	}
-	return p.FilterByCap(capability)
-}
-
-func (ps *PluginStore) getAllByCap(capability string) []CompatPlugin {
-	ps.RLock()
-	defer ps.RUnlock()
-
-	result := make([]CompatPlugin, 0, 1)
-	for _, p := range ps.plugins {
-		if _, err := p.FilterByCap(capability); err == nil {
-			result = append(result, p)
-		}
-	}
-	return result
-}
-
-// SetState sets the active state of the plugin and updates plugindb.
-func (ps *PluginStore) SetState(p *v2.Plugin, state bool) {
-	ps.Lock()
-	defer ps.Unlock()
-
-	p.PluginObj.Enabled = state
-	ps.updatePluginDB()
-}
-
-// Add adds a plugin to memory and plugindb.
-func (ps *PluginStore) Add(p *v2.Plugin) {
-	ps.Lock()
-	ps.plugins[p.GetID()] = p
-	ps.nameToID[p.Name()] = p.GetID()
-	ps.updatePluginDB()
-	ps.Unlock()
-}
-
-// Remove removes a plugin from memory, plugindb and disk.
-func (ps *PluginStore) Remove(p *v2.Plugin) {
-	ps.Lock()
-	delete(ps.plugins, p.GetID())
-	delete(ps.nameToID, p.Name())
-	ps.updatePluginDB()
-	p.RemoveFromDisk()
-	ps.Unlock()
-}
-
-// Callers are expected to hold the store lock.
-func (ps *PluginStore) updatePluginDB() error {
-	jsonData, err := json.Marshal(ps.plugins)
-	if err != nil {
-		logrus.Debugf("Error in json.Marshal: %v", err)
-		return err
-	}
-	ioutils.AtomicWriteFile(ps.plugindb, jsonData, 0600)
-	return nil
-}
-
-// LookupWithCapability returns a plugin matching the given name and capability.
-func LookupWithCapability(name, capability string, mode int) (CompatPlugin, error) {
-	var (
-		p   *v2.Plugin
-		err error
-	)
-
-	// Lookup using new model.
-	if store != nil {
-		fullName := name
-		if named, err := reference.ParseNamed(fullName); err == nil { // FIXME: validate
-			if reference.IsNameOnly(named) {
-				named = reference.WithDefaultTag(named)
-			}
-			ref, ok := named.(reference.NamedTagged)
-			if !ok {
-				return nil, fmt.Errorf("invalid name: %s", named.String())
-			}
-			fullName = ref.String()
-		}
-		p, err = store.GetByName(fullName)
-		if err == nil {
-			p.Lock()
-			p.RefCount += mode
-			p.Unlock()
-			return p.FilterByCap(capability)
-		}
-		if _, ok := err.(ErrNotFound); !ok {
-			return nil, err
-		}
-	}
-
-	// Lookup using legacy model.
-	if allowV1PluginsFallback {
-		p, err := plugins.Get(name, capability)
-		if err != nil {
-			return nil, fmt.Errorf("legacy plugin: %v", err)
-		}
-		return p, nil
-	}
-
-	return nil, err
-}
-
-// FindWithCapability returns a list of plugins matching the given capability.
-func FindWithCapability(capability string) ([]CompatPlugin, error) {
-	result := make([]CompatPlugin, 0, 1)
-
-	/* Daemon start always calls plugin.Init thereby initializing a store.
-	 * So store on experimental builds can never be nil, even while
-	 * handling legacy plugins. However, there are legacy plugin unit
-	 * tests where the volume subsystem directly talks with the plugin,
-	 * bypassing the daemon. For such tests, this check is necessary.
-	 */
-	if store != nil {
-		store.RLock()
-		result = store.getAllByCap(capability)
-		store.RUnlock()
-	}
-
-	// Lookup with legacy model
-	if allowV1PluginsFallback {
-		pl, err := plugins.GetAll(capability)
-		if err != nil {
-			return nil, fmt.Errorf("legacy plugin: %v", err)
-		}
-		for _, p := range pl {
-			result = append(result, p)
-		}
+	result := make([]getter.CompatPlugin, len(pl))
+	for i, p := range pl {
+		result[i] = p
 	}
 	return result, nil
 }
 
-// Handle sets a callback for a given capability. It is only used by network
-// and ipam drivers during plugin registration. The callback registers the
-// driver with the subsystem (network, ipam).
-func Handle(capability string, callback func(string, *plugins.Client)) {
-	pluginType := fmt.Sprintf("docker.%s/1", strings.ToLower(capability))
-
-	// Register callback with new plugin model.
-	store.handlers[pluginType] = callback
-
-	// Register callback with legacy plugin model.
-	if allowV1PluginsFallback {
-		plugins.Handle(capability, callback)
-	}
-}
-
-// CallHandler calls the registered callback. It is invoked during plugin enable.
-func (ps *PluginStore) CallHandler(p *v2.Plugin) {
-	for _, typ := range p.GetTypes() {
-		if handler := ps.handlers[typ.String()]; handler != nil {
-			handler(p.Name(), p.Client())
-		}
-	}
+// Get returns a plugin matching the given name and capability.
+func (ps Store) Get(name, capability string, _ int) (getter.CompatPlugin, error) {
+	return plugins.Get(name, capability)
 }

+ 234 - 0
plugin/store/store_experimental.go

@@ -0,0 +1,234 @@
+// +build experimental
+
+package store
+
+import (
+	"encoding/json"
+	"fmt"
+	"strings"
+
+	"github.com/Sirupsen/logrus"
+	"github.com/docker/docker/pkg/ioutils"
+	"github.com/docker/docker/pkg/plugins"
+	"github.com/docker/docker/plugin/getter"
+	"github.com/docker/docker/plugin/v2"
+	"github.com/docker/docker/reference"
+)
+
+/* allowV1PluginsFallback determines daemon's support for V1 plugins.
+ * When the time comes to remove support for V1 plugins, flipping
+ * this bool is all that will be needed.
+ */
+var allowV1PluginsFallback = true
+
+// ErrNotFound indicates that a plugin was not found locally.
+type ErrNotFound string
+
+func (name ErrNotFound) Error() string { return fmt.Sprintf("plugin %q not found", string(name)) }
+
+// GetByName retreives a plugin by name.
+func (ps *Store) GetByName(name string) (*v2.Plugin, error) {
+	ps.RLock()
+	defer ps.RUnlock()
+
+	id, nameOk := ps.nameToID[name]
+	if !nameOk {
+		return nil, ErrNotFound(name)
+	}
+
+	p, idOk := ps.plugins[id]
+	if !idOk {
+		return nil, ErrNotFound(id)
+	}
+	return p, nil
+}
+
+// GetByID retreives a plugin by ID.
+func (ps *Store) GetByID(id string) (*v2.Plugin, error) {
+	ps.RLock()
+	defer ps.RUnlock()
+
+	p, idOk := ps.plugins[id]
+	if !idOk {
+		return nil, ErrNotFound(id)
+	}
+	return p, nil
+}
+
+// GetAll retreives all plugins.
+func (ps *Store) GetAll() map[string]*v2.Plugin {
+	ps.RLock()
+	defer ps.RUnlock()
+	return ps.plugins
+}
+
+// SetAll initialized plugins during daemon restore.
+func (ps *Store) SetAll(plugins map[string]*v2.Plugin) {
+	ps.Lock()
+	defer ps.Unlock()
+	ps.plugins = plugins
+}
+
+func (ps *Store) getByCap(name string, capability string) (*v2.Plugin, error) {
+	ps.RLock()
+	defer ps.RUnlock()
+
+	p, err := ps.GetByName(name)
+	if err != nil {
+		return nil, err
+	}
+	return p.FilterByCap(capability)
+}
+
+func (ps *Store) getAllByCap(capability string) []getter.CompatPlugin {
+	ps.RLock()
+	defer ps.RUnlock()
+
+	result := make([]getter.CompatPlugin, 0, 1)
+	for _, p := range ps.plugins {
+		if _, err := p.FilterByCap(capability); err == nil {
+			result = append(result, p)
+		}
+	}
+	return result
+}
+
+// SetState sets the active state of the plugin and updates plugindb.
+func (ps *Store) SetState(p *v2.Plugin, state bool) {
+	ps.Lock()
+	defer ps.Unlock()
+
+	p.PluginObj.Enabled = state
+	ps.updatePluginDB()
+}
+
+// Add adds a plugin to memory and plugindb.
+func (ps *Store) Add(p *v2.Plugin) {
+	ps.Lock()
+	ps.plugins[p.GetID()] = p
+	ps.nameToID[p.Name()] = p.GetID()
+	ps.updatePluginDB()
+	ps.Unlock()
+}
+
+// Remove removes a plugin from memory, plugindb and disk.
+func (ps *Store) Remove(p *v2.Plugin) {
+	ps.Lock()
+	delete(ps.plugins, p.GetID())
+	delete(ps.nameToID, p.Name())
+	ps.updatePluginDB()
+	p.RemoveFromDisk()
+	ps.Unlock()
+}
+
+// Callers are expected to hold the store lock.
+func (ps *Store) updatePluginDB() error {
+	jsonData, err := json.Marshal(ps.plugins)
+	if err != nil {
+		logrus.Debugf("Error in json.Marshal: %v", err)
+		return err
+	}
+	ioutils.AtomicWriteFile(ps.plugindb, jsonData, 0600)
+	return nil
+}
+
+// Get returns a plugin matching the given name and capability.
+func (ps *Store) Get(name, capability string, mode int) (getter.CompatPlugin, error) {
+	var (
+		p   *v2.Plugin
+		err error
+	)
+
+	// Lookup using new model.
+	if ps != nil {
+		fullName := name
+		if named, err := reference.ParseNamed(fullName); err == nil { // FIXME: validate
+			if reference.IsNameOnly(named) {
+				named = reference.WithDefaultTag(named)
+			}
+			ref, ok := named.(reference.NamedTagged)
+			if !ok {
+				return nil, fmt.Errorf("invalid name: %s", named.String())
+			}
+			fullName = ref.String()
+		}
+		p, err = ps.GetByName(fullName)
+		if err == nil {
+			p.Lock()
+			p.RefCount += mode
+			p.Unlock()
+			return p.FilterByCap(capability)
+		}
+		if _, ok := err.(ErrNotFound); !ok {
+			return nil, err
+		}
+	}
+
+	// Lookup using legacy model.
+	if allowV1PluginsFallback {
+		p, err := plugins.Get(name, capability)
+		if err != nil {
+			return nil, fmt.Errorf("legacy plugin: %v", err)
+		}
+		return p, nil
+	}
+
+	return nil, err
+}
+
+// GetAllByCap returns a list of plugins matching the given capability.
+func (ps *Store) GetAllByCap(capability string) ([]getter.CompatPlugin, error) {
+	result := make([]getter.CompatPlugin, 0, 1)
+
+	/* Daemon start always calls plugin.Init thereby initializing a store.
+	 * So store on experimental builds can never be nil, even while
+	 * handling legacy plugins. However, there are legacy plugin unit
+	 * tests where the volume subsystem directly talks with the plugin,
+	 * bypassing the daemon. For such tests, this check is necessary.
+	 */
+	if ps != nil {
+		ps.RLock()
+		result = ps.getAllByCap(capability)
+		ps.RUnlock()
+	}
+
+	// Lookup with legacy model
+	if allowV1PluginsFallback {
+		pl, err := plugins.GetAll(capability)
+		if err != nil {
+			return nil, fmt.Errorf("legacy plugin: %v", err)
+		}
+		for _, p := range pl {
+			result = append(result, p)
+		}
+	}
+	return result, nil
+}
+
+// Handle sets a callback for a given capability. It is only used by network
+// and ipam drivers during plugin registration. The callback registers the
+// driver with the subsystem (network, ipam).
+func (ps Store) Handle(capability string, callback func(string, *plugins.Client)) {
+	pluginType := fmt.Sprintf("docker.%s/1", strings.ToLower(capability))
+
+	store := &ps
+
+	// Register callback with new plugin model.
+	store.Lock()
+	store.handlers[pluginType] = callback
+	store.Unlock()
+
+	// Register callback with legacy plugin model.
+	if allowV1PluginsFallback {
+		plugins.Handle(capability, callback)
+	}
+}
+
+// CallHandler calls the registered callback. It is invoked during plugin enable.
+func (ps *Store) CallHandler(p *v2.Plugin) {
+	for _, typ := range p.GetTypes() {
+		if handler := ps.handlers[typ.String()]; handler != nil {
+			handler(p.Name(), p.Client())
+		}
+	}
+}

+ 0 - 242
plugin/v2/plugin.go

@@ -1,32 +1,13 @@
-// +build experimental
-
 package v2
 
 import (
-	"encoding/json"
-	"errors"
-	"fmt"
-	"os"
-	"path/filepath"
-	"strings"
 	"sync"
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/pkg/plugins"
-	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/restartmanager"
-	"github.com/opencontainers/runtime-spec/specs-go"
 )
 
-const defaultPluginRuntimeDestination = "/run/docker/plugins"
-
-// ErrInadequateCapability indicates that the plugin did not have the requested capability.
-type ErrInadequateCapability string
-
-func (cap ErrInadequateCapability) Error() string {
-	return fmt.Sprintf("plugin does not provide %q capability", cap)
-}
-
 // Plugin represents an individual plugin.
 type Plugin struct {
 	sync.RWMutex
@@ -37,226 +18,3 @@ type Plugin struct {
 	ExitChan          chan bool                     `json:"-"`
 	RefCount          int                           `json:"-"`
 }
-
-func newPluginObj(name, id, tag string) types.Plugin {
-	return types.Plugin{Name: name, ID: id, Tag: tag}
-}
-
-// NewPlugin creates a plugin.
-func NewPlugin(name, id, runRoot, tag string) *Plugin {
-	return &Plugin{
-		PluginObj:         newPluginObj(name, id, tag),
-		RuntimeSourcePath: filepath.Join(runRoot, id),
-	}
-}
-
-// Client returns the plugin client.
-func (p *Plugin) Client() *plugins.Client {
-	return p.PClient
-}
-
-// IsLegacy returns true for legacy plugins and false otherwise.
-func (p *Plugin) IsLegacy() bool {
-	return false
-}
-
-// Name returns the plugin name.
-func (p *Plugin) Name() string {
-	name := p.PluginObj.Name
-	if len(p.PluginObj.Tag) > 0 {
-		// TODO: this feels hacky, maybe we should be storing the distribution reference rather than splitting these
-		name += ":" + p.PluginObj.Tag
-	}
-	return name
-}
-
-// FilterByCap query the plugin for a given capability.
-func (p *Plugin) FilterByCap(capability string) (*Plugin, error) {
-	capability = strings.ToLower(capability)
-	for _, typ := range p.PluginObj.Manifest.Interface.Types {
-		if typ.Capability == capability && typ.Prefix == "docker" {
-			return p, nil
-		}
-	}
-	return nil, ErrInadequateCapability(capability)
-}
-
-// RemoveFromDisk deletes the plugin's runtime files from disk.
-func (p *Plugin) RemoveFromDisk() error {
-	return os.RemoveAll(p.RuntimeSourcePath)
-}
-
-// InitPlugin populates the plugin object from the plugin manifest file.
-func (p *Plugin) InitPlugin(libRoot string) error {
-	dt, err := os.Open(filepath.Join(libRoot, p.PluginObj.ID, "manifest.json"))
-	if err != nil {
-		return err
-	}
-	err = json.NewDecoder(dt).Decode(&p.PluginObj.Manifest)
-	dt.Close()
-	if err != nil {
-		return err
-	}
-
-	p.PluginObj.Config.Mounts = make([]types.PluginMount, len(p.PluginObj.Manifest.Mounts))
-	for i, mount := range p.PluginObj.Manifest.Mounts {
-		p.PluginObj.Config.Mounts[i] = mount
-	}
-	p.PluginObj.Config.Env = make([]string, 0, len(p.PluginObj.Manifest.Env))
-	for _, env := range p.PluginObj.Manifest.Env {
-		if env.Value != nil {
-			p.PluginObj.Config.Env = append(p.PluginObj.Config.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value))
-		}
-	}
-	copy(p.PluginObj.Config.Args, p.PluginObj.Manifest.Args.Value)
-
-	f, err := os.Create(filepath.Join(libRoot, p.PluginObj.ID, "plugin-config.json"))
-	if err != nil {
-		return err
-	}
-	err = json.NewEncoder(f).Encode(&p.PluginObj.Config)
-	f.Close()
-	return err
-}
-
-// Set is used to pass arguments to the plugin.
-func (p *Plugin) Set(args []string) error {
-	m := make(map[string]string, len(args))
-	for _, arg := range args {
-		i := strings.Index(arg, "=")
-		if i < 0 {
-			return fmt.Errorf("No equal sign '=' found in %s", arg)
-		}
-		m[arg[:i]] = arg[i+1:]
-	}
-	return errors.New("not implemented")
-}
-
-// ComputePrivileges takes the manifest file and computes the list of access necessary
-// for the plugin on the host.
-func (p *Plugin) ComputePrivileges() types.PluginPrivileges {
-	m := p.PluginObj.Manifest
-	var privileges types.PluginPrivileges
-	if m.Network.Type != "null" && m.Network.Type != "bridge" {
-		privileges = append(privileges, types.PluginPrivilege{
-			Name:        "network",
-			Description: "",
-			Value:       []string{m.Network.Type},
-		})
-	}
-	for _, mount := range m.Mounts {
-		if mount.Source != nil {
-			privileges = append(privileges, types.PluginPrivilege{
-				Name:        "mount",
-				Description: "",
-				Value:       []string{*mount.Source},
-			})
-		}
-	}
-	for _, device := range m.Devices {
-		if device.Path != nil {
-			privileges = append(privileges, types.PluginPrivilege{
-				Name:        "device",
-				Description: "",
-				Value:       []string{*device.Path},
-			})
-		}
-	}
-	if len(m.Capabilities) > 0 {
-		privileges = append(privileges, types.PluginPrivilege{
-			Name:        "capabilities",
-			Description: "",
-			Value:       m.Capabilities,
-		})
-	}
-	return privileges
-}
-
-// IsEnabled returns the active state of the plugin.
-func (p *Plugin) IsEnabled() bool {
-	p.RLock()
-	defer p.RUnlock()
-
-	return p.PluginObj.Enabled
-}
-
-// GetID returns the plugin's ID.
-func (p *Plugin) GetID() string {
-	p.RLock()
-	defer p.RUnlock()
-
-	return p.PluginObj.ID
-}
-
-// GetSocket returns the plugin socket.
-func (p *Plugin) GetSocket() string {
-	p.RLock()
-	defer p.RUnlock()
-
-	return p.PluginObj.Manifest.Interface.Socket
-}
-
-// GetTypes returns the interface types of a plugin.
-func (p *Plugin) GetTypes() []types.PluginInterfaceType {
-	p.RLock()
-	defer p.RUnlock()
-
-	return p.PluginObj.Manifest.Interface.Types
-}
-
-// InitSpec creates an OCI spec from the plugin's config.
-func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
-	rootfs := filepath.Join(libRoot, p.PluginObj.ID, "rootfs")
-	s.Root = specs.Root{
-		Path:     rootfs,
-		Readonly: false, // TODO: all plugins should be readonly? settable in manifest?
-	}
-
-	mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
-		Source:      &p.RuntimeSourcePath,
-		Destination: defaultPluginRuntimeDestination,
-		Type:        "bind",
-		Options:     []string{"rbind", "rshared"},
-	})
-	for _, mount := range mounts {
-		m := specs.Mount{
-			Destination: mount.Destination,
-			Type:        mount.Type,
-			Options:     mount.Options,
-		}
-		// TODO: if nil, then it's required and user didn't set it
-		if mount.Source != nil {
-			m.Source = *mount.Source
-		}
-		if m.Source != "" && m.Type == "bind" {
-			fi, err := os.Lstat(filepath.Join(rootfs, string(os.PathSeparator), m.Destination)) // TODO: followsymlinks
-			if err != nil {
-				return nil, err
-			}
-			if fi.IsDir() {
-				if err := os.MkdirAll(m.Source, 0700); err != nil {
-					return nil, err
-				}
-			}
-		}
-		s.Mounts = append(s.Mounts, m)
-	}
-
-	envs := make([]string, 1, len(p.PluginObj.Config.Env)+1)
-	envs[0] = "PATH=" + system.DefaultPathEnv
-	envs = append(envs, p.PluginObj.Config.Env...)
-
-	args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...)
-	cwd := p.PluginObj.Manifest.Workdir
-	if len(cwd) == 0 {
-		cwd = "/"
-	}
-	s.Process = specs.Process{
-		Terminal: false,
-		Args:     args,
-		Cwd:      cwd,
-		Env:      envs,
-	}
-
-	return &s, nil
-}

+ 249 - 0
plugin/v2/plugin_experimental.go

@@ -0,0 +1,249 @@
+// +build experimental
+
+package v2
+
+import (
+	"encoding/json"
+	"errors"
+	"fmt"
+	"os"
+	"path/filepath"
+	"strings"
+
+	"github.com/docker/docker/api/types"
+	"github.com/docker/docker/pkg/plugins"
+	"github.com/docker/docker/pkg/system"
+	"github.com/opencontainers/runtime-spec/specs-go"
+)
+
+const defaultPluginRuntimeDestination = "/run/docker/plugins"
+
+// ErrInadequateCapability indicates that the plugin did not have the requested capability.
+type ErrInadequateCapability string
+
+func (cap ErrInadequateCapability) Error() string {
+	return fmt.Sprintf("plugin does not provide %q capability", cap)
+}
+
+func newPluginObj(name, id, tag string) types.Plugin {
+	return types.Plugin{Name: name, ID: id, Tag: tag}
+}
+
+// NewPlugin creates a plugin.
+func NewPlugin(name, id, runRoot, tag string) *Plugin {
+	return &Plugin{
+		PluginObj:         newPluginObj(name, id, tag),
+		RuntimeSourcePath: filepath.Join(runRoot, id),
+	}
+}
+
+// Client returns the plugin client.
+func (p *Plugin) Client() *plugins.Client {
+	return p.PClient
+}
+
+// IsV1 returns true for V1 plugins and false otherwise.
+func (p *Plugin) IsV1() bool {
+	return false
+}
+
+// Name returns the plugin name.
+func (p *Plugin) Name() string {
+	name := p.PluginObj.Name
+	if len(p.PluginObj.Tag) > 0 {
+		// TODO: this feels hacky, maybe we should be storing the distribution reference rather than splitting these
+		name += ":" + p.PluginObj.Tag
+	}
+	return name
+}
+
+// FilterByCap query the plugin for a given capability.
+func (p *Plugin) FilterByCap(capability string) (*Plugin, error) {
+	capability = strings.ToLower(capability)
+	for _, typ := range p.PluginObj.Manifest.Interface.Types {
+		if typ.Capability == capability && typ.Prefix == "docker" {
+			return p, nil
+		}
+	}
+	return nil, ErrInadequateCapability(capability)
+}
+
+// RemoveFromDisk deletes the plugin's runtime files from disk.
+func (p *Plugin) RemoveFromDisk() error {
+	return os.RemoveAll(p.RuntimeSourcePath)
+}
+
+// InitPlugin populates the plugin object from the plugin manifest file.
+func (p *Plugin) InitPlugin(libRoot string) error {
+	dt, err := os.Open(filepath.Join(libRoot, p.PluginObj.ID, "manifest.json"))
+	if err != nil {
+		return err
+	}
+	err = json.NewDecoder(dt).Decode(&p.PluginObj.Manifest)
+	dt.Close()
+	if err != nil {
+		return err
+	}
+
+	p.PluginObj.Config.Mounts = make([]types.PluginMount, len(p.PluginObj.Manifest.Mounts))
+	for i, mount := range p.PluginObj.Manifest.Mounts {
+		p.PluginObj.Config.Mounts[i] = mount
+	}
+	p.PluginObj.Config.Env = make([]string, 0, len(p.PluginObj.Manifest.Env))
+	for _, env := range p.PluginObj.Manifest.Env {
+		if env.Value != nil {
+			p.PluginObj.Config.Env = append(p.PluginObj.Config.Env, fmt.Sprintf("%s=%s", env.Name, *env.Value))
+		}
+	}
+	copy(p.PluginObj.Config.Args, p.PluginObj.Manifest.Args.Value)
+
+	f, err := os.Create(filepath.Join(libRoot, p.PluginObj.ID, "plugin-config.json"))
+	if err != nil {
+		return err
+	}
+	err = json.NewEncoder(f).Encode(&p.PluginObj.Config)
+	f.Close()
+	return err
+}
+
+// Set is used to pass arguments to the plugin.
+func (p *Plugin) Set(args []string) error {
+	m := make(map[string]string, len(args))
+	for _, arg := range args {
+		i := strings.Index(arg, "=")
+		if i < 0 {
+			return fmt.Errorf("No equal sign '=' found in %s", arg)
+		}
+		m[arg[:i]] = arg[i+1:]
+	}
+	return errors.New("not implemented")
+}
+
+// ComputePrivileges takes the manifest file and computes the list of access necessary
+// for the plugin on the host.
+func (p *Plugin) ComputePrivileges() types.PluginPrivileges {
+	m := p.PluginObj.Manifest
+	var privileges types.PluginPrivileges
+	if m.Network.Type != "null" && m.Network.Type != "bridge" {
+		privileges = append(privileges, types.PluginPrivilege{
+			Name:        "network",
+			Description: "",
+			Value:       []string{m.Network.Type},
+		})
+	}
+	for _, mount := range m.Mounts {
+		if mount.Source != nil {
+			privileges = append(privileges, types.PluginPrivilege{
+				Name:        "mount",
+				Description: "",
+				Value:       []string{*mount.Source},
+			})
+		}
+	}
+	for _, device := range m.Devices {
+		if device.Path != nil {
+			privileges = append(privileges, types.PluginPrivilege{
+				Name:        "device",
+				Description: "",
+				Value:       []string{*device.Path},
+			})
+		}
+	}
+	if len(m.Capabilities) > 0 {
+		privileges = append(privileges, types.PluginPrivilege{
+			Name:        "capabilities",
+			Description: "",
+			Value:       m.Capabilities,
+		})
+	}
+	return privileges
+}
+
+// IsEnabled returns the active state of the plugin.
+func (p *Plugin) IsEnabled() bool {
+	p.RLock()
+	defer p.RUnlock()
+
+	return p.PluginObj.Enabled
+}
+
+// GetID returns the plugin's ID.
+func (p *Plugin) GetID() string {
+	p.RLock()
+	defer p.RUnlock()
+
+	return p.PluginObj.ID
+}
+
+// GetSocket returns the plugin socket.
+func (p *Plugin) GetSocket() string {
+	p.RLock()
+	defer p.RUnlock()
+
+	return p.PluginObj.Manifest.Interface.Socket
+}
+
+// GetTypes returns the interface types of a plugin.
+func (p *Plugin) GetTypes() []types.PluginInterfaceType {
+	p.RLock()
+	defer p.RUnlock()
+
+	return p.PluginObj.Manifest.Interface.Types
+}
+
+// InitSpec creates an OCI spec from the plugin's config.
+func (p *Plugin) InitSpec(s specs.Spec, libRoot string) (*specs.Spec, error) {
+	rootfs := filepath.Join(libRoot, p.PluginObj.ID, "rootfs")
+	s.Root = specs.Root{
+		Path:     rootfs,
+		Readonly: false, // TODO: all plugins should be readonly? settable in manifest?
+	}
+
+	mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
+		Source:      &p.RuntimeSourcePath,
+		Destination: defaultPluginRuntimeDestination,
+		Type:        "bind",
+		Options:     []string{"rbind", "rshared"},
+	})
+	for _, mount := range mounts {
+		m := specs.Mount{
+			Destination: mount.Destination,
+			Type:        mount.Type,
+			Options:     mount.Options,
+		}
+		// TODO: if nil, then it's required and user didn't set it
+		if mount.Source != nil {
+			m.Source = *mount.Source
+		}
+		if m.Source != "" && m.Type == "bind" {
+			fi, err := os.Lstat(filepath.Join(rootfs, m.Destination)) // TODO: followsymlinks
+			if err != nil {
+				return nil, err
+			}
+			if fi.IsDir() {
+				if err := os.MkdirAll(m.Source, 0700); err != nil {
+					return nil, err
+				}
+			}
+		}
+		s.Mounts = append(s.Mounts, m)
+	}
+
+	envs := make([]string, 1, len(p.PluginObj.Config.Env)+1)
+	envs[0] = "PATH=" + system.DefaultPathEnv
+	envs = append(envs, p.PluginObj.Config.Env...)
+
+	args := append(p.PluginObj.Manifest.Entrypoint, p.PluginObj.Config.Args...)
+	cwd := p.PluginObj.Manifest.Workdir
+	if len(cwd) == 0 {
+		cwd = "/"
+	}
+	s.Process = specs.Process{
+		Terminal: false,
+		Args:     args,
+		Cwd:      cwd,
+		Env:      envs,
+	}
+
+	return &s, nil
+}

+ 20 - 10
volume/drivers/extpoint.go

@@ -7,14 +7,17 @@ import (
 	"sync"
 
 	"github.com/docker/docker/pkg/locker"
-	pluginStore "github.com/docker/docker/plugin/store"
+	"github.com/docker/docker/plugin/getter"
 	"github.com/docker/docker/volume"
 )
 
 // currently created by hand. generation tool would generate this like:
 // $ extpoint-gen Driver > volume/extpoint.go
 
-var drivers = &driverExtpoint{extensions: make(map[string]volume.Driver), driverLock: &locker.Locker{}}
+var drivers = &driverExtpoint{
+	extensions: make(map[string]volume.Driver),
+	driverLock: &locker.Locker{},
+}
 
 const extName = "VolumeDriver"
 
@@ -49,7 +52,13 @@ type volumeDriver interface {
 type driverExtpoint struct {
 	extensions map[string]volume.Driver
 	sync.Mutex
-	driverLock *locker.Locker
+	driverLock   *locker.Locker
+	plugingetter getter.PluginGetter
+}
+
+// RegisterPluginGetter sets the plugingetter
+func RegisterPluginGetter(plugingetter getter.PluginGetter) {
+	drivers.plugingetter = plugingetter
 }
 
 // Register associates the given driver to the given name, checking if
@@ -72,6 +81,7 @@ func Register(extension volume.Driver, name string) bool {
 	}
 
 	drivers.extensions[name] = extension
+
 	return true
 }
 
@@ -102,7 +112,7 @@ func lookup(name string, mode int) (volume.Driver, error) {
 		return ext, nil
 	}
 
-	p, err := pluginStore.LookupWithCapability(name, extName, mode)
+	p, err := drivers.plugingetter.Get(name, extName, mode)
 	if err != nil {
 		return nil, fmt.Errorf("Error looking up volume plugin %s: %v", name, err)
 	}
@@ -112,7 +122,7 @@ func lookup(name string, mode int) (volume.Driver, error) {
 		return nil, err
 	}
 
-	if p.IsLegacy() {
+	if p.IsV1() {
 		drivers.Lock()
 		drivers.extensions[name] = d
 		drivers.Unlock()
@@ -134,7 +144,7 @@ func GetDriver(name string) (volume.Driver, error) {
 	if name == "" {
 		name = volume.DefaultDriverName
 	}
-	return lookup(name, pluginStore.LOOKUP)
+	return lookup(name, getter.LOOKUP)
 }
 
 // CreateDriver returns a volume driver by its name and increments RefCount.
@@ -143,7 +153,7 @@ func CreateDriver(name string) (volume.Driver, error) {
 	if name == "" {
 		name = volume.DefaultDriverName
 	}
-	return lookup(name, pluginStore.CREATE)
+	return lookup(name, getter.CREATE)
 }
 
 // RemoveDriver returns a volume driver by its name and decrements RefCount..
@@ -152,7 +162,7 @@ func RemoveDriver(name string) (volume.Driver, error) {
 	if name == "" {
 		name = volume.DefaultDriverName
 	}
-	return lookup(name, pluginStore.REMOVE)
+	return lookup(name, getter.REMOVE)
 }
 
 // GetDriverList returns list of volume drivers registered.
@@ -169,7 +179,7 @@ func GetDriverList() []string {
 
 // GetAllDrivers lists all the registered drivers
 func GetAllDrivers() ([]volume.Driver, error) {
-	plugins, err := pluginStore.FindWithCapability(extName)
+	plugins, err := drivers.plugingetter.GetAllByCap(extName)
 	if err != nil {
 		return nil, fmt.Errorf("error listing plugins: %v", err)
 	}
@@ -190,7 +200,7 @@ func GetAllDrivers() ([]volume.Driver, error) {
 		}
 
 		ext = NewVolumeDriver(name, p.Client())
-		if p.IsLegacy() {
+		if p.IsV1() {
 			drivers.extensions[name] = ext
 		}
 		ds = append(ds, ext)

+ 5 - 1
volume/drivers/extpoint_test.go

@@ -3,16 +3,20 @@ package volumedrivers
 import (
 	"testing"
 
+	pluginstore "github.com/docker/docker/plugin/store"
 	volumetestutils "github.com/docker/docker/volume/testutils"
 )
 
 func TestGetDriver(t *testing.T) {
+	pluginStore := pluginstore.NewStore("/var/lib/docker")
+	RegisterPluginGetter(pluginStore)
+
 	_, err := GetDriver("missing")
 	if err == nil {
 		t.Fatal("Expected error, was nil")
 	}
-
 	Register(volumetestutils.NewFakeDriver("fake"), "fake")
+
 	d, err := GetDriver("fake")
 	if err != nil {
 		t.Fatal(err)

+ 4 - 0
volume/store/store_test.go

@@ -5,11 +5,15 @@ import (
 	"strings"
 	"testing"
 
+	pluginstore "github.com/docker/docker/plugin/store"
 	"github.com/docker/docker/volume/drivers"
 	volumetestutils "github.com/docker/docker/volume/testutils"
 )
 
 func TestCreate(t *testing.T) {
+	pluginStore := pluginstore.NewStore("/var/lib/docker")
+	volumedrivers.RegisterPluginGetter(pluginStore)
+
 	volumedrivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
 	defer volumedrivers.Unregister("fake")
 	s, err := New("")