Merge pull request #35829 from cpuguy83/no_private_mount_for_plugins
Perform plugin mounts in the runtime
This commit is contained in:
commit
20028325da
|
@ -23,7 +23,7 @@ func newPluginDriver(name string, pl plugingetter.CompatPlugin, config Options)
|
||||||
home := config.Root
|
home := config.Root
|
||||||
if !pl.IsV1() {
|
if !pl.IsV1() {
|
||||||
if p, ok := pl.(*v2.Plugin); ok {
|
if p, ok := pl.(*v2.Plugin); ok {
|
||||||
if p.PropagatedMount != "" {
|
if p.PluginObj.Config.PropagatedMount != "" {
|
||||||
home = p.PluginObj.Config.PropagatedMount
|
home = p.PluginObj.Config.PropagatedMount
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/archive"
|
"github.com/docker/docker/pkg/archive"
|
||||||
"github.com/docker/docker/pkg/containerfs"
|
"github.com/docker/docker/pkg/containerfs"
|
||||||
|
@ -143,7 +142,7 @@ func (d *graphDriverProxy) Get(id, mountLabel string) (containerfs.ContainerFS,
|
||||||
if ret.Err != "" {
|
if ret.Err != "" {
|
||||||
err = errors.New(ret.Err)
|
err = errors.New(ret.Err)
|
||||||
}
|
}
|
||||||
return containerfs.NewLocalContainerFS(filepath.Join(d.p.BasePath(), ret.Dir)), err
|
return containerfs.NewLocalContainerFS(d.p.ScopedPath(ret.Dir)), err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *graphDriverProxy) Put(id string) error {
|
func (d *graphDriverProxy) Put(id string) error {
|
||||||
|
|
|
@ -3,7 +3,7 @@ package logger // import "github.com/docker/docker/daemon/logger"
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"path/filepath"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -19,7 +19,6 @@ type pluginAdapter struct {
|
||||||
driverName string
|
driverName string
|
||||||
id string
|
id string
|
||||||
plugin logPlugin
|
plugin logPlugin
|
||||||
basePath string
|
|
||||||
fifoPath string
|
fifoPath string
|
||||||
capabilities Capability
|
capabilities Capability
|
||||||
logInfo Info
|
logInfo Info
|
||||||
|
@ -58,7 +57,7 @@ func (a *pluginAdapter) Close() error {
|
||||||
a.mu.Lock()
|
a.mu.Lock()
|
||||||
defer a.mu.Unlock()
|
defer a.mu.Unlock()
|
||||||
|
|
||||||
if err := a.plugin.StopLogging(strings.TrimPrefix(a.fifoPath, a.basePath)); err != nil {
|
if err := a.plugin.StopLogging(filepath.Join("/", "run", "docker", "logging", a.id)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/docker/docker/api/types/plugins/logdriver"
|
"github.com/docker/docker/api/types/plugins/logdriver"
|
||||||
getter "github.com/docker/docker/pkg/plugingetter"
|
getter "github.com/docker/docker/pkg/plugingetter"
|
||||||
|
@ -39,18 +38,20 @@ func getPlugin(name string, mode int) (Creator, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
d := &logPluginProxy{p.Client()}
|
d := &logPluginProxy{p.Client()}
|
||||||
return makePluginCreator(name, d, p.BasePath()), nil
|
return makePluginCreator(name, d, p.ScopedPath), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePluginCreator(name string, l *logPluginProxy, basePath string) Creator {
|
func makePluginCreator(name string, l *logPluginProxy, scopePath func(s string) string) Creator {
|
||||||
return func(logCtx Info) (logger Logger, err error) {
|
return func(logCtx Info) (logger Logger, err error) {
|
||||||
defer func() {
|
defer func() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pluginGetter.Get(name, extName, getter.Release)
|
pluginGetter.Get(name, extName, getter.Release)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
root := filepath.Join(basePath, "run", "docker", "logging")
|
|
||||||
if err := os.MkdirAll(root, 0700); err != nil {
|
unscopedPath := filepath.Join("/", "run", "docker", "logging")
|
||||||
|
logRoot := scopePath(unscopedPath)
|
||||||
|
if err := os.MkdirAll(logRoot, 0700); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,8 +60,7 @@ func makePluginCreator(name string, l *logPluginProxy, basePath string) Creator
|
||||||
driverName: name,
|
driverName: name,
|
||||||
id: id,
|
id: id,
|
||||||
plugin: l,
|
plugin: l,
|
||||||
basePath: basePath,
|
fifoPath: filepath.Join(logRoot, id),
|
||||||
fifoPath: filepath.Join(root, id),
|
|
||||||
logInfo: logCtx,
|
logInfo: logCtx,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ func makePluginCreator(name string, l *logPluginProxy, basePath string) Creator
|
||||||
a.stream = stream
|
a.stream = stream
|
||||||
a.enc = logdriver.NewLogEntryEncoder(a.stream)
|
a.enc = logdriver.NewLogEntryEncoder(a.stream)
|
||||||
|
|
||||||
if err := l.StartLogging(strings.TrimPrefix(a.fifoPath, basePath), logCtx); err != nil {
|
if err := l.StartLogging(filepath.Join(unscopedPath, id), logCtx); err != nil {
|
||||||
return nil, errors.Wrapf(err, "error creating logger")
|
return nil, errors.Wrapf(err, "error creating logger")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,8 @@
|
||||||
package daemon // import "github.com/docker/docker/daemon"
|
package daemon // import "github.com/docker/docker/daemon"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"path/filepath"
|
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/mount"
|
|
||||||
"github.com/docker/docker/pkg/plugingetter"
|
"github.com/docker/docker/pkg/plugingetter"
|
||||||
metrics "github.com/docker/go-metrics"
|
metrics "github.com/docker/go-metrics"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -132,18 +130,6 @@ func (d *Daemon) cleanupMetricsPlugins() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type metricsPlugin struct {
|
|
||||||
plugingetter.CompatPlugin
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p metricsPlugin) sock() string {
|
|
||||||
return "metrics.sock"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p metricsPlugin) sockBase() string {
|
|
||||||
return filepath.Join(p.BasePath(), "run", "docker")
|
|
||||||
}
|
|
||||||
|
|
||||||
func pluginStartMetricsCollection(p plugingetter.CompatPlugin) error {
|
func pluginStartMetricsCollection(p plugingetter.CompatPlugin) error {
|
||||||
type metricsPluginResponse struct {
|
type metricsPluginResponse struct {
|
||||||
Err string
|
Err string
|
||||||
|
@ -162,12 +148,4 @@ func pluginStopMetricsCollection(p plugingetter.CompatPlugin) {
|
||||||
if err := p.Client().Call(metricsPluginType+".StopMetrics", nil, nil); err != nil {
|
if err := p.Client().Call(metricsPluginType+".StopMetrics", nil, nil); err != nil {
|
||||||
logrus.WithError(err).WithField("name", p.Name()).Error("error stopping metrics collector")
|
logrus.WithError(err).WithField("name", p.Name()).Error("error stopping metrics collector")
|
||||||
}
|
}
|
||||||
|
|
||||||
mp := metricsPlugin{p}
|
|
||||||
sockPath := filepath.Join(mp.sockBase(), mp.sock())
|
|
||||||
if err := mount.Unmount(sockPath); err != nil {
|
|
||||||
if mounted, _ := mount.Mounted(sockPath); mounted {
|
|
||||||
logrus.WithError(err).WithField("name", p.Name()).WithField("socket", sockPath).Error("error unmounting metrics socket for plugin")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,13 @@ package daemon // import "github.com/docker/docker/daemon"
|
||||||
import (
|
import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/mount"
|
|
||||||
"github.com/docker/docker/pkg/plugingetter"
|
"github.com/docker/docker/pkg/plugingetter"
|
||||||
"github.com/docker/docker/pkg/plugins"
|
"github.com/docker/docker/pkg/plugins"
|
||||||
|
"github.com/docker/docker/plugin"
|
||||||
metrics "github.com/docker/go-metrics"
|
metrics "github.com/docker/go-metrics"
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
|
@ -34,52 +34,22 @@ func (daemon *Daemon) listenMetricsSock() (string, error) {
|
||||||
return path, nil
|
return path, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func registerMetricsPluginCallback(getter plugingetter.PluginGetter, sockPath string) {
|
func registerMetricsPluginCallback(store *plugin.Store, sockPath string) {
|
||||||
getter.Handle(metricsPluginType, func(name string, client *plugins.Client) {
|
store.RegisterRuntimeOpt(metricsPluginType, func(s *specs.Spec) {
|
||||||
|
f := plugin.WithSpecMounts([]specs.Mount{
|
||||||
|
{Type: "bind", Source: sockPath, Destination: "/run/docker/metrics.sock", Options: []string{"bind", "ro"}},
|
||||||
|
})
|
||||||
|
f(s)
|
||||||
|
})
|
||||||
|
store.Handle(metricsPluginType, func(name string, client *plugins.Client) {
|
||||||
// Use lookup since nothing in the system can really reference it, no need
|
// Use lookup since nothing in the system can really reference it, no need
|
||||||
// to protect against removal
|
// to protect against removal
|
||||||
p, err := getter.Get(name, metricsPluginType, plugingetter.Lookup)
|
p, err := store.Get(name, metricsPluginType, plugingetter.Lookup)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mp := metricsPlugin{p}
|
|
||||||
sockBase := mp.sockBase()
|
|
||||||
if err := os.MkdirAll(sockBase, 0755); err != nil {
|
|
||||||
logrus.WithError(err).WithField("name", name).WithField("path", sockBase).Error("error creating metrics plugin base path")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
defer func() {
|
|
||||||
if err != nil {
|
|
||||||
os.RemoveAll(sockBase)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
pluginSockPath := filepath.Join(sockBase, mp.sock())
|
|
||||||
_, err = os.Stat(pluginSockPath)
|
|
||||||
if err == nil {
|
|
||||||
mount.Unmount(pluginSockPath)
|
|
||||||
} else {
|
|
||||||
logrus.WithField("path", pluginSockPath).Debugf("creating plugin socket")
|
|
||||||
f, err := os.OpenFile(pluginSockPath, os.O_CREATE, 0600)
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
f.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := mount.Mount(sockPath, pluginSockPath, "none", "bind,ro"); err != nil {
|
|
||||||
logrus.WithError(err).WithField("name", name).Error("could not mount metrics socket to plugin")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := pluginStartMetricsCollection(p); err != nil {
|
if err := pluginStartMetricsCollection(p); err != nil {
|
||||||
if err := mount.Unmount(pluginSockPath); err != nil {
|
|
||||||
if mounted, _ := mount.Mounted(pluginSockPath); mounted {
|
|
||||||
logrus.WithError(err).WithField("sock_path", pluginSockPath).Error("error unmounting metrics socket from plugin during cleanup")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logrus.WithError(err).WithField("name", name).Error("error while initializing metrics plugin")
|
logrus.WithError(err).WithField("name", name).Error("error while initializing metrics plugin")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/integration-cli/checker"
|
"github.com/docker/docker/integration-cli/checker"
|
||||||
|
@ -199,12 +197,6 @@ func (s *DockerDaemonSuite) TestVolumePlugin(c *check.C) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Fatalf("Could not install plugin: %v %s", err, out)
|
c.Fatalf("Could not install plugin: %v %s", err, out)
|
||||||
}
|
}
|
||||||
pluginID, err := s.d.Cmd("plugin", "inspect", "-f", "{{.Id}}", pName)
|
|
||||||
pluginID = strings.TrimSpace(pluginID)
|
|
||||||
if err != nil {
|
|
||||||
c.Fatalf("Could not retrieve plugin ID: %v %s", err, pluginID)
|
|
||||||
}
|
|
||||||
mountpointPrefix := filepath.Join(s.d.RootDir(), "plugins", pluginID, "rootfs")
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if out, err := s.d.Cmd("plugin", "disable", pName); err != nil {
|
if out, err := s.d.Cmd("plugin", "disable", pName); err != nil {
|
||||||
c.Fatalf("Could not disable plugin: %v %s", err, out)
|
c.Fatalf("Could not disable plugin: %v %s", err, out)
|
||||||
|
@ -213,11 +205,6 @@ func (s *DockerDaemonSuite) TestVolumePlugin(c *check.C) {
|
||||||
if out, err := s.d.Cmd("plugin", "remove", pName); err != nil {
|
if out, err := s.d.Cmd("plugin", "remove", pName); err != nil {
|
||||||
c.Fatalf("Could not remove plugin: %v %s", err, out)
|
c.Fatalf("Could not remove plugin: %v %s", err, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
exists, err := existsMountpointWithPrefix(mountpointPrefix)
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
c.Assert(exists, checker.Equals, false)
|
|
||||||
|
|
||||||
}()
|
}()
|
||||||
|
|
||||||
out, err = s.d.Cmd("volume", "create", "-d", pName, volName)
|
out, err = s.d.Cmd("volume", "create", "-d", pName, volName)
|
||||||
|
@ -237,21 +224,11 @@ func (s *DockerDaemonSuite) TestVolumePlugin(c *check.C) {
|
||||||
c.Assert(out, checker.Contains, volName)
|
c.Assert(out, checker.Contains, volName)
|
||||||
c.Assert(out, checker.Contains, pName)
|
c.Assert(out, checker.Contains, pName)
|
||||||
|
|
||||||
mountPoint, err := s.d.Cmd("volume", "inspect", volName, "--format", "{{.Mountpoint}}")
|
|
||||||
if err != nil {
|
|
||||||
c.Fatalf("Could not inspect volume: %v %s", err, mountPoint)
|
|
||||||
}
|
|
||||||
mountPoint = strings.TrimSpace(mountPoint)
|
|
||||||
|
|
||||||
out, err = s.d.Cmd("run", "--rm", "-v", volName+":"+destDir, "busybox", "touch", destDir+destFile)
|
out, err = s.d.Cmd("run", "--rm", "-v", volName+":"+destDir, "busybox", "touch", destDir+destFile)
|
||||||
c.Assert(err, checker.IsNil, check.Commentf(out))
|
c.Assert(err, checker.IsNil, check.Commentf(out))
|
||||||
path := filepath.Join(s.d.RootDir(), "plugins", pluginID, "rootfs", mountPoint, destFile)
|
|
||||||
_, err = os.Lstat(path)
|
|
||||||
c.Assert(err, checker.IsNil)
|
|
||||||
|
|
||||||
exists, err := existsMountpointWithPrefix(mountpointPrefix)
|
out, err = s.d.Cmd("run", "--rm", "-v", volName+":"+destDir, "busybox", "ls", destDir+destFile)
|
||||||
c.Assert(err, checker.IsNil)
|
c.Assert(err, checker.IsNil, check.Commentf(out))
|
||||||
c.Assert(exists, checker.Equals, true)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *DockerDaemonSuite) TestGraphdriverPlugin(c *check.C) {
|
func (s *DockerDaemonSuite) TestGraphdriverPlugin(c *check.C) {
|
||||||
|
|
|
@ -17,7 +17,7 @@ const (
|
||||||
type CompatPlugin interface {
|
type CompatPlugin interface {
|
||||||
Client() *plugins.Client
|
Client() *plugins.Client
|
||||||
Name() string
|
Name() string
|
||||||
BasePath() string
|
ScopedPath(string) string
|
||||||
IsV1() bool
|
IsV1() bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,8 @@
|
||||||
|
|
||||||
package plugins // import "github.com/docker/docker/pkg/plugins"
|
package plugins // import "github.com/docker/docker/pkg/plugins"
|
||||||
|
|
||||||
// BasePath returns the path to which all paths returned by the plugin are relative to.
|
// ScopedPath returns the path scoped to the plugin's rootfs.
|
||||||
// For v1 plugins, this always returns the host's root directory.
|
// For v1 plugins, this always returns the path unchanged as v1 plugins run directly on the host.
|
||||||
func (p *Plugin) BasePath() string {
|
func (p *Plugin) ScopedPath(s string) string {
|
||||||
return "/"
|
return s
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,7 @@
|
||||||
package plugins // import "github.com/docker/docker/pkg/plugins"
|
package plugins // import "github.com/docker/docker/pkg/plugins"
|
||||||
|
|
||||||
// BasePath returns the path to which all paths returned by the plugin are relative to.
|
// ScopedPath returns the path scoped to the plugin's rootfs.
|
||||||
// For Windows v1 plugins, this returns an empty string, since the plugin is already aware
|
// For v1 plugins, this always returns the path unchanged as v1 plugins run directly on the host.
|
||||||
// of the absolute path of the mount.
|
func (p *Plugin) ScopedPath(s string) string {
|
||||||
func (p *Plugin) BasePath() string {
|
return s
|
||||||
return ""
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,12 +5,14 @@ import (
|
||||||
|
|
||||||
"github.com/docker/docker/pkg/plugins"
|
"github.com/docker/docker/pkg/plugins"
|
||||||
"github.com/docker/docker/plugin/v2"
|
"github.com/docker/docker/plugin/v2"
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Store manages the plugin inventory in memory and on-disk
|
// Store manages the plugin inventory in memory and on-disk
|
||||||
type Store struct {
|
type Store struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
plugins map[string]*v2.Plugin
|
plugins map[string]*v2.Plugin
|
||||||
|
specOpts map[string][]SpecOpt
|
||||||
/* handlers are necessary for transition path of legacy plugins
|
/* handlers are necessary for transition path of legacy plugins
|
||||||
* to the new model. Legacy plugins use Handle() for registering an
|
* to the new model. Legacy plugins use Handle() for registering an
|
||||||
* activation callback.*/
|
* activation callback.*/
|
||||||
|
@ -21,10 +23,14 @@ type Store struct {
|
||||||
func NewStore() *Store {
|
func NewStore() *Store {
|
||||||
return &Store{
|
return &Store{
|
||||||
plugins: make(map[string]*v2.Plugin),
|
plugins: make(map[string]*v2.Plugin),
|
||||||
|
specOpts: make(map[string][]SpecOpt),
|
||||||
handlers: make(map[string][]func(string, *plugins.Client)),
|
handlers: make(map[string][]func(string, *plugins.Client)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SpecOpt is used for subsystems that need to modify the runtime spec of a plugin
|
||||||
|
type SpecOpt func(*specs.Spec)
|
||||||
|
|
||||||
// CreateOpt is used to configure specific plugin details when created
|
// CreateOpt is used to configure specific plugin details when created
|
||||||
type CreateOpt func(p *v2.Plugin)
|
type CreateOpt func(p *v2.Plugin)
|
||||||
|
|
||||||
|
@ -35,3 +41,10 @@ func WithSwarmService(id string) CreateOpt {
|
||||||
p.SwarmServiceID = id
|
p.SwarmServiceID = id
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithSpecMounts is a SpecOpt which appends the provided mounts to the runtime spec
|
||||||
|
func WithSpecMounts(mounts []specs.Mount) SpecOpt {
|
||||||
|
return func(s *specs.Spec) {
|
||||||
|
s.Mounts = append(s.Mounts, mounts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -112,11 +112,6 @@ func NewManager(config ManagerConfig) (*Manager, error) {
|
||||||
return nil, errors.Wrapf(err, "failed to mkdir %v", dirName)
|
return nil, errors.Wrapf(err, "failed to mkdir %v", dirName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := setupRoot(manager.config.Root); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
manager.executor, err = config.CreateExecutor(manager)
|
manager.executor, err = config.CreateExecutor(manager)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -151,16 +146,6 @@ func (pm *Manager) HandleExitEvent(id string) error {
|
||||||
|
|
||||||
os.RemoveAll(filepath.Join(pm.config.ExecRoot, id))
|
os.RemoveAll(filepath.Join(pm.config.ExecRoot, id))
|
||||||
|
|
||||||
if p.PropagatedMount != "" {
|
|
||||||
if err := mount.Unmount(p.PropagatedMount); err != nil {
|
|
||||||
logrus.Warnf("Could not unmount %s: %v", p.PropagatedMount, err)
|
|
||||||
}
|
|
||||||
propRoot := filepath.Join(filepath.Dir(p.Rootfs), "propagated-mount")
|
|
||||||
if err := mount.Unmount(propRoot); err != nil {
|
|
||||||
logrus.Warn("Could not unmount %s: %v", propRoot, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pm.mu.RLock()
|
pm.mu.RLock()
|
||||||
c := pm.cMap[p]
|
c := pm.cMap[p]
|
||||||
if c.exitChan != nil {
|
if c.exitChan != nil {
|
||||||
|
@ -171,6 +156,10 @@ func (pm *Manager) HandleExitEvent(id string) error {
|
||||||
|
|
||||||
if restart {
|
if restart {
|
||||||
pm.enable(p, c, true)
|
pm.enable(p, c, true)
|
||||||
|
} else {
|
||||||
|
if err := mount.RecursiveUnmount(filepath.Join(pm.config.Root, id)); err != nil {
|
||||||
|
return errors.Wrap(err, "error cleaning up plugin mounts")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -239,28 +228,17 @@ func (pm *Manager) reload() error { // todo: restore
|
||||||
// check if we need to migrate an older propagated mount from before
|
// check if we need to migrate an older propagated mount from before
|
||||||
// these mounts were stored outside the plugin rootfs
|
// these mounts were stored outside the plugin rootfs
|
||||||
if _, err := os.Stat(propRoot); os.IsNotExist(err) {
|
if _, err := os.Stat(propRoot); os.IsNotExist(err) {
|
||||||
if _, err := os.Stat(p.PropagatedMount); err == nil {
|
rootfsProp := filepath.Join(p.Rootfs, p.PluginObj.Config.PropagatedMount)
|
||||||
// make sure nothing is mounted here
|
if _, err := os.Stat(rootfsProp); err == nil {
|
||||||
// don't care about errors
|
if err := os.Rename(rootfsProp, propRoot); err != nil {
|
||||||
mount.Unmount(p.PropagatedMount)
|
|
||||||
if err := os.Rename(p.PropagatedMount, propRoot); err != nil {
|
|
||||||
logrus.WithError(err).WithField("dir", propRoot).Error("error migrating propagated mount storage")
|
logrus.WithError(err).WithField("dir", propRoot).Error("error migrating propagated mount storage")
|
||||||
}
|
}
|
||||||
if err := os.MkdirAll(p.PropagatedMount, 0755); err != nil {
|
|
||||||
logrus.WithError(err).WithField("dir", p.PropagatedMount).Error("error migrating propagated mount storage")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := os.MkdirAll(propRoot, 0755); err != nil {
|
if err := os.MkdirAll(propRoot, 0755); err != nil {
|
||||||
logrus.Errorf("failed to create PropagatedMount directory at %s: %v", propRoot, err)
|
logrus.Errorf("failed to create PropagatedMount directory at %s: %v", propRoot, err)
|
||||||
}
|
}
|
||||||
// TODO: sanitize PropagatedMount and prevent breakout
|
|
||||||
p.PropagatedMount = filepath.Join(p.Rootfs, p.PluginObj.Config.PropagatedMount)
|
|
||||||
if err := os.MkdirAll(p.PropagatedMount, 0755); err != nil {
|
|
||||||
logrus.Errorf("failed to create PropagatedMount directory at %s: %v", p.PropagatedMount, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
"golang.org/x/sys/unix"
|
"golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) (err error) {
|
func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error {
|
||||||
p.Rootfs = filepath.Join(pm.config.Root, p.PluginObj.ID, "rootfs")
|
p.Rootfs = filepath.Join(pm.config.Root, p.PluginObj.ID, "rootfs")
|
||||||
if p.IsEnabled() && !force {
|
if p.IsEnabled() && !force {
|
||||||
return errors.Wrap(enabledError(p.Name()), "plugin already enabled")
|
return errors.Wrap(enabledError(p.Name()), "plugin already enabled")
|
||||||
|
@ -40,20 +40,16 @@ func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) (err error) {
|
||||||
pm.mu.Unlock()
|
pm.mu.Unlock()
|
||||||
|
|
||||||
var propRoot string
|
var propRoot string
|
||||||
if p.PropagatedMount != "" {
|
if p.PluginObj.Config.PropagatedMount != "" {
|
||||||
propRoot = filepath.Join(filepath.Dir(p.Rootfs), "propagated-mount")
|
propRoot = filepath.Join(filepath.Dir(p.Rootfs), "propagated-mount")
|
||||||
|
|
||||||
if err = os.MkdirAll(propRoot, 0755); err != nil {
|
if err := os.MkdirAll(propRoot, 0755); err != nil {
|
||||||
logrus.Errorf("failed to create PropagatedMount directory at %s: %v", propRoot, err)
|
logrus.Errorf("failed to create PropagatedMount directory at %s: %v", propRoot, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mount.MakeRShared(propRoot); err != nil {
|
if err := mount.MakeRShared(propRoot); err != nil {
|
||||||
return errors.Wrap(err, "error setting up propagated mount dir")
|
return errors.Wrap(err, "error setting up propagated mount dir")
|
||||||
}
|
}
|
||||||
|
|
||||||
if err = mount.Mount(propRoot, p.PropagatedMount, "none", "rbind"); err != nil {
|
|
||||||
return errors.Wrap(err, "error creating mount for propagated mount")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rootFS := containerfs.NewLocalContainerFS(filepath.Join(pm.config.Root, p.PluginObj.ID, rootFSFileName))
|
rootFS := containerfs.NewLocalContainerFS(filepath.Join(pm.config.Root, p.PluginObj.ID, rootFSFileName))
|
||||||
|
@ -63,16 +59,12 @@ func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) (err error) {
|
||||||
|
|
||||||
stdout, stderr := makeLoggerStreams(p.GetID())
|
stdout, stderr := makeLoggerStreams(p.GetID())
|
||||||
if err := pm.executor.Create(p.GetID(), *spec, stdout, stderr); err != nil {
|
if err := pm.executor.Create(p.GetID(), *spec, stdout, stderr); err != nil {
|
||||||
if p.PropagatedMount != "" {
|
if p.PluginObj.Config.PropagatedMount != "" {
|
||||||
if err := mount.Unmount(p.PropagatedMount); err != nil {
|
|
||||||
logrus.Warnf("Could not unmount %s: %v", p.PropagatedMount, err)
|
|
||||||
}
|
|
||||||
if err := mount.Unmount(propRoot); err != nil {
|
if err := mount.Unmount(propRoot); err != nil {
|
||||||
logrus.Warnf("Could not unmount %s: %v", propRoot, err)
|
logrus.Warnf("Could not unmount %s: %v", propRoot, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pm.pluginPostStart(p, c)
|
return pm.pluginPostStart(p, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,13 +159,6 @@ func shutdownPlugin(p *v2.Plugin, c *controller, executor Executor) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupRoot(root string) error {
|
|
||||||
if err := mount.MakePrivate(root); err != nil {
|
|
||||||
return errors.Wrap(err, "error setting plugin manager root to private")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pm *Manager) disable(p *v2.Plugin, c *controller) error {
|
func (pm *Manager) disable(p *v2.Plugin, c *controller) error {
|
||||||
if !p.IsEnabled() {
|
if !p.IsEnabled() {
|
||||||
return errors.Wrap(errDisabled(p.Name()), "plugin is already disabled")
|
return errors.Wrap(errDisabled(p.Name()), "plugin is already disabled")
|
||||||
|
@ -202,7 +187,9 @@ func (pm *Manager) Shutdown() {
|
||||||
shutdownPlugin(p, c, pm.executor)
|
shutdownPlugin(p, c, pm.executor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mount.Unmount(pm.config.Root)
|
if err := mount.RecursiveUnmount(pm.config.Root); err != nil {
|
||||||
|
logrus.WithError(err).Warn("error cleaning up plugin mounts")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *Manager) upgradePlugin(p *v2.Plugin, configDigest digest.Digest, blobsums []digest.Digest, tmpRootFSDir string, privileges *types.PluginPrivileges) (err error) {
|
func (pm *Manager) upgradePlugin(p *v2.Plugin, configDigest digest.Digest, blobsums []digest.Digest, tmpRootFSDir string, privileges *types.PluginPrivileges) (err error) {
|
||||||
|
|
|
@ -26,5 +26,3 @@ func (pm *Manager) restore(p *v2.Plugin) error {
|
||||||
// Shutdown plugins
|
// Shutdown plugins
|
||||||
func (pm *Manager) Shutdown() {
|
func (pm *Manager) Shutdown() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func setupRoot(root string) error { return nil }
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/docker/docker/pkg/plugingetter"
|
"github.com/docker/docker/pkg/plugingetter"
|
||||||
"github.com/docker/docker/pkg/plugins"
|
"github.com/docker/docker/pkg/plugins"
|
||||||
"github.com/docker/docker/plugin/v2"
|
"github.com/docker/docker/plugin/v2"
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
@ -64,6 +65,10 @@ func (ps *Store) GetAll() map[string]*v2.Plugin {
|
||||||
func (ps *Store) SetAll(plugins map[string]*v2.Plugin) {
|
func (ps *Store) SetAll(plugins map[string]*v2.Plugin) {
|
||||||
ps.Lock()
|
ps.Lock()
|
||||||
defer ps.Unlock()
|
defer ps.Unlock()
|
||||||
|
|
||||||
|
for _, p := range plugins {
|
||||||
|
ps.setSpecOpts(p)
|
||||||
|
}
|
||||||
ps.plugins = plugins
|
ps.plugins = plugins
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -90,6 +95,22 @@ func (ps *Store) SetState(p *v2.Plugin, state bool) {
|
||||||
p.PluginObj.Enabled = state
|
p.PluginObj.Enabled = state
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ps *Store) setSpecOpts(p *v2.Plugin) {
|
||||||
|
var specOpts []SpecOpt
|
||||||
|
for _, typ := range p.GetTypes() {
|
||||||
|
opts, ok := ps.specOpts[typ.String()]
|
||||||
|
if ok {
|
||||||
|
specOpts = append(specOpts, opts...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
p.SetSpecOptModifier(func(s *specs.Spec) {
|
||||||
|
for _, o := range specOpts {
|
||||||
|
o(s)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// Add adds a plugin to memory and plugindb.
|
// Add adds a plugin to memory and plugindb.
|
||||||
// An error will be returned if there is a collision.
|
// An error will be returned if there is a collision.
|
||||||
func (ps *Store) Add(p *v2.Plugin) error {
|
func (ps *Store) Add(p *v2.Plugin) error {
|
||||||
|
@ -99,6 +120,9 @@ func (ps *Store) Add(p *v2.Plugin) error {
|
||||||
if v, exist := ps.plugins[p.GetID()]; exist {
|
if v, exist := ps.plugins[p.GetID()]; exist {
|
||||||
return fmt.Errorf("plugin %q has the same ID %s as %q", p.Name(), p.GetID(), v.Name())
|
return fmt.Errorf("plugin %q has the same ID %s as %q", p.Name(), p.GetID(), v.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ps.setSpecOpts(p)
|
||||||
|
|
||||||
ps.plugins[p.GetID()] = p
|
ps.plugins[p.GetID()] = p
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -182,20 +206,24 @@ func (ps *Store) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, er
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func pluginType(cap string) string {
|
||||||
|
return fmt.Sprintf("docker.%s/%s", strings.ToLower(cap), defaultAPIVersion)
|
||||||
|
}
|
||||||
|
|
||||||
// Handle sets a callback for a given capability. It is only used by network
|
// Handle sets a callback for a given capability. It is only used by network
|
||||||
// and ipam drivers during plugin registration. The callback registers the
|
// and ipam drivers during plugin registration. The callback registers the
|
||||||
// driver with the subsystem (network, ipam).
|
// driver with the subsystem (network, ipam).
|
||||||
func (ps *Store) Handle(capability string, callback func(string, *plugins.Client)) {
|
func (ps *Store) Handle(capability string, callback func(string, *plugins.Client)) {
|
||||||
pluginType := fmt.Sprintf("docker.%s/%s", strings.ToLower(capability), defaultAPIVersion)
|
typ := pluginType(capability)
|
||||||
|
|
||||||
// Register callback with new plugin model.
|
// Register callback with new plugin model.
|
||||||
ps.Lock()
|
ps.Lock()
|
||||||
handlers, ok := ps.handlers[pluginType]
|
handlers, ok := ps.handlers[typ]
|
||||||
if !ok {
|
if !ok {
|
||||||
handlers = []func(string, *plugins.Client){}
|
handlers = []func(string, *plugins.Client){}
|
||||||
}
|
}
|
||||||
handlers = append(handlers, callback)
|
handlers = append(handlers, callback)
|
||||||
ps.handlers[pluginType] = handlers
|
ps.handlers[typ] = handlers
|
||||||
ps.Unlock()
|
ps.Unlock()
|
||||||
|
|
||||||
// Register callback with legacy plugin model.
|
// Register callback with legacy plugin model.
|
||||||
|
@ -204,6 +232,15 @@ func (ps *Store) Handle(capability string, callback func(string, *plugins.Client
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// RegisterRuntimeOpt stores a list of SpecOpts for the provided capability.
|
||||||
|
// These options are applied to the runtime spec before a plugin is started for the specified capability.
|
||||||
|
func (ps *Store) RegisterRuntimeOpt(cap string, opts ...SpecOpt) {
|
||||||
|
ps.Lock()
|
||||||
|
defer ps.Unlock()
|
||||||
|
typ := pluginType(cap)
|
||||||
|
ps.specOpts[typ] = append(ps.specOpts[typ], opts...)
|
||||||
|
}
|
||||||
|
|
||||||
// CallHandler calls the registered callback. It is invoked during plugin enable.
|
// CallHandler calls the registered callback. It is invoked during plugin enable.
|
||||||
func (ps *Store) CallHandler(p *v2.Plugin) {
|
func (ps *Store) CallHandler(p *v2.Plugin) {
|
||||||
for _, typ := range p.GetTypes() {
|
for _, typ := range p.GetTypes() {
|
||||||
|
|
|
@ -2,6 +2,7 @@ package v2 // import "github.com/docker/docker/plugin/v2"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -9,20 +10,22 @@ import (
|
||||||
"github.com/docker/docker/pkg/plugingetter"
|
"github.com/docker/docker/pkg/plugingetter"
|
||||||
"github.com/docker/docker/pkg/plugins"
|
"github.com/docker/docker/pkg/plugins"
|
||||||
"github.com/opencontainers/go-digest"
|
"github.com/opencontainers/go-digest"
|
||||||
|
specs "github.com/opencontainers/runtime-spec/specs-go"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Plugin represents an individual plugin.
|
// Plugin represents an individual plugin.
|
||||||
type Plugin struct {
|
type Plugin struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
PluginObj types.Plugin `json:"plugin"` // todo: embed struct
|
PluginObj types.Plugin `json:"plugin"` // todo: embed struct
|
||||||
pClient *plugins.Client
|
pClient *plugins.Client
|
||||||
refCount int
|
refCount int
|
||||||
PropagatedMount string // TODO: make private
|
Rootfs string // TODO: make private
|
||||||
Rootfs string // TODO: make private
|
|
||||||
|
|
||||||
Config digest.Digest
|
Config digest.Digest
|
||||||
Blobsums []digest.Digest
|
Blobsums []digest.Digest
|
||||||
|
|
||||||
|
modifyRuntimeSpec func(*specs.Spec)
|
||||||
|
|
||||||
SwarmServiceID string
|
SwarmServiceID string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,10 +40,13 @@ func (e ErrInadequateCapability) Error() string {
|
||||||
return fmt.Sprintf("plugin does not provide %q capability", e.cap)
|
return fmt.Sprintf("plugin does not provide %q capability", e.cap)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BasePath returns the path to which all paths returned by the plugin are relative to.
|
// ScopedPath returns the path scoped to the plugin rootfs
|
||||||
// For Plugin objects this returns the host path of the plugin container's rootfs.
|
func (p *Plugin) ScopedPath(s string) string {
|
||||||
func (p *Plugin) BasePath() string {
|
if p.PluginObj.Config.PropagatedMount != "" && strings.HasPrefix(s, p.PluginObj.Config.PropagatedMount) {
|
||||||
return p.Rootfs
|
// re-scope to the propagated mount path on the host
|
||||||
|
return filepath.Join(filepath.Dir(p.Rootfs), "propagated-mount", strings.TrimPrefix(s, p.PluginObj.Config.PropagatedMount))
|
||||||
|
}
|
||||||
|
return filepath.Join(p.Rootfs, s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Client returns the plugin client.
|
// Client returns the plugin client.
|
||||||
|
@ -250,3 +256,11 @@ func (p *Plugin) Acquire() {
|
||||||
func (p *Plugin) Release() {
|
func (p *Plugin) Release() {
|
||||||
p.AddRefCount(plugingetter.Release)
|
p.AddRefCount(plugingetter.Release)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetSpecOptModifier sets the function to use to modify the the generated
|
||||||
|
// runtime spec.
|
||||||
|
func (p *Plugin) SetSpecOptModifier(f func(*specs.Spec)) {
|
||||||
|
p.mu.Lock()
|
||||||
|
p.modifyRuntimeSpec = f
|
||||||
|
p.mu.Unlock()
|
||||||
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"runtime"
|
"runtime"
|
||||||
|
"sort"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/docker/docker/api/types"
|
"github.com/docker/docker/api/types"
|
||||||
|
@ -16,6 +17,7 @@ import (
|
||||||
// InitSpec creates an OCI spec from the plugin's config.
|
// InitSpec creates an OCI spec from the plugin's config.
|
||||||
func (p *Plugin) InitSpec(execRoot string) (*specs.Spec, error) {
|
func (p *Plugin) InitSpec(execRoot string) (*specs.Spec, error) {
|
||||||
s := oci.DefaultSpec()
|
s := oci.DefaultSpec()
|
||||||
|
|
||||||
s.Root = &specs.Root{
|
s.Root = &specs.Root{
|
||||||
Path: p.Rootfs,
|
Path: p.Rootfs,
|
||||||
Readonly: false, // TODO: all plugins should be readonly? settable in config?
|
Readonly: false, // TODO: all plugins should be readonly? settable in config?
|
||||||
|
@ -31,6 +33,17 @@ func (p *Plugin) InitSpec(execRoot string) (*specs.Spec, error) {
|
||||||
return nil, errors.WithStack(err)
|
return nil, errors.WithStack(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if p.PluginObj.Config.PropagatedMount != "" {
|
||||||
|
pRoot := filepath.Join(filepath.Dir(p.Rootfs), "propagated-mount")
|
||||||
|
s.Mounts = append(s.Mounts, specs.Mount{
|
||||||
|
Source: pRoot,
|
||||||
|
Destination: p.PluginObj.Config.PropagatedMount,
|
||||||
|
Type: "bind",
|
||||||
|
Options: []string{"rbind", "rw", "rshared"},
|
||||||
|
})
|
||||||
|
s.Linux.RootfsPropagation = "rshared"
|
||||||
|
}
|
||||||
|
|
||||||
mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
|
mounts := append(p.PluginObj.Config.Mounts, types.PluginMount{
|
||||||
Source: &execRoot,
|
Source: &execRoot,
|
||||||
Destination: defaultPluginRuntimeDestination,
|
Destination: defaultPluginRuntimeDestination,
|
||||||
|
@ -88,11 +101,6 @@ func (p *Plugin) InitSpec(execRoot string) (*specs.Spec, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if p.PluginObj.Config.PropagatedMount != "" {
|
|
||||||
p.PropagatedMount = filepath.Join(p.Rootfs, p.PluginObj.Config.PropagatedMount)
|
|
||||||
s.Linux.RootfsPropagation = "rshared"
|
|
||||||
}
|
|
||||||
|
|
||||||
if p.PluginObj.Config.Linux.AllowAllDevices {
|
if p.PluginObj.Config.Linux.AllowAllDevices {
|
||||||
s.Linux.Resources.Devices = []specs.LinuxDeviceCgroup{{Allow: true, Access: "rwm"}}
|
s.Linux.Resources.Devices = []specs.LinuxDeviceCgroup{{Allow: true, Access: "rwm"}}
|
||||||
}
|
}
|
||||||
|
@ -126,5 +134,13 @@ func (p *Plugin) InitSpec(execRoot string) (*specs.Spec, error) {
|
||||||
caps.Inheritable = append(caps.Inheritable, p.PluginObj.Config.Linux.Capabilities...)
|
caps.Inheritable = append(caps.Inheritable, p.PluginObj.Config.Linux.Capabilities...)
|
||||||
caps.Effective = append(caps.Effective, p.PluginObj.Config.Linux.Capabilities...)
|
caps.Effective = append(caps.Effective, p.PluginObj.Config.Linux.Capabilities...)
|
||||||
|
|
||||||
|
if p.modifyRuntimeSpec != nil {
|
||||||
|
p.modifyRuntimeSpec(&s)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(s.Mounts, func(i, j int) bool {
|
||||||
|
return s.Mounts[i].Destination < s.Mounts[j].Destination
|
||||||
|
})
|
||||||
|
|
||||||
return &s, nil
|
return &s, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package volumedrivers // import "github.com/docker/docker/volume/drivers"
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"path/filepath"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -16,7 +15,7 @@ var (
|
||||||
|
|
||||||
type volumeDriverAdapter struct {
|
type volumeDriverAdapter struct {
|
||||||
name string
|
name string
|
||||||
baseHostPath string
|
scopePath func(s string) string
|
||||||
capabilities *volume.Capability
|
capabilities *volume.Capability
|
||||||
proxy *volumeDriverProxy
|
proxy *volumeDriverProxy
|
||||||
}
|
}
|
||||||
|
@ -30,10 +29,10 @@ func (a *volumeDriverAdapter) Create(name string, opts map[string]string) (volum
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &volumeAdapter{
|
return &volumeAdapter{
|
||||||
proxy: a.proxy,
|
proxy: a.proxy,
|
||||||
name: name,
|
name: name,
|
||||||
driverName: a.name,
|
driverName: a.name,
|
||||||
baseHostPath: a.baseHostPath,
|
scopePath: a.scopePath,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,13 +40,6 @@ func (a *volumeDriverAdapter) Remove(v volume.Volume) error {
|
||||||
return a.proxy.Remove(v.Name())
|
return a.proxy.Remove(v.Name())
|
||||||
}
|
}
|
||||||
|
|
||||||
func hostPath(baseHostPath, path string) string {
|
|
||||||
if baseHostPath != "" {
|
|
||||||
path = filepath.Join(baseHostPath, path)
|
|
||||||
}
|
|
||||||
return path
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *volumeDriverAdapter) List() ([]volume.Volume, error) {
|
func (a *volumeDriverAdapter) List() ([]volume.Volume, error) {
|
||||||
ls, err := a.proxy.List()
|
ls, err := a.proxy.List()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -57,11 +49,11 @@ func (a *volumeDriverAdapter) List() ([]volume.Volume, error) {
|
||||||
var out []volume.Volume
|
var out []volume.Volume
|
||||||
for _, vp := range ls {
|
for _, vp := range ls {
|
||||||
out = append(out, &volumeAdapter{
|
out = append(out, &volumeAdapter{
|
||||||
proxy: a.proxy,
|
proxy: a.proxy,
|
||||||
name: vp.Name,
|
name: vp.Name,
|
||||||
baseHostPath: a.baseHostPath,
|
scopePath: a.scopePath,
|
||||||
driverName: a.name,
|
driverName: a.name,
|
||||||
eMount: hostPath(a.baseHostPath, vp.Mountpoint),
|
eMount: a.scopePath(vp.Mountpoint),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return out, nil
|
return out, nil
|
||||||
|
@ -79,13 +71,13 @@ func (a *volumeDriverAdapter) Get(name string) (volume.Volume, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
return &volumeAdapter{
|
return &volumeAdapter{
|
||||||
proxy: a.proxy,
|
proxy: a.proxy,
|
||||||
name: v.Name,
|
name: v.Name,
|
||||||
driverName: a.Name(),
|
driverName: a.Name(),
|
||||||
eMount: v.Mountpoint,
|
eMount: v.Mountpoint,
|
||||||
createdAt: v.CreatedAt,
|
createdAt: v.CreatedAt,
|
||||||
status: v.Status,
|
status: v.Status,
|
||||||
baseHostPath: a.baseHostPath,
|
scopePath: a.scopePath,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,13 +114,13 @@ func (a *volumeDriverAdapter) getCapabilities() volume.Capability {
|
||||||
}
|
}
|
||||||
|
|
||||||
type volumeAdapter struct {
|
type volumeAdapter struct {
|
||||||
proxy *volumeDriverProxy
|
proxy *volumeDriverProxy
|
||||||
name string
|
name string
|
||||||
baseHostPath string
|
scopePath func(string) string
|
||||||
driverName string
|
driverName string
|
||||||
eMount string // ephemeral host volume path
|
eMount string // ephemeral host volume path
|
||||||
createdAt time.Time // time the directory was created
|
createdAt time.Time // time the directory was created
|
||||||
status map[string]interface{}
|
status map[string]interface{}
|
||||||
}
|
}
|
||||||
|
|
||||||
type proxyVolume struct {
|
type proxyVolume struct {
|
||||||
|
@ -149,7 +141,7 @@ func (a *volumeAdapter) DriverName() string {
|
||||||
func (a *volumeAdapter) Path() string {
|
func (a *volumeAdapter) Path() string {
|
||||||
if len(a.eMount) == 0 {
|
if len(a.eMount) == 0 {
|
||||||
mountpoint, _ := a.proxy.Path(a.name)
|
mountpoint, _ := a.proxy.Path(a.name)
|
||||||
a.eMount = hostPath(a.baseHostPath, mountpoint)
|
a.eMount = a.scopePath(mountpoint)
|
||||||
}
|
}
|
||||||
return a.eMount
|
return a.eMount
|
||||||
}
|
}
|
||||||
|
@ -160,7 +152,7 @@ func (a *volumeAdapter) CachedPath() string {
|
||||||
|
|
||||||
func (a *volumeAdapter) Mount(id string) (string, error) {
|
func (a *volumeAdapter) Mount(id string) (string, error) {
|
||||||
mountpoint, err := a.proxy.Mount(a.name, id)
|
mountpoint, err := a.proxy.Mount(a.name, id)
|
||||||
a.eMount = hostPath(a.baseHostPath, mountpoint)
|
a.eMount = a.scopePath(mountpoint)
|
||||||
return a.eMount, err
|
return a.eMount, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,9 +25,9 @@ var drivers = &driverExtpoint{
|
||||||
const extName = "VolumeDriver"
|
const extName = "VolumeDriver"
|
||||||
|
|
||||||
// NewVolumeDriver returns a driver has the given name mapped on the given client.
|
// NewVolumeDriver returns a driver has the given name mapped on the given client.
|
||||||
func NewVolumeDriver(name string, baseHostPath string, c client) volume.Driver {
|
func NewVolumeDriver(name string, scopePath func(string) string, c client) volume.Driver {
|
||||||
proxy := &volumeDriverProxy{c}
|
proxy := &volumeDriverProxy{c}
|
||||||
return &volumeDriverAdapter{name: name, baseHostPath: baseHostPath, proxy: proxy}
|
return &volumeDriverAdapter{name: name, scopePath: scopePath, proxy: proxy}
|
||||||
}
|
}
|
||||||
|
|
||||||
// volumeDriver defines the available functions that volume plugins must implement.
|
// volumeDriver defines the available functions that volume plugins must implement.
|
||||||
|
@ -129,7 +129,7 @@ func lookup(name string, mode int) (volume.Driver, error) {
|
||||||
return nil, errors.Wrap(err, "error looking up volume plugin "+name)
|
return nil, errors.Wrap(err, "error looking up volume plugin "+name)
|
||||||
}
|
}
|
||||||
|
|
||||||
d := NewVolumeDriver(p.Name(), p.BasePath(), p.Client())
|
d := NewVolumeDriver(p.Name(), p.ScopedPath, p.Client())
|
||||||
if err := validateDriver(d); err != nil {
|
if err := validateDriver(d); err != nil {
|
||||||
if mode > 0 {
|
if mode > 0 {
|
||||||
// Undo any reference count changes from the initial `Get`
|
// Undo any reference count changes from the initial `Get`
|
||||||
|
@ -224,7 +224,7 @@ func GetAllDrivers() ([]volume.Driver, error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
ext := NewVolumeDriver(name, p.BasePath(), p.Client())
|
ext := NewVolumeDriver(name, p.ScopedPath, p.Client())
|
||||||
if p.IsV1() {
|
if p.IsV1() {
|
||||||
drivers.extensions[name] = ext
|
drivers.extensions[name] = ext
|
||||||
}
|
}
|
||||||
|
|
|
@ -178,8 +178,8 @@ func (p *fakePlugin) IsV1() bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *fakePlugin) BasePath() string {
|
func (p *fakePlugin) ScopedPath(s string) string {
|
||||||
return ""
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
type fakePluginGetter struct {
|
type fakePluginGetter struct {
|
||||||
|
|
Loading…
Reference in New Issue