Browse Source

Make graphdrivers work with pluginv2.

As part of making graphdrivers support pluginv2, a PluginGetter
interface was necessary for cleaner separation and avoiding import
cycles.

This commit creates a PluginGetter interface and makes pluginStore
implement it. Then the pluginStore object is created in the daemon
(rather than by the plugin manager) and passed to plugin init as
well as to the different subsystems (eg. graphdrivers, volumedrivers).
A side effect of this change was that some code was moved out of
experimental. This is good, since plugin support will be stable soon.

Signed-off-by: Anusha Ragunathan <anusha@docker.com>
Anusha Ragunathan 8 years ago
parent
commit
fefea805e9

+ 0 - 2
api/types/plugin.go

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

+ 8 - 0
daemon/daemon.go

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

+ 5 - 4
daemon/graphdriver/driver.go

@@ -12,6 +12,7 @@ import (
 
 
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/archive"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/idtools"
+	"github.com/docker/docker/plugin/getter"
 )
 )
 
 
 // FsMagic unsigned id of the filesystem in use.
 // 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
 // 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 {
 	if initFunc, exists := drivers[name]; exists {
 		return initFunc(filepath.Join(home, name), options, uidMaps, gidMaps)
 		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
 		return pluginDriver, nil
 	}
 	}
 	logrus.Errorf("Failed to GetDriver graph %s %s", name, home)
 	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.
 // 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 != "" {
 	if name != "" {
 		logrus.Debugf("[graphdriver] trying provided driver: %s", name) // so the logs show specified driver
 		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
 	// 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)
 		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 {
 	if err != nil {
 		t.Logf("graphdriver: %v\n", err)
 		t.Logf("graphdriver: %v\n", err)
 		if err == graphdriver.ErrNotSupported || err == graphdriver.ErrPrerequisites || err == graphdriver.ErrIncompatibleFS {
 		if err == graphdriver.ErrNotSupported || err == graphdriver.ErrPrerequisites || err == graphdriver.ErrIncompatibleFS {

+ 3 - 3
daemon/graphdriver/plugin.go

@@ -6,7 +6,7 @@ import (
 	"fmt"
 	"fmt"
 	"io"
 	"io"
 
 
-	"github.com/docker/docker/pkg/plugins"
+	"github.com/docker/docker/plugin/getter"
 )
 )
 
 
 type pluginClient interface {
 type pluginClient interface {
@@ -18,8 +18,8 @@ type pluginClient interface {
 	SendFile(string, io.Reader, interface{}) error
 	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 {
 	if err != nil {
 		return nil, fmt.Errorf("Error looking up graphdriver plugin %s: %v", name, err)
 		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
 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
 	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/archive"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/idtools"
 	"github.com/docker/docker/pkg/stringid"
 	"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/asm"
 	"github.com/vbatts/tar-split/tar/storage"
 	"github.com/vbatts/tar-split/tar/storage"
 )
 )
@@ -44,6 +45,7 @@ type StoreOptions struct {
 	GraphDriverOptions        []string
 	GraphDriverOptions        []string
 	UIDMaps                   []idtools.IDMap
 	UIDMaps                   []idtools.IDMap
 	GIDMaps                   []idtools.IDMap
 	GIDMaps                   []idtools.IDMap
+	PluginGetter              getter.PluginGetter
 }
 }
 
 
 // NewStoreFromOptions creates a new Store instance
 // NewStoreFromOptions creates a new Store instance
@@ -53,7 +55,8 @@ func NewStoreFromOptions(options StoreOptions) (Store, error) {
 		options.GraphDriver,
 		options.GraphDriver,
 		options.GraphDriverOptions,
 		options.GraphDriverOptions,
 		options.UIDMaps,
 		options.UIDMaps,
-		options.GIDMaps)
+		options.GIDMaps,
+		options.PluginGetter)
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf("error initializing graphdriver: %v", err)
 		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()) {
 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
 	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
 	return true
 }
 }
 
 

+ 4 - 8
plugin/backend.go

@@ -17,7 +17,6 @@ import (
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/pkg/stringid"
 	"github.com/docker/docker/plugin/distribution"
 	"github.com/docker/docker/plugin/distribution"
 	"github.com/docker/docker/plugin/v2"
 	"github.com/docker/docker/plugin/v2"
-	"github.com/docker/docker/reference"
 )
 )
 
 
 // Disable deactivates a plugin, which implies that they cannot be used by containers.
 // 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.
 // 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) {
 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 {
 	if err != nil {
-		logrus.Debugf("error in reference.ParseNamed: %v", err)
+		logrus.Debugf("error in distribution.GetRef: %v", err)
 		return nil, err
 		return nil, err
 	}
 	}
 	name = ref.String()
 	name = ref.String()
@@ -76,7 +75,7 @@ func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.A
 		return nil, err
 		return nil, err
 	}
 	}
 
 
-	pd, err := distribution.Pull(name, pm.registryService, metaHeader, authConfig)
+	pd, err := distribution.Pull(ref, pm.registryService, metaHeader, authConfig)
 	if err != nil {
 	if err != nil {
 		logrus.Debugf("error in distribution.Pull(): %v", err)
 		logrus.Debugf("error in distribution.Pull(): %v", err)
 		return nil, err
 		return nil, err
@@ -87,10 +86,7 @@ func (pm *Manager) Pull(name string, metaHeader http.Header, authConfig *types.A
 		return nil, err
 		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)
 	p := v2.NewPlugin(ref.Name(), pluginID, pm.runRoot, tag)
 	if err := p.InitPlugin(pm.libRoot); err != nil {
 	if err := p.InitPlugin(pm.libRoot); err != nil {
 		return nil, err
 		return nil, err

+ 15 - 3
plugin/distribution/pull.go

@@ -62,14 +62,26 @@ func (pd *pullData) Layer() (io.ReadCloser, error) {
 	return rsc, nil
 	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)
 	ref, err := reference.ParseNamed(name)
 	if err != nil {
 	if err != nil {
-		logrus.Debugf("pull.go: error in ParseNamed: %v", err)
 		return nil, 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)
 	repoInfo, err := rs.ResolveRepository(ref)
 	if err != nil {
 	if err != nil {
 		logrus.Debugf("pull.go: error in ResolveRepository: %v", err)
 		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
 	sync.RWMutex
 	libRoot           string
 	libRoot           string
 	runRoot           string
 	runRoot           string
-	pluginStore       *store.PluginStore
+	pluginStore       *store.Store
 	containerdClient  libcontainerd.Client
 	containerdClient  libcontainerd.Client
 	registryService   registry.Service
 	registryService   registry.Service
 	liveRestore       bool
 	liveRestore       bool
@@ -50,7 +50,7 @@ func GetManager() *Manager {
 
 
 // Init (was NewManager) instantiates the singleton Manager.
 // Init (was NewManager) instantiates the singleton Manager.
 // TODO: revert this to NewManager once we get rid of all the singletons.
 // 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 {
 	if manager != nil {
 		return nil
 		return nil
 	}
 	}
@@ -59,7 +59,7 @@ func Init(root string, remote libcontainerd.Remote, rs registry.Service, liveRes
 	manager = &Manager{
 	manager = &Manager{
 		libRoot:           root,
 		libRoot:           root,
 		runRoot:           "/run/docker",
 		runRoot:           "/run/docker",
-		pluginStore:       store.NewPluginStore(root),
+		pluginStore:       ps,
 		registryService:   rs,
 		registryService:   rs,
 		liveRestore:       liveRestore,
 		liveRestore:       liveRestore,
 		pluginEventLogger: evL,
 		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
 package store
 
 
 import (
 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/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 {
 	if err != nil {
 		return nil, err
 		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
 	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
 package v2
 
 
 import (
 import (
-	"encoding/json"
-	"errors"
-	"fmt"
-	"os"
-	"path/filepath"
-	"strings"
 	"sync"
 	"sync"
 
 
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/api/types"
 	"github.com/docker/docker/pkg/plugins"
 	"github.com/docker/docker/pkg/plugins"
-	"github.com/docker/docker/pkg/system"
 	"github.com/docker/docker/restartmanager"
 	"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.
 // Plugin represents an individual plugin.
 type Plugin struct {
 type Plugin struct {
 	sync.RWMutex
 	sync.RWMutex
@@ -37,226 +18,3 @@ type Plugin struct {
 	ExitChan          chan bool                     `json:"-"`
 	ExitChan          chan bool                     `json:"-"`
 	RefCount          int                           `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"
 	"sync"
 
 
 	"github.com/docker/docker/pkg/locker"
 	"github.com/docker/docker/pkg/locker"
-	pluginStore "github.com/docker/docker/plugin/store"
+	"github.com/docker/docker/plugin/getter"
 	"github.com/docker/docker/volume"
 	"github.com/docker/docker/volume"
 )
 )
 
 
 // currently created by hand. generation tool would generate this like:
 // currently created by hand. generation tool would generate this like:
 // $ extpoint-gen Driver > volume/extpoint.go
 // $ 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"
 const extName = "VolumeDriver"
 
 
@@ -49,7 +52,13 @@ type volumeDriver interface {
 type driverExtpoint struct {
 type driverExtpoint struct {
 	extensions map[string]volume.Driver
 	extensions map[string]volume.Driver
 	sync.Mutex
 	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
 // 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
 	drivers.extensions[name] = extension
+
 	return true
 	return true
 }
 }
 
 
@@ -102,7 +112,7 @@ func lookup(name string, mode int) (volume.Driver, error) {
 		return ext, nil
 		return ext, nil
 	}
 	}
 
 
-	p, err := pluginStore.LookupWithCapability(name, extName, mode)
+	p, err := drivers.plugingetter.Get(name, extName, mode)
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf("Error looking up volume plugin %s: %v", name, err)
 		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
 		return nil, err
 	}
 	}
 
 
-	if p.IsLegacy() {
+	if p.IsV1() {
 		drivers.Lock()
 		drivers.Lock()
 		drivers.extensions[name] = d
 		drivers.extensions[name] = d
 		drivers.Unlock()
 		drivers.Unlock()
@@ -134,7 +144,7 @@ func GetDriver(name string) (volume.Driver, error) {
 	if name == "" {
 	if name == "" {
 		name = volume.DefaultDriverName
 		name = volume.DefaultDriverName
 	}
 	}
-	return lookup(name, pluginStore.LOOKUP)
+	return lookup(name, getter.LOOKUP)
 }
 }
 
 
 // CreateDriver returns a volume driver by its name and increments RefCount.
 // CreateDriver returns a volume driver by its name and increments RefCount.
@@ -143,7 +153,7 @@ func CreateDriver(name string) (volume.Driver, error) {
 	if name == "" {
 	if name == "" {
 		name = volume.DefaultDriverName
 		name = volume.DefaultDriverName
 	}
 	}
-	return lookup(name, pluginStore.CREATE)
+	return lookup(name, getter.CREATE)
 }
 }
 
 
 // RemoveDriver returns a volume driver by its name and decrements RefCount..
 // RemoveDriver returns a volume driver by its name and decrements RefCount..
@@ -152,7 +162,7 @@ func RemoveDriver(name string) (volume.Driver, error) {
 	if name == "" {
 	if name == "" {
 		name = volume.DefaultDriverName
 		name = volume.DefaultDriverName
 	}
 	}
-	return lookup(name, pluginStore.REMOVE)
+	return lookup(name, getter.REMOVE)
 }
 }
 
 
 // GetDriverList returns list of volume drivers registered.
 // GetDriverList returns list of volume drivers registered.
@@ -169,7 +179,7 @@ func GetDriverList() []string {
 
 
 // GetAllDrivers lists all the registered drivers
 // GetAllDrivers lists all the registered drivers
 func GetAllDrivers() ([]volume.Driver, error) {
 func GetAllDrivers() ([]volume.Driver, error) {
-	plugins, err := pluginStore.FindWithCapability(extName)
+	plugins, err := drivers.plugingetter.GetAllByCap(extName)
 	if err != nil {
 	if err != nil {
 		return nil, fmt.Errorf("error listing plugins: %v", err)
 		return nil, fmt.Errorf("error listing plugins: %v", err)
 	}
 	}
@@ -190,7 +200,7 @@ func GetAllDrivers() ([]volume.Driver, error) {
 		}
 		}
 
 
 		ext = NewVolumeDriver(name, p.Client())
 		ext = NewVolumeDriver(name, p.Client())
-		if p.IsLegacy() {
+		if p.IsV1() {
 			drivers.extensions[name] = ext
 			drivers.extensions[name] = ext
 		}
 		}
 		ds = append(ds, ext)
 		ds = append(ds, ext)

+ 5 - 1
volume/drivers/extpoint_test.go

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

+ 4 - 0
volume/store/store_test.go

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