From f07146ebf88a14443cf602d71b3d9f263693ea56 Mon Sep 17 00:00:00 2001 From: Anusha Ragunathan Date: Wed, 7 Sep 2016 17:01:10 -0700 Subject: [PATCH] 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 Upstream-commit: fefea805e930a67fb6327f8e59415932861358cb Component: engine --- components/engine/api/types/plugin.go | 2 - components/engine/daemon/daemon.go | 8 + .../engine/daemon/daemon_experimental.go | 2 +- .../engine/daemon/graphdriver/driver.go | 9 +- .../graphdriver/graphtest/graphtest_unix.go | 2 +- .../engine/daemon/graphdriver/plugin.go | 6 +- .../daemon/graphdriver/plugin_unsupported.go | 4 +- components/engine/layer/layer_store.go | 5 +- components/engine/layer/layer_test.go | 2 +- components/engine/pkg/plugins/plugins.go | 4 +- components/engine/plugin/backend.go | 12 +- components/engine/plugin/distribution/pull.go | 18 +- components/engine/plugin/getter/interface.go | 25 ++ components/engine/plugin/manager.go | 6 +- components/engine/plugin/store/defs.go | 31 +++ components/engine/plugin/store/interface.go | 19 -- components/engine/plugin/store/legacy.go | 25 -- components/engine/plugin/store/store.go | 253 +----------------- .../engine/plugin/store/store_experimental.go | 234 ++++++++++++++++ components/engine/plugin/v2/plugin.go | 242 ----------------- .../engine/plugin/v2/plugin_experimental.go | 249 +++++++++++++++++ components/engine/volume/drivers/extpoint.go | 30 ++- .../engine/volume/drivers/extpoint_test.go | 6 +- components/engine/volume/store/store_test.go | 4 + 24 files changed, 629 insertions(+), 569 deletions(-) mode change 100755 => 100644 components/engine/daemon/daemon.go create mode 100644 components/engine/plugin/getter/interface.go create mode 100644 components/engine/plugin/store/defs.go delete mode 100644 components/engine/plugin/store/interface.go delete mode 100644 components/engine/plugin/store/legacy.go create mode 100644 components/engine/plugin/store/store_experimental.go create mode 100644 components/engine/plugin/v2/plugin_experimental.go diff --git a/components/engine/api/types/plugin.go b/components/engine/api/types/plugin.go index 601c0ac12a..11091ef274 100644 --- a/components/engine/api/types/plugin.go +++ b/components/engine/api/types/plugin.go @@ -1,5 +1,3 @@ -// +build experimental - package types import ( diff --git a/components/engine/daemon/daemon.go b/components/engine/daemon/daemon.go old mode 100755 new mode 100644 index cc537ae973..9911f3d2a3 --- a/components/engine/daemon/daemon.go +++ b/components/engine/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/components/engine/daemon/daemon_experimental.go b/components/engine/daemon/daemon_experimental.go index 149e4c71bc..22795eec83 100644 --- a/components/engine/daemon/daemon_experimental.go +++ b/components/engine/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/components/engine/daemon/graphdriver/driver.go b/components/engine/daemon/graphdriver/driver.go index 434669da20..19bd8afad5 100644 --- a/components/engine/daemon/graphdriver/driver.go +++ b/components/engine/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/components/engine/daemon/graphdriver/graphtest/graphtest_unix.go b/components/engine/daemon/graphdriver/graphtest/graphtest_unix.go index 68be47b171..70fba957f9 100644 --- a/components/engine/daemon/graphdriver/graphtest/graphtest_unix.go +++ b/components/engine/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/components/engine/daemon/graphdriver/plugin.go b/components/engine/daemon/graphdriver/plugin.go index 9f172b72d4..9a044d2f86 100644 --- a/components/engine/daemon/graphdriver/plugin.go +++ b/components/engine/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/components/engine/daemon/graphdriver/plugin_unsupported.go b/components/engine/daemon/graphdriver/plugin_unsupported.go index daa7a170e4..a16ef06858 100644 --- a/components/engine/daemon/graphdriver/plugin_unsupported.go +++ b/components/engine/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/components/engine/layer/layer_store.go b/components/engine/layer/layer_store.go index 6d5cb2599d..a587b01abf 100644 --- a/components/engine/layer/layer_store.go +++ b/components/engine/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/components/engine/layer/layer_test.go b/components/engine/layer/layer_test.go index 9df71b92a6..2d9e1e9b55 100644 --- a/components/engine/layer/layer_test.go +++ b/components/engine/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/components/engine/pkg/plugins/plugins.go b/components/engine/pkg/plugins/plugins.go index debcd087c9..9385b9b1bc 100644 --- a/components/engine/pkg/plugins/plugins.go +++ b/components/engine/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/components/engine/plugin/backend.go b/components/engine/plugin/backend.go index 56d361076b..826075f3d3 100644 --- a/components/engine/plugin/backend.go +++ b/components/engine/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/components/engine/plugin/distribution/pull.go b/components/engine/plugin/distribution/pull.go index d061744558..a4824539d5 100644 --- a/components/engine/plugin/distribution/pull.go +++ b/components/engine/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/components/engine/plugin/getter/interface.go b/components/engine/plugin/getter/interface.go new file mode 100644 index 0000000000..1c1977d300 --- /dev/null +++ b/components/engine/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/components/engine/plugin/manager.go b/components/engine/plugin/manager.go index 4fa6b143b7..cc7efa1223 100644 --- a/components/engine/plugin/manager.go +++ b/components/engine/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/components/engine/plugin/store/defs.go b/components/engine/plugin/store/defs.go new file mode 100644 index 0000000000..c6c76766a7 --- /dev/null +++ b/components/engine/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/components/engine/plugin/store/interface.go b/components/engine/plugin/store/interface.go deleted file mode 100644 index d33a5f5dde..0000000000 --- a/components/engine/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/components/engine/plugin/store/legacy.go b/components/engine/plugin/store/legacy.go deleted file mode 100644 index 86b276fc5d..0000000000 --- a/components/engine/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/components/engine/plugin/store/store.go b/components/engine/plugin/store/store.go index 4063e762ea..2ba1b4ad94 100644 --- a/components/engine/plugin/store/store.go +++ b/components/engine/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" + "github.com/docker/docker/plugin/getter" ) -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 -) - -// 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/components/engine/plugin/store/store_experimental.go b/components/engine/plugin/store/store_experimental.go new file mode 100644 index 0000000000..a1fe10851f --- /dev/null +++ b/components/engine/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/components/engine/plugin/v2/plugin.go b/components/engine/plugin/v2/plugin.go index 80cd3d80e0..9cd37d3f06 100644 --- a/components/engine/plugin/v2/plugin.go +++ b/components/engine/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/components/engine/plugin/v2/plugin_experimental.go b/components/engine/plugin/v2/plugin_experimental.go new file mode 100644 index 0000000000..948437c186 --- /dev/null +++ b/components/engine/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/components/engine/volume/drivers/extpoint.go b/components/engine/volume/drivers/extpoint.go index 314535ca24..eae24d9fd9 100644 --- a/components/engine/volume/drivers/extpoint.go +++ b/components/engine/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/components/engine/volume/drivers/extpoint_test.go b/components/engine/volume/drivers/extpoint_test.go index f96b3104b2..eb6d14bb70 100644 --- a/components/engine/volume/drivers/extpoint_test.go +++ b/components/engine/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/components/engine/volume/store/store_test.go b/components/engine/volume/store/store_test.go index 089c14f8a2..b27c6578d0 100644 --- a/components/engine/volume/store/store_test.go +++ b/components/engine/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("")