mirror of
https://github.com/moby/moby.git
synced 2022-11-09 12:21:53 -05:00
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"
|
"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 (
|
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")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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
|
package 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
|
package 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 ""
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,7 +65,6 @@ func (pm *Manager) enable(p *v2.Plugin, c *controller, force bool) error {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return pm.pluginPostStart(p, c)
|
return pm.pluginPostStart(p, c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@ package v2
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
@ -39,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.
|
||||||
|
|
|
@ -2,7 +2,6 @@ package volumedrivers
|
||||||
|
|
||||||
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 a new issue