summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnusha Ragunathan <anusha.ragunathan@docker.com>2016-09-20 10:59:33 -0700
committerGitHub <noreply@github.com>2016-09-20 10:59:33 -0700
commitef728a1641c354d246bb66f40b662ca8f5fd68c3 (patch)
tree7d424866c9106400f24b9604e892f92f2ad2ac68
parent434887824241806f6bc0a686966245c569778c00 (diff)
parentfefea805e930a67fb6327f8e59415932861358cb (diff)
downloaddocker-ef728a1641c354d246bb66f40b662ca8f5fd68c3.tar.gz
Merge pull request #26645 from anusha-ragunathan/use-pluginv2-graph
Make graphdrivers work with pluginv2.
-rw-r--r--api/types/plugin.go2
-rw-r--r--[-rwxr-xr-x]daemon/daemon.go8
-rw-r--r--daemon/daemon_experimental.go2
-rw-r--r--daemon/graphdriver/driver.go9
-rw-r--r--daemon/graphdriver/graphtest/graphtest_unix.go2
-rw-r--r--daemon/graphdriver/plugin.go6
-rw-r--r--daemon/graphdriver/plugin_unsupported.go4
-rw-r--r--layer/layer_store.go5
-rw-r--r--layer/layer_test.go2
-rw-r--r--pkg/plugins/plugins.go4
-rw-r--r--plugin/backend.go12
-rw-r--r--plugin/distribution/pull.go18
-rw-r--r--plugin/getter/interface.go25
-rw-r--r--plugin/manager.go6
-rw-r--r--plugin/store/defs.go31
-rw-r--r--plugin/store/interface.go19
-rw-r--r--plugin/store/legacy.go25
-rw-r--r--plugin/store/store.go253
-rw-r--r--plugin/store/store_experimental.go234
-rw-r--r--plugin/v2/plugin.go242
-rw-r--r--plugin/v2/plugin_experimental.go249
-rw-r--r--volume/drivers/extpoint.go30
-rw-r--r--volume/drivers/extpoint_test.go6
-rw-r--r--volume/store/store_test.go4
24 files changed, 629 insertions, 569 deletions
diff --git a/api/types/plugin.go b/api/types/plugin.go
index 601c0ac12a..11091ef274 100644
--- a/api/types/plugin.go
+++ b/api/types/plugin.go
@@ -1,5 +1,3 @@
-// +build experimental
-
package types
import (
diff --git a/daemon/daemon.go b/daemon/daemon.go
index cc537ae973..9911f3d2a3 100755..100644
--- a/daemon/daemon.go
+++ b/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")
}
diff --git a/daemon/daemon_experimental.go b/daemon/daemon_experimental.go
index 149e4c71bc..22795eec83 100644
--- a/daemon/daemon_experimental.go
+++ b/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() {
diff --git a/daemon/graphdriver/driver.go b/daemon/graphdriver/driver.go
index 434669da20..19bd8afad5 100644
--- a/daemon/graphdriver/driver.go
+++ b/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
diff --git a/daemon/graphdriver/graphtest/graphtest_unix.go b/daemon/graphdriver/graphtest/graphtest_unix.go
index 68be47b171..70fba957f9 100644
--- a/daemon/graphdriver/graphtest/graphtest_unix.go
+++ b/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 {
diff --git a/daemon/graphdriver/plugin.go b/daemon/graphdriver/plugin.go
index 9f172b72d4..9a044d2f86 100644
--- a/daemon/graphdriver/plugin.go
+++ b/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)
}
diff --git a/daemon/graphdriver/plugin_unsupported.go b/daemon/graphdriver/plugin_unsupported.go
index daa7a170e4..a16ef06858 100644
--- a/daemon/graphdriver/plugin_unsupported.go
+++ b/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
}
diff --git a/layer/layer_store.go b/layer/layer_store.go
index 6d5cb2599d..a587b01abf 100644
--- a/layer/layer_store.go
+++ b/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)
}
diff --git a/layer/layer_test.go b/layer/layer_test.go
index 9df71b92a6..2d9e1e9b55 100644
--- a/layer/layer_test.go
+++ b/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()) {
diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go
index debcd087c9..9385b9b1bc 100644
--- a/pkg/plugins/plugins.go
+++ b/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
}
diff --git a/plugin/backend.go b/plugin/backend.go
index 56d361076b..826075f3d3 100644
--- a/plugin/backend.go
+++ b/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
diff --git a/plugin/distribution/pull.go b/plugin/distribution/pull.go
index d061744558..a4824539d5 100644
--- a/plugin/distribution/pull.go
+++ b/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)
diff --git a/plugin/getter/interface.go b/plugin/getter/interface.go
new file mode 100644
index 0000000000..1c1977d300
--- /dev/null
+++ b/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)
+}
diff --git a/plugin/manager.go b/plugin/manager.go
index 4fa6b143b7..cc7efa1223 100644
--- a/plugin/manager.go
+++ b/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,
diff --git a/plugin/store/defs.go b/plugin/store/defs.go
new file mode 100644
index 0000000000..c6c76766a7
--- /dev/null
+++ b/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"),
+ }
+}
diff --git a/plugin/store/interface.go b/plugin/store/interface.go
deleted file mode 100644
index d33a5f5dde..0000000000
--- a/plugin/store/interface.go
+++ /dev/null
@@ -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
-}
diff --git a/plugin/store/legacy.go b/plugin/store/legacy.go
deleted file mode 100644
index 86b276fc5d..0000000000
--- a/plugin/store/legacy.go
+++ /dev/null
@@ -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)
-}
diff --git a/plugin/store/store.go b/plugin/store/store.go
index 4063e762ea..2ba1b4ad94 100644
--- a/plugin/store/store.go
+++ b/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)
}
diff --git a/plugin/store/store_experimental.go b/plugin/store/store_experimental.go
new file mode 100644
index 0000000000..a1fe10851f
--- /dev/null
+++ b/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())
+ }
+ }
+}
diff --git a/plugin/v2/plugin.go b/plugin/v2/plugin.go
index 80cd3d80e0..9cd37d3f06 100644
--- a/plugin/v2/plugin.go
+++ b/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
-}
diff --git a/plugin/v2/plugin_experimental.go b/plugin/v2/plugin_experimental.go
new file mode 100644
index 0000000000..948437c186
--- /dev/null
+++ b/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
+}
diff --git a/volume/drivers/extpoint.go b/volume/drivers/extpoint.go
index 314535ca24..eae24d9fd9 100644
--- a/volume/drivers/extpoint.go
+++ b/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)
diff --git a/volume/drivers/extpoint_test.go b/volume/drivers/extpoint_test.go
index f96b3104b2..eb6d14bb70 100644
--- a/volume/drivers/extpoint_test.go
+++ b/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)
diff --git a/volume/store/store_test.go b/volume/store/store_test.go
index 089c14f8a2..b27c6578d0 100644
--- a/volume/store/store_test.go
+++ b/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("")