Ensure plugin returns correctly scoped paths
Before this change, volume management was relying on the fact that everything the plugin mounts is visible on the host within the plugin's rootfs. In practice this caused some issues with mount leaks, so we changed the behavior such that mounts are not visible on the plugin's rootfs, but available outside of it, which breaks volume management. To fix the issue, allow the plugin to scope the path correctly rather than assuming that everything is visible in `p.Rootfs`. In practice this is just scoping the `PropagatedMount` paths to the correct host path. Signed-off-by: Brian Goff <cpuguy83@gmail.com>
This commit is contained in:
parent
a53930a04f
commit
0e5eaf8ee3
11 changed files with 60 additions and 68 deletions
|
@ -4,7 +4,6 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/docker/docker/pkg/archive"
|
||||
"github.com/docker/docker/pkg/containerfs"
|
||||
|
@ -143,7 +142,7 @@ func (d *graphDriverProxy) Get(id, mountLabel string) (containerfs.ContainerFS,
|
|||
if 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 {
|
||||
|
|
|
@ -3,7 +3,7 @@ package logger
|
|||
import (
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -19,7 +19,6 @@ type pluginAdapter struct {
|
|||
driverName string
|
||||
id string
|
||||
plugin logPlugin
|
||||
basePath string
|
||||
fifoPath string
|
||||
capabilities Capability
|
||||
logInfo Info
|
||||
|
@ -58,7 +57,7 @@ func (a *pluginAdapter) Close() error {
|
|||
a.mu.Lock()
|
||||
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
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,6 @@ import (
|
|||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/docker/docker/api/types/plugins/logdriver"
|
||||
getter "github.com/docker/docker/pkg/plugingetter"
|
||||
|
@ -39,18 +38,20 @@ func getPlugin(name string, mode int) (Creator, error) {
|
|||
}
|
||||
|
||||
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) {
|
||||
defer func() {
|
||||
if err != nil {
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -59,8 +60,7 @@ func makePluginCreator(name string, l *logPluginProxy, basePath string) Creator
|
|||
driverName: name,
|
||||
id: id,
|
||||
plugin: l,
|
||||
basePath: basePath,
|
||||
fifoPath: filepath.Join(root, id),
|
||||
fifoPath: filepath.Join(logRoot, id),
|
||||
logInfo: logCtx,
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ func makePluginCreator(name string, l *logPluginProxy, basePath string) Creator
|
|||
a.stream = 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")
|
||||
}
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ const (
|
|||
type CompatPlugin interface {
|
||||
Client() *plugins.Client
|
||||
Name() string
|
||||
BasePath() string
|
||||
ScopedPath(string) string
|
||||
IsV1() bool
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
|
||||
package plugins
|
||||
|
||||
// BasePath returns the path to which all paths returned by the plugin are relative to.
|
||||
// For v1 plugins, this always returns the host's root directory.
|
||||
func (p *Plugin) BasePath() string {
|
||||
return "/"
|
||||
// ScopedPath returns the path scoped to the plugin's rootfs.
|
||||
// For v1 plugins, this always returns the path unchanged as v1 plugins run directly on the host.
|
||||
func (p *Plugin) ScopedPath(s string) string {
|
||||
return s
|
||||
}
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package plugins
|
||||
|
||||
// BasePath returns the path to which all paths returned by the plugin are relative to.
|
||||
// For Windows v1 plugins, this returns an empty string, since the plugin is already aware
|
||||
// of the absolute path of the mount.
|
||||
func (p *Plugin) BasePath() string {
|
||||
return ""
|
||||
// ScopedPath returns the path scoped to the plugin's rootfs.
|
||||
// For v1 plugins, this always returns the path unchanged as v1 plugins run directly on the host.
|
||||
func (p *Plugin) ScopedPath(s string) string {
|
||||
return s
|
||||
}
|
||||
|
|
|
@ -65,7 +65,6 @@ func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pm.pluginPostStart(p, c)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,6 +2,7 @@ package v2
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
|
@ -39,10 +40,13 @@ func (e ErrInadequateCapability) Error() string {
|
|||
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.
|
||||
// For Plugin objects this returns the host path of the plugin container's rootfs.
|
||||
func (p *Plugin) BasePath() string {
|
||||
return p.Rootfs
|
||||
// ScopedPath returns the path scoped to the plugin rootfs
|
||||
func (p *Plugin) ScopedPath(s string) string {
|
||||
if p.PluginObj.Config.PropagatedMount != "" && strings.HasPrefix(s, p.PluginObj.Config.PropagatedMount) {
|
||||
// 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.
|
||||
|
|
|
@ -2,7 +2,6 @@ package volumedrivers
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
|
@ -16,7 +15,7 @@ var (
|
|||
|
||||
type volumeDriverAdapter struct {
|
||||
name string
|
||||
baseHostPath string
|
||||
scopePath func(s string) string
|
||||
capabilities *volume.Capability
|
||||
proxy *volumeDriverProxy
|
||||
}
|
||||
|
@ -33,7 +32,7 @@ func (a *volumeDriverAdapter) Create(name string, opts map[string]string) (volum
|
|||
proxy: a.proxy,
|
||||
name: name,
|
||||
driverName: a.name,
|
||||
baseHostPath: a.baseHostPath,
|
||||
scopePath: a.scopePath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -41,13 +40,6 @@ func (a *volumeDriverAdapter) Remove(v volume.Volume) error {
|
|||
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) {
|
||||
ls, err := a.proxy.List()
|
||||
if err != nil {
|
||||
|
@ -59,9 +51,9 @@ func (a *volumeDriverAdapter) List() ([]volume.Volume, error) {
|
|||
out = append(out, &volumeAdapter{
|
||||
proxy: a.proxy,
|
||||
name: vp.Name,
|
||||
baseHostPath: a.baseHostPath,
|
||||
scopePath: a.scopePath,
|
||||
driverName: a.name,
|
||||
eMount: hostPath(a.baseHostPath, vp.Mountpoint),
|
||||
eMount: a.scopePath(vp.Mountpoint),
|
||||
})
|
||||
}
|
||||
return out, nil
|
||||
|
@ -85,7 +77,7 @@ func (a *volumeDriverAdapter) Get(name string) (volume.Volume, error) {
|
|||
eMount: v.Mountpoint,
|
||||
createdAt: v.CreatedAt,
|
||||
status: v.Status,
|
||||
baseHostPath: a.baseHostPath,
|
||||
scopePath: a.scopePath,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@ -124,7 +116,7 @@ func (a *volumeDriverAdapter) getCapabilities() volume.Capability {
|
|||
type volumeAdapter struct {
|
||||
proxy *volumeDriverProxy
|
||||
name string
|
||||
baseHostPath string
|
||||
scopePath func(string) string
|
||||
driverName string
|
||||
eMount string // ephemeral host volume path
|
||||
createdAt time.Time // time the directory was created
|
||||
|
@ -149,7 +141,7 @@ func (a *volumeAdapter) DriverName() string {
|
|||
func (a *volumeAdapter) Path() string {
|
||||
if len(a.eMount) == 0 {
|
||||
mountpoint, _ := a.proxy.Path(a.name)
|
||||
a.eMount = hostPath(a.baseHostPath, mountpoint)
|
||||
a.eMount = a.scopePath(mountpoint)
|
||||
}
|
||||
return a.eMount
|
||||
}
|
||||
|
@ -160,7 +152,7 @@ func (a *volumeAdapter) CachedPath() string {
|
|||
|
||||
func (a *volumeAdapter) Mount(id string) (string, error) {
|
||||
mountpoint, err := a.proxy.Mount(a.name, id)
|
||||
a.eMount = hostPath(a.baseHostPath, mountpoint)
|
||||
a.eMount = a.scopePath(mountpoint)
|
||||
return a.eMount, err
|
||||
}
|
||||
|
||||
|
|
|
@ -25,9 +25,9 @@ var drivers = &driverExtpoint{
|
|||
const extName = "VolumeDriver"
|
||||
|
||||
// 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}
|
||||
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.
|
||||
|
@ -129,7 +129,7 @@ func lookup(name string, mode int) (volume.Driver, error) {
|
|||
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 mode > 0 {
|
||||
// Undo any reference count changes from the initial `Get`
|
||||
|
@ -224,7 +224,7 @@ func GetAllDrivers() ([]volume.Driver, error) {
|
|||
continue
|
||||
}
|
||||
|
||||
ext := NewVolumeDriver(name, p.BasePath(), p.Client())
|
||||
ext := NewVolumeDriver(name, p.ScopedPath, p.Client())
|
||||
if p.IsV1() {
|
||||
drivers.extensions[name] = ext
|
||||
}
|
||||
|
|
|
@ -178,8 +178,8 @@ func (p *fakePlugin) IsV1() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (p *fakePlugin) BasePath() string {
|
||||
return ""
|
||||
func (p *fakePlugin) ScopedPath(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
type fakePluginGetter struct {
|
||||
|
|
Loading…
Reference in a new issue