Merge pull request #36637 from cpuguy83/no_global_driver_store

No global volume driver store
This commit is contained in:
Brian Goff 2018-04-18 16:32:23 -04:00 committed by GitHub
commit 6cd806aa53
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 236 additions and 582 deletions

View file

@ -179,11 +179,6 @@ func (daemon *Daemon) restore() error {
delete(containers, id)
continue
}
// verify that all volumes valid and have been migrated from the pre-1.7 layout
if err := daemon.verifyVolumesInfo(c); err != nil {
// don't skip the container due to error
logrus.Errorf("Failed to verify volumes for container '%s': %v", c.ID, err)
}
if err := daemon.Register(c); err != nil {
logrus.Errorf("Failed to register container %s: %s", c.ID, err)
delete(containers, id)
@ -1150,17 +1145,15 @@ func setDefaultMtu(conf *config.Config) {
}
func (daemon *Daemon) configureVolumes(rootIDs idtools.IDPair) (*store.VolumeStore, error) {
volumesDriver, err := local.New(daemon.configStore.Root, rootIDs)
volumeDriver, err := local.New(daemon.configStore.Root, rootIDs)
if err != nil {
return nil, err
}
volumedrivers.RegisterPluginGetter(daemon.PluginStore)
if !volumedrivers.Register(volumesDriver, volumesDriver.Name()) {
drivers := volumedrivers.NewStore(daemon.PluginStore)
if !drivers.Register(volumeDriver, volumeDriver.Name()) {
return nil, errors.New("local volume driver could not be registered")
}
return store.New(daemon.configStore.Root)
return store.New(daemon.configStore.Root, drivers)
}
// IsShuttingDown tells whether the daemon is shutting down or not

View file

@ -13,7 +13,6 @@ import (
_ "github.com/docker/docker/pkg/discovery/memory"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/pkg/truncindex"
"github.com/docker/docker/volume"
volumedrivers "github.com/docker/docker/volume/drivers"
"github.com/docker/docker/volume/local"
"github.com/docker/docker/volume/store"
@ -121,7 +120,8 @@ func initDaemonWithVolumeStore(tmp string) (*Daemon, error) {
repository: tmp,
root: tmp,
}
daemon.volumes, err = store.New(tmp)
drivers := volumedrivers.NewStore(nil)
daemon.volumes, err = store.New(tmp, drivers)
if err != nil {
return nil, err
}
@ -130,7 +130,7 @@ func initDaemonWithVolumeStore(tmp string) (*Daemon, error) {
if err != nil {
return nil, err
}
volumedrivers.Register(volumesDriver, volumesDriver.Name())
drivers.Register(volumesDriver, volumesDriver.Name())
return daemon, nil
}
@ -208,7 +208,6 @@ func TestContainerInitDNS(t *testing.T) {
if err != nil {
t.Fatal(err)
}
defer volumedrivers.Unregister(volume.DefaultDriverName)
c, err := daemon.load(containerID)
if err != nil {

View file

@ -6,18 +6,11 @@ import (
"errors"
"io/ioutil"
"os"
"path/filepath"
"testing"
containertypes "github.com/docker/docker/api/types/container"
"github.com/docker/docker/container"
"github.com/docker/docker/daemon/config"
"github.com/docker/docker/pkg/idtools"
"github.com/docker/docker/volume"
"github.com/docker/docker/volume/drivers"
"github.com/docker/docker/volume/local"
"github.com/docker/docker/volume/store"
"github.com/gotestyourself/gotestyourself/assert"
)
type fakeContainerGetter struct {
@ -273,85 +266,3 @@ func TestNetworkOptions(t *testing.T) {
t.Fatal("Expected networkOptions error, got nil")
}
}
func TestMigratePre17Volumes(t *testing.T) {
rootDir, err := ioutil.TempDir("", "test-daemon-volumes")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(rootDir)
volumeRoot := filepath.Join(rootDir, "volumes")
err = os.MkdirAll(volumeRoot, 0755)
if err != nil {
t.Fatal(err)
}
containerRoot := filepath.Join(rootDir, "containers")
cid := "1234"
err = os.MkdirAll(filepath.Join(containerRoot, cid), 0755)
assert.NilError(t, err)
vid := "5678"
vfsPath := filepath.Join(rootDir, "vfs", "dir", vid)
err = os.MkdirAll(vfsPath, 0755)
assert.NilError(t, err)
config := []byte(`
{
"ID": "` + cid + `",
"Volumes": {
"/foo": "` + vfsPath + `",
"/bar": "/foo",
"/quux": "/quux"
},
"VolumesRW": {
"/foo": true,
"/bar": true,
"/quux": false
}
}
`)
volStore, err := store.New(volumeRoot)
if err != nil {
t.Fatal(err)
}
drv, err := local.New(volumeRoot, idtools.IDPair{UID: 0, GID: 0})
if err != nil {
t.Fatal(err)
}
volumedrivers.Register(drv, volume.DefaultDriverName)
daemon := &Daemon{
root: rootDir,
repository: containerRoot,
volumes: volStore,
}
err = ioutil.WriteFile(filepath.Join(containerRoot, cid, "config.v2.json"), config, 600)
if err != nil {
t.Fatal(err)
}
c, err := daemon.load(cid)
if err != nil {
t.Fatal(err)
}
if err := daemon.verifyVolumesInfo(c); err != nil {
t.Fatal(err)
}
expected := map[string]volume.MountPoint{
"/foo": {Destination: "/foo", RW: true, Name: vid},
"/bar": {Source: "/foo", Destination: "/bar", RW: true},
"/quux": {Source: "/quux", Destination: "/quux", RW: false},
}
for id, mp := range c.MountPoints {
x, exists := expected[id]
if !exists {
t.Fatal("volume not migrated")
}
if mp.Source != x.Source || mp.Destination != x.Destination || mp.RW != x.RW || mp.Name != x.Name {
t.Fatalf("got unexpected mountpoint, expected: %+v, got: %+v", x, mp)
}
}
}

View file

@ -632,13 +632,6 @@ func setupDaemonProcess(config *config.Config) error {
return nil
}
// verifyVolumesInfo is a no-op on windows.
// This is called during daemon initialization to migrate volumes from pre-1.7.
// volumes were not supported on windows pre-1.7
func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error {
return nil
}
func (daemon *Daemon) setupSeccompProfile() error {
return nil
}

View file

@ -35,41 +35,39 @@ func (daemon *Daemon) SystemDiskUsage(ctx context.Context) (*types.DiskUsage, er
return nil, fmt.Errorf("failed to retrieve image list: %v", err)
}
// Get all local volumes
allVolumes := []*types.Volume{}
getLocalVols := func(v volume.Volume) error {
select {
case <-ctx.Done():
return ctx.Err()
default:
if d, ok := v.(volume.DetailedVolume); ok {
// skip local volumes with mount options since these could have external
// mounted filesystems that will be slow to enumerate.
if len(d.Options()) > 0 {
return nil
}
}
name := v.Name()
refs := daemon.volumes.Refs(v)
tv := volumeToAPIType(v)
sz, err := directory.Size(ctx, v.Path())
if err != nil {
logrus.Warnf("failed to determine size of volume %v", name)
sz = -1
}
tv.UsageData = &types.VolumeUsageData{Size: sz, RefCount: int64(len(refs))}
allVolumes = append(allVolumes, tv)
}
return nil
}
err = daemon.traverseLocalVolumes(getLocalVols)
volumes, err := daemon.volumes.FilterByDriver(volume.DefaultDriverName)
if err != nil {
return nil, err
}
var allVolumes []*types.Volume
for _, v := range volumes {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
}
if d, ok := v.(volume.DetailedVolume); ok {
if len(d.Options()) > 0 {
// skip local volumes with mount options since these could have external
// mounted filesystems that will be slow to enumerate.
continue
}
}
name := v.Name()
refs := daemon.volumes.Refs(v)
tv := volumeToAPIType(v)
sz, err := directory.Size(ctx, v.Path())
if err != nil {
logrus.Warnf("failed to determine size of volume %v", name)
sz = -1
}
tv.UsageData = &types.VolumeUsageData{Size: sz, RefCount: int64(len(refs))}
allVolumes = append(allVolumes, tv)
}
allLayersSize, err := daemon.imageService.LayerDiskUsage(ctx)
if err != nil {
return nil, err

View file

@ -19,7 +19,6 @@ import (
"github.com/docker/docker/pkg/sysinfo"
"github.com/docker/docker/pkg/system"
"github.com/docker/docker/registry"
"github.com/docker/docker/volume/drivers"
"github.com/docker/go-connections/sockets"
"github.com/sirupsen/logrus"
)
@ -196,7 +195,7 @@ func (daemon *Daemon) SystemVersion() types.Version {
func (daemon *Daemon) showPluginsInfo() types.PluginsInfo {
var pluginsInfo types.PluginsInfo
pluginsInfo.Volume = volumedrivers.GetDriverList()
pluginsInfo.Volume = daemon.volumes.GetDriverList()
pluginsInfo.Network = daemon.GetNetworkDriverList()
// The authorization plugins are returned in the order they are
// used as they constitute a request/response modification chain.

View file

@ -107,11 +107,20 @@ func (daemon *Daemon) VolumesPrune(ctx context.Context, pruneFilters filters.Arg
rep := &types.VolumesPruneReport{}
pruneVols := func(v volume.Volume) error {
volumes, err := daemon.volumes.FilterByDriver(volume.DefaultDriverName)
if err != nil {
return nil, err
}
for _, v := range volumes {
select {
case <-ctx.Done():
logrus.Debugf("VolumesPrune operation cancelled: %#v", *rep)
return ctx.Err()
err := ctx.Err()
if err == context.Canceled {
return rep, nil
}
return rep, err
default:
}
@ -122,9 +131,10 @@ func (daemon *Daemon) VolumesPrune(ctx context.Context, pruneFilters filters.Arg
detailedVolume, ok := v.(volume.DetailedVolume)
if ok {
if !matchLabels(pruneFilters, detailedVolume.Labels()) {
return nil
continue
}
}
vSize, err := directory.Size(ctx, v.Path())
if err != nil {
logrus.Warnf("could not determine size of volume %s: %v", name, err)
@ -132,21 +142,15 @@ func (daemon *Daemon) VolumesPrune(ctx context.Context, pruneFilters filters.Arg
err = daemon.volumeRm(v)
if err != nil {
logrus.Warnf("could not remove volume %s: %v", name, err)
return nil
continue
}
rep.SpaceReclaimed += uint64(vSize)
rep.VolumesDeleted = append(rep.VolumesDeleted, name)
}
return nil
}
err = daemon.traverseLocalVolumes(pruneVols)
if err == context.Canceled {
return rep, nil
}
return rep, err
return rep, nil
}
// localNetworksPrune removes unused local networks

View file

@ -1,7 +1,6 @@
package daemon // import "github.com/docker/docker/daemon"
import (
"fmt"
"os"
"path/filepath"
"reflect"
@ -15,7 +14,6 @@ import (
"github.com/docker/docker/container"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/volume"
"github.com/docker/docker/volume/drivers"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
@ -385,32 +383,3 @@ func (daemon *Daemon) backportMountSpec(container *container.Container) {
cm.Spec.ReadOnly = !cm.RW
}
}
func (daemon *Daemon) traverseLocalVolumes(fn func(volume.Volume) error) error {
localVolumeDriver, err := volumedrivers.GetDriver(volume.DefaultDriverName)
if err != nil {
return fmt.Errorf("can't retrieve local volume driver: %v", err)
}
vols, err := localVolumeDriver.List()
if err != nil {
return fmt.Errorf("can't retrieve local volumes: %v", err)
}
for _, v := range vols {
name := v.Name()
vol, err := daemon.volumes.Get(name)
if err != nil {
logrus.Warnf("failed to retrieve volume %s from store: %v", name, err)
} else {
// daemon.volumes.Get will return DetailedVolume
v = vol
}
err = fn(v)
if err != nil {
return err
}
}
return nil
}

View file

@ -3,10 +3,8 @@
package daemon // import "github.com/docker/docker/daemon"
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
"sort"
"strconv"
"strings"
@ -15,9 +13,6 @@ import (
"github.com/docker/docker/pkg/fileutils"
"github.com/docker/docker/pkg/mount"
"github.com/docker/docker/volume"
"github.com/docker/docker/volume/drivers"
"github.com/docker/docker/volume/local"
"github.com/pkg/errors"
)
// setupMounts iterates through each of the mount points for a container and
@ -113,80 +108,6 @@ func setBindModeIfNull(bind *volume.MountPoint) {
}
}
// migrateVolume links the contents of a volume created pre Docker 1.7
// into the location expected by the local driver.
// It creates a symlink from DOCKER_ROOT/vfs/dir/VOLUME_ID to DOCKER_ROOT/volumes/VOLUME_ID/_container_data.
// It preserves the volume json configuration generated pre Docker 1.7 to be able to
// downgrade from Docker 1.7 to Docker 1.6 without losing volume compatibility.
func migrateVolume(id, vfs string) error {
l, err := volumedrivers.GetDriver(volume.DefaultDriverName)
if err != nil {
return err
}
newDataPath := l.(*local.Root).DataPath(id)
fi, err := os.Stat(newDataPath)
if err != nil && !os.IsNotExist(err) {
return err
}
if fi != nil && fi.IsDir() {
return nil
}
return os.Symlink(vfs, newDataPath)
}
// verifyVolumesInfo ports volumes configured for the containers pre docker 1.7.
// It reads the container configuration and creates valid mount points for the old volumes.
func (daemon *Daemon) verifyVolumesInfo(container *container.Container) error {
container.Lock()
defer container.Unlock()
// Inspect old structures only when we're upgrading from old versions
// to versions >= 1.7 and the MountPoints has not been populated with volumes data.
type volumes struct {
Volumes map[string]string
VolumesRW map[string]bool
}
cfgPath, err := container.ConfigPath()
if err != nil {
return err
}
f, err := os.Open(cfgPath)
if err != nil {
return errors.Wrap(err, "could not open container config")
}
defer f.Close()
var cv volumes
if err := json.NewDecoder(f).Decode(&cv); err != nil {
return errors.Wrap(err, "could not decode container config")
}
if len(container.MountPoints) == 0 && len(cv.Volumes) > 0 {
for destination, hostPath := range cv.Volumes {
vfsPath := filepath.Join(daemon.root, "vfs", "dir")
rw := cv.VolumesRW != nil && cv.VolumesRW[destination]
if strings.HasPrefix(hostPath, vfsPath) {
id := filepath.Base(hostPath)
v, err := daemon.volumes.CreateWithRef(id, volume.DefaultDriverName, container.ID, nil, nil)
if err != nil {
return err
}
if err := migrateVolume(id, hostPath); err != nil {
return err
}
container.AddMountPointWithVolume(destination, v, true)
} else { // Bind mount
m := volume.MountPoint{Source: hostPath, Destination: destination, RW: rw}
container.MountPoints[destination] = &m
}
}
}
return nil
}
func (daemon *Daemon) mountVolumes(container *container.Container) error {
mounts, err := daemon.setupMounts(container)
if err != nil {

View file

@ -35,7 +35,6 @@ import (
testdaemon "github.com/docker/docker/internal/test/daemon"
"github.com/docker/docker/opts"
"github.com/docker/docker/pkg/mount"
"github.com/docker/docker/pkg/stringid"
units "github.com/docker/go-units"
"github.com/docker/libnetwork/iptables"
"github.com/docker/libtrust"
@ -2668,84 +2667,6 @@ func (s *DockerDaemonSuite) TestDaemonRestartSaveContainerExitCode(c *check.C) {
c.Assert(out, checker.Equals, errMsg1)
}
func (s *DockerDaemonSuite) TestDaemonBackcompatPre17Volumes(c *check.C) {
testRequires(c, SameHostDaemon)
d := s.d
d.StartWithBusybox(c)
// hack to be able to side-load a container config
out, err := d.Cmd("create", "busybox:latest")
c.Assert(err, checker.IsNil, check.Commentf(out))
id := strings.TrimSpace(out)
out, err = d.Cmd("inspect", "--type=image", "--format={{.ID}}", "busybox:latest")
c.Assert(err, checker.IsNil, check.Commentf(out))
d.Stop(c)
<-d.Wait
imageID := strings.TrimSpace(out)
volumeID := stringid.GenerateNonCryptoID()
vfsPath := filepath.Join(d.Root, "vfs", "dir", volumeID)
c.Assert(os.MkdirAll(vfsPath, 0755), checker.IsNil)
config := []byte(`
{
"ID": "` + id + `",
"Name": "hello",
"Driver": "` + d.StorageDriver() + `",
"Image": "` + imageID + `",
"Config": {"Image": "busybox:latest"},
"NetworkSettings": {},
"Volumes": {
"/bar":"/foo",
"/foo": "` + vfsPath + `",
"/quux":"/quux"
},
"VolumesRW": {
"/bar": true,
"/foo": true,
"/quux": false
}
}
`)
configPath := filepath.Join(d.Root, "containers", id, "config.v2.json")
c.Assert(ioutil.WriteFile(configPath, config, 600), checker.IsNil)
d.Start(c)
out, err = d.Cmd("inspect", "--type=container", "--format={{ json .Mounts }}", id)
c.Assert(err, checker.IsNil, check.Commentf(out))
type mount struct {
Name string
Source string
Destination string
Driver string
RW bool
}
ls := []mount{}
err = json.NewDecoder(strings.NewReader(out)).Decode(&ls)
c.Assert(err, checker.IsNil)
expected := []mount{
{Source: "/foo", Destination: "/bar", RW: true},
{Name: volumeID, Destination: "/foo", RW: true},
{Source: "/quux", Destination: "/quux", RW: false},
}
c.Assert(ls, checker.HasLen, len(expected))
for _, m := range ls {
var matched bool
for _, x := range expected {
if m.Source == x.Source && m.Destination == x.Destination && m.RW == x.RW || m.Name != x.Name {
matched = true
break
}
}
c.Assert(matched, checker.True, check.Commentf("did find match for %+v", m))
}
}
func (s *DockerDaemonSuite) TestDaemonWithUserlandProxyPath(c *check.C) {
testRequires(c, SameHostDaemon, DaemonIsLinux)

View file

@ -1,4 +1,4 @@
package volumedrivers // import "github.com/docker/docker/volume/drivers"
package drivers // import "github.com/docker/docker/volume/drivers"
import (
"errors"

View file

@ -1,12 +1,13 @@
//go:generate pluginrpc-gen -i $GOFILE -o proxy.go -type volumeDriver -name VolumeDriver
package volumedrivers // import "github.com/docker/docker/volume/drivers"
package drivers // import "github.com/docker/docker/volume/drivers"
import (
"fmt"
"sort"
"sync"
"github.com/docker/docker/errdefs"
"github.com/docker/docker/pkg/locker"
getter "github.com/docker/docker/pkg/plugingetter"
"github.com/docker/docker/volume"
@ -14,14 +15,6 @@ import (
"github.com/sirupsen/logrus"
)
// 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{},
}
const extName = "VolumeDriver"
// NewVolumeDriver returns a driver has the given name mapped on the given client.
@ -53,53 +46,21 @@ type volumeDriver interface {
Capabilities() (capabilities volume.Capability, err error)
}
type driverExtpoint struct {
extensions map[string]volume.Driver
sync.Mutex
// Store is an in-memory store for volume drivers
type Store struct {
extensions map[string]volume.Driver
mu sync.Mutex
driverLock *locker.Locker
plugingetter getter.PluginGetter
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
// the name is already associated
func Register(extension volume.Driver, name string) bool {
if name == "" {
return false
// NewStore creates a new volume driver store
func NewStore(pg getter.PluginGetter) *Store {
return &Store{
extensions: make(map[string]volume.Driver),
driverLock: locker.New(),
pluginGetter: pg,
}
drivers.Lock()
defer drivers.Unlock()
_, exists := drivers.extensions[name]
if exists {
return false
}
if err := validateDriver(extension); err != nil {
return false
}
drivers.extensions[name] = extension
return true
}
// Unregister dissociates the name from its driver, if the association exists.
func Unregister(name string) bool {
drivers.Lock()
defer drivers.Unlock()
_, exists := drivers.extensions[name]
if !exists {
return false
}
delete(drivers.extensions, name)
return true
}
type driverNotFoundError string
@ -113,18 +74,21 @@ func (driverNotFoundError) NotFound() {}
// lookup returns the driver associated with the given name. If a
// driver with the given name has not been registered it checks if
// there is a VolumeDriver plugin available with the given name.
func lookup(name string, mode int) (volume.Driver, error) {
drivers.driverLock.Lock(name)
defer drivers.driverLock.Unlock(name)
func (s *Store) lookup(name string, mode int) (volume.Driver, error) {
if name == "" {
return nil, errdefs.InvalidParameter(errors.New("driver name cannot be empty"))
}
s.driverLock.Lock(name)
defer s.driverLock.Unlock(name)
drivers.Lock()
ext, ok := drivers.extensions[name]
drivers.Unlock()
s.mu.Lock()
ext, ok := s.extensions[name]
s.mu.Unlock()
if ok {
return ext, nil
}
if drivers.plugingetter != nil {
p, err := drivers.plugingetter.Get(name, extName, mode)
if s.pluginGetter != nil {
p, err := s.pluginGetter.Get(name, extName, mode)
if err != nil {
return nil, errors.Wrap(err, "error looking up volume plugin "+name)
}
@ -133,7 +97,7 @@ func lookup(name string, mode int) (volume.Driver, error) {
if err := validateDriver(d); err != nil {
if mode > 0 {
// Undo any reference count changes from the initial `Get`
if _, err := drivers.plugingetter.Get(name, extName, mode*-1); err != nil {
if _, err := s.pluginGetter.Get(name, extName, mode*-1); err != nil {
logrus.WithError(err).WithField("action", "validate-driver").WithField("plugin", name).Error("error releasing reference to plugin")
}
}
@ -141,9 +105,9 @@ func lookup(name string, mode int) (volume.Driver, error) {
}
if p.IsV1() {
drivers.Lock()
drivers.extensions[name] = d
drivers.Unlock()
s.mu.Lock()
s.extensions[name] = d
s.mu.Unlock()
}
return d, nil
}
@ -158,75 +122,88 @@ func validateDriver(vd volume.Driver) error {
return nil
}
// Register associates the given driver to the given name, checking if
// the name is already associated
func (s *Store) Register(d volume.Driver, name string) bool {
if name == "" {
return false
}
s.mu.Lock()
defer s.mu.Unlock()
if _, exists := s.extensions[name]; exists {
return false
}
if err := validateDriver(d); err != nil {
return false
}
s.extensions[name] = d
return true
}
// GetDriver returns a volume driver by its name.
// If the driver is empty, it looks for the local driver.
func GetDriver(name string) (volume.Driver, error) {
if name == "" {
name = volume.DefaultDriverName
}
return lookup(name, getter.Lookup)
func (s *Store) GetDriver(name string) (volume.Driver, error) {
return s.lookup(name, getter.Lookup)
}
// CreateDriver returns a volume driver by its name and increments RefCount.
// If the driver is empty, it looks for the local driver.
func CreateDriver(name string) (volume.Driver, error) {
if name == "" {
name = volume.DefaultDriverName
}
return lookup(name, getter.Acquire)
func (s *Store) CreateDriver(name string) (volume.Driver, error) {
return s.lookup(name, getter.Acquire)
}
// ReleaseDriver returns a volume driver by its name and decrements RefCount..
// If the driver is empty, it looks for the local driver.
func ReleaseDriver(name string) (volume.Driver, error) {
if name == "" {
name = volume.DefaultDriverName
}
return lookup(name, getter.Release)
func (s *Store) ReleaseDriver(name string) (volume.Driver, error) {
return s.lookup(name, getter.Release)
}
// GetDriverList returns list of volume drivers registered.
// If no driver is registered, empty string list will be returned.
func GetDriverList() []string {
func (s *Store) GetDriverList() []string {
var driverList []string
drivers.Lock()
for driverName := range drivers.extensions {
s.mu.Lock()
for driverName := range s.extensions {
driverList = append(driverList, driverName)
}
drivers.Unlock()
s.mu.Unlock()
sort.Strings(driverList)
return driverList
}
// GetAllDrivers lists all the registered drivers
func GetAllDrivers() ([]volume.Driver, error) {
func (s *Store) GetAllDrivers() ([]volume.Driver, error) {
var plugins []getter.CompatPlugin
if drivers.plugingetter != nil {
if s.pluginGetter != nil {
var err error
plugins, err = drivers.plugingetter.GetAllByCap(extName)
plugins, err = s.pluginGetter.GetAllByCap(extName)
if err != nil {
return nil, fmt.Errorf("error listing plugins: %v", err)
}
}
var ds []volume.Driver
drivers.Lock()
defer drivers.Unlock()
s.mu.Lock()
defer s.mu.Unlock()
for _, d := range drivers.extensions {
for _, d := range s.extensions {
ds = append(ds, d)
}
for _, p := range plugins {
name := p.Name()
if _, ok := drivers.extensions[name]; ok {
if _, ok := s.extensions[name]; ok {
continue
}
ext := NewVolumeDriver(name, p.ScopedPath, p.Client())
if p.IsV1() {
drivers.extensions[name] = ext
s.extensions[name] = ext
}
ds = append(ds, ext)
}

View file

@ -1,4 +1,4 @@
package volumedrivers // import "github.com/docker/docker/volume/drivers"
package drivers // import "github.com/docker/docker/volume/drivers"
import (
"testing"
@ -7,13 +7,14 @@ import (
)
func TestGetDriver(t *testing.T) {
_, err := GetDriver("missing")
s := NewStore(nil)
_, err := s.GetDriver("missing")
if err == nil {
t.Fatal("Expected error, was nil")
}
Register(volumetestutils.NewFakeDriver("fake"), "fake")
s.Register(volumetestutils.NewFakeDriver("fake"), "fake")
d, err := GetDriver("fake")
d, err := s.GetDriver("fake")
if err != nil {
t.Fatal(err)
}

View file

@ -1,6 +1,6 @@
// generated code - DO NOT EDIT
package volumedrivers // import "github.com/docker/docker/volume/drivers"
package drivers // import "github.com/docker/docker/volume/drivers"
import (
"errors"

View file

@ -1,4 +1,4 @@
package volumedrivers // import "github.com/docker/docker/volume/drivers"
package drivers // import "github.com/docker/docker/volume/drivers"
import (
"fmt"

View file

@ -5,7 +5,6 @@ import (
"github.com/boltdb/bolt"
"github.com/docker/docker/volume"
"github.com/docker/docker/volume/drivers"
"github.com/sirupsen/logrus"
)
@ -33,7 +32,7 @@ func (s *VolumeStore) restore() {
var v volume.Volume
var err error
if meta.Driver != "" {
v, err = lookupVolume(meta.Driver, meta.Name)
v, err = lookupVolume(s.drivers, meta.Driver, meta.Name)
if err != nil && err != errNoSuchVolume {
logrus.WithError(err).WithField("driver", meta.Driver).WithField("volume", meta.Name).Warn("Error restoring volume")
return
@ -59,7 +58,7 @@ func (s *VolumeStore) restore() {
}
// increment driver refcount
volumedrivers.CreateDriver(meta.Driver)
s.drivers.CreateDriver(meta.Driver)
// cache the volume
s.globalLock.Lock()

View file

@ -18,11 +18,11 @@ func TestRestore(t *testing.T) {
assert.NilError(t, err)
defer os.RemoveAll(dir)
drivers := volumedrivers.NewStore(nil)
driverName := "test-restore"
volumedrivers.Register(volumetestutils.NewFakeDriver(driverName), driverName)
defer volumedrivers.Unregister("test-restore")
drivers.Register(volumetestutils.NewFakeDriver(driverName), driverName)
s, err := New(dir)
s, err := New(dir, drivers)
assert.NilError(t, err)
defer s.Shutdown()
@ -36,7 +36,7 @@ func TestRestore(t *testing.T) {
s.Shutdown()
s, err = New(dir)
s, err = New(dir, drivers)
assert.NilError(t, err)
v, err := s.Get("test1")

View file

@ -66,13 +66,14 @@ func (v volumeWrapper) CachedPath() string {
// New initializes a VolumeStore to keep
// reference counting of volumes in the system.
func New(rootPath string) (*VolumeStore, error) {
func New(rootPath string, drivers *drivers.Store) (*VolumeStore, error) {
vs := &VolumeStore{
locks: &locker.Locker{},
names: make(map[string]volume.Volume),
refs: make(map[string]map[string]struct{}),
labels: make(map[string]map[string]string),
options: make(map[string]map[string]string),
drivers: drivers,
}
if rootPath != "" {
@ -157,7 +158,7 @@ func (s *VolumeStore) Purge(name string) {
v, exists := s.names[name]
if exists {
driverName := v.DriverName()
if _, err := volumedrivers.ReleaseDriver(driverName); err != nil {
if _, err := s.drivers.ReleaseDriver(driverName); err != nil {
logrus.WithError(err).WithField("driver", driverName).Error("Error releasing reference to volume driver")
}
}
@ -175,7 +176,8 @@ func (s *VolumeStore) Purge(name string) {
type VolumeStore struct {
// locks ensures that only one action is being performed on a particular volume at a time without locking the entire store
// since actions on volumes can be quite slow, this ensures the store is free to handle requests for other volumes.
locks *locker.Locker
locks *locker.Locker
drivers *drivers.Store
// globalLock is used to protect access to mutable structures used by the store object
globalLock sync.RWMutex
// names stores the volume name -> volume relationship.
@ -226,7 +228,7 @@ func (s *VolumeStore) list() ([]volume.Volume, []string, error) {
warnings []string
)
drivers, err := volumedrivers.GetAllDrivers()
drivers, err := s.drivers.GetAllDrivers()
if err != nil {
return nil, nil, err
}
@ -329,7 +331,7 @@ func (s *VolumeStore) checkConflict(name, driverName string) (volume.Volume, err
if driverName != "" {
// Retrieve canonical driver name to avoid inconsistencies (for example
// "plugin" vs. "plugin:latest")
vd, err := volumedrivers.GetDriver(driverName)
vd, err := s.drivers.GetDriver(driverName)
if err != nil {
return nil, err
}
@ -341,7 +343,7 @@ func (s *VolumeStore) checkConflict(name, driverName string) (volume.Volume, err
// let's check if the found volume ref
// is stale by checking with the driver if it still exists
exists, err := volumeExists(v)
exists, err := volumeExists(s.drivers, v)
if err != nil {
return nil, errors.Wrapf(errNameConflict, "found reference to volume '%s' in driver '%s', but got an error while checking the driver: %v", name, vDriverName, err)
}
@ -366,8 +368,8 @@ func (s *VolumeStore) checkConflict(name, driverName string) (volume.Volume, err
// volumeExists returns if the volume is still present in the driver.
// An error is returned if there was an issue communicating with the driver.
func volumeExists(v volume.Volume) (bool, error) {
exists, err := lookupVolume(v.DriverName(), v.Name())
func volumeExists(store *drivers.Store, v volume.Volume) (bool, error) {
exists, err := lookupVolume(store, v.DriverName(), v.Name())
if err != nil {
return false, err
}
@ -412,7 +414,10 @@ func (s *VolumeStore) create(name, driverName string, opts, labels map[string]st
}
}
vd, err := volumedrivers.CreateDriver(driverName)
if driverName == "" {
driverName = volume.DefaultDriverName
}
vd, err := s.drivers.CreateDriver(driverName)
if err != nil {
return nil, &OpErr{Op: "create", Name: name, Err: err}
}
@ -421,7 +426,7 @@ func (s *VolumeStore) create(name, driverName string, opts, labels map[string]st
if v, _ = vd.Get(name); v == nil {
v, err = vd.Create(name, opts)
if err != nil {
if _, err := volumedrivers.ReleaseDriver(driverName); err != nil {
if _, err := s.drivers.ReleaseDriver(driverName); err != nil {
logrus.WithError(err).WithField("driver", driverName).Error("Error releasing reference to volume driver")
}
return nil, err
@ -455,7 +460,10 @@ func (s *VolumeStore) GetWithRef(name, driverName, ref string) (volume.Volume, e
s.locks.Lock(name)
defer s.locks.Unlock(name)
vd, err := volumedrivers.GetDriver(driverName)
if driverName == "" {
driverName = volume.DefaultDriverName
}
vd, err := s.drivers.GetDriver(driverName)
if err != nil {
return nil, &OpErr{Err: err, Name: name, Op: "get"}
}
@ -510,7 +518,7 @@ func (s *VolumeStore) getVolume(name string) (volume.Volume, error) {
}
if meta.Driver != "" {
vol, err := lookupVolume(meta.Driver, name)
vol, err := lookupVolume(s.drivers, meta.Driver, name)
if err != nil {
return nil, err
}
@ -520,7 +528,7 @@ func (s *VolumeStore) getVolume(name string) (volume.Volume, error) {
}
var scope string
vd, err := volumedrivers.GetDriver(meta.Driver)
vd, err := s.drivers.GetDriver(meta.Driver)
if err == nil {
scope = vd.Scope()
}
@ -528,7 +536,7 @@ func (s *VolumeStore) getVolume(name string) (volume.Volume, error) {
}
logrus.Debugf("Probing all drivers for volume with name: %s", name)
drivers, err := volumedrivers.GetAllDrivers()
drivers, err := s.drivers.GetAllDrivers()
if err != nil {
return nil, err
}
@ -552,8 +560,11 @@ func (s *VolumeStore) getVolume(name string) (volume.Volume, error) {
// If the driver returns an error that is not communication related the
// error is logged but not returned.
// If the volume is not found it will return `nil, nil``
func lookupVolume(driverName, volumeName string) (volume.Volume, error) {
vd, err := volumedrivers.GetDriver(driverName)
func lookupVolume(store *drivers.Store, driverName, volumeName string) (volume.Volume, error) {
if driverName == "" {
driverName = volume.DefaultDriverName
}
vd, err := store.GetDriver(driverName)
if err != nil {
return nil, errors.Wrapf(err, "error while checking if volume %q exists in driver %q", volumeName, driverName)
}
@ -585,7 +596,7 @@ func (s *VolumeStore) Remove(v volume.Volume) error {
return &OpErr{Err: errVolumeInUse, Name: v.Name(), Op: "remove", Refs: s.getRefs(name)}
}
vd, err := volumedrivers.GetDriver(v.DriverName())
vd, err := s.drivers.GetDriver(v.DriverName())
if err != nil {
return &OpErr{Err: err, Name: v.DriverName(), Op: "remove"}
}
@ -627,7 +638,7 @@ func (s *VolumeStore) Refs(v volume.Volume) []string {
// FilterByDriver returns the available volumes filtered by driver name
func (s *VolumeStore) FilterByDriver(name string) ([]volume.Volume, error) {
vd, err := volumedrivers.GetDriver(name)
vd, err := s.drivers.GetDriver(name)
if err != nil {
return nil, &OpErr{Err: err, Name: name, Op: "list"}
}
@ -686,3 +697,10 @@ func unwrapVolume(v volume.Volume) volume.Volume {
func (s *VolumeStore) Shutdown() error {
return s.db.Close()
}
// GetDriverList gets the list of volume drivers from the configured volume driver
// store.
// TODO(@cpuguy83): This should be factored out into a separate service.
func (s *VolumeStore) GetDriverList() []string {
return s.drivers.GetDriverList()
}

View file

@ -10,7 +10,7 @@ import (
"testing"
"github.com/docker/docker/volume"
"github.com/docker/docker/volume/drivers"
volumedrivers "github.com/docker/docker/volume/drivers"
volumetestutils "github.com/docker/docker/volume/testutils"
"github.com/google/go-cmp/cmp"
"github.com/gotestyourself/gotestyourself/assert"
@ -18,18 +18,12 @@ import (
)
func TestCreate(t *testing.T) {
volumedrivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
defer volumedrivers.Unregister("fake")
dir, err := ioutil.TempDir("", "test-create")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
t.Parallel()
s, cleanup := setupTest(t)
defer cleanup()
s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
s, err := New(dir)
if err != nil {
t.Fatal(err)
}
v, err := s.Create("fake1", "fake", nil, nil)
if err != nil {
t.Fatal(err)
@ -53,19 +47,13 @@ func TestCreate(t *testing.T) {
}
func TestRemove(t *testing.T) {
volumedrivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
volumedrivers.Register(volumetestutils.NewFakeDriver("noop"), "noop")
defer volumedrivers.Unregister("fake")
defer volumedrivers.Unregister("noop")
dir, err := ioutil.TempDir("", "test-remove")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
s, err := New(dir)
if err != nil {
t.Fatal(err)
}
t.Parallel()
s, cleanup := setupTest(t)
defer cleanup()
s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
s.drivers.Register(volumetestutils.NewFakeDriver("noop"), "noop")
// doing string compare here since this error comes directly from the driver
expected := "no such volume"
@ -91,20 +79,19 @@ func TestRemove(t *testing.T) {
}
func TestList(t *testing.T) {
volumedrivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
volumedrivers.Register(volumetestutils.NewFakeDriver("fake2"), "fake2")
defer volumedrivers.Unregister("fake")
defer volumedrivers.Unregister("fake2")
t.Parallel()
dir, err := ioutil.TempDir("", "test-list")
if err != nil {
t.Fatal(err)
}
assert.NilError(t, err)
defer os.RemoveAll(dir)
s, err := New(dir)
if err != nil {
t.Fatal(err)
}
drivers := volumedrivers.NewStore(nil)
drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
drivers.Register(volumetestutils.NewFakeDriver("fake2"), "fake2")
s, err := New(dir, drivers)
assert.NilError(t, err)
if _, err := s.Create("test", "fake", nil, nil); err != nil {
t.Fatal(err)
}
@ -124,7 +111,7 @@ func TestList(t *testing.T) {
}
// and again with a new store
s, err = New(dir)
s, err = New(dir, drivers)
if err != nil {
t.Fatal(err)
}
@ -138,18 +125,12 @@ func TestList(t *testing.T) {
}
func TestFilterByDriver(t *testing.T) {
volumedrivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
volumedrivers.Register(volumetestutils.NewFakeDriver("noop"), "noop")
defer volumedrivers.Unregister("fake")
defer volumedrivers.Unregister("noop")
dir, err := ioutil.TempDir("", "test-filter-driver")
if err != nil {
t.Fatal(err)
}
s, err := New(dir)
if err != nil {
t.Fatal(err)
}
t.Parallel()
s, cleanup := setupTest(t)
defer cleanup()
s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
s.drivers.Register(volumetestutils.NewFakeDriver("noop"), "noop")
if _, err := s.Create("fake1", "fake", nil, nil); err != nil {
t.Fatal(err)
@ -171,17 +152,12 @@ func TestFilterByDriver(t *testing.T) {
}
func TestFilterByUsed(t *testing.T) {
volumedrivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
volumedrivers.Register(volumetestutils.NewFakeDriver("noop"), "noop")
dir, err := ioutil.TempDir("", "test-filter-used")
if err != nil {
t.Fatal(err)
}
t.Parallel()
s, cleanup := setupTest(t)
defer cleanup()
s, err := New(dir)
if err != nil {
t.Fatal(err)
}
s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
s.drivers.Register(volumetestutils.NewFakeDriver("noop"), "noop")
if _, err := s.CreateWithRef("fake1", "fake", "volReference", nil, nil); err != nil {
t.Fatal(err)
@ -213,16 +189,10 @@ func TestFilterByUsed(t *testing.T) {
}
func TestDerefMultipleOfSameRef(t *testing.T) {
volumedrivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
dir, err := ioutil.TempDir("", "test-same-deref")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
s, err := New(dir)
if err != nil {
t.Fatal(err)
}
t.Parallel()
s, cleanup := setupTest(t)
defer cleanup()
s.drivers.Register(volumetestutils.NewFakeDriver("fake"), "fake")
v, err := s.CreateWithRef("fake1", "fake", "volReference", nil, nil)
if err != nil {
@ -240,17 +210,12 @@ func TestDerefMultipleOfSameRef(t *testing.T) {
}
func TestCreateKeepOptsLabelsWhenExistsRemotely(t *testing.T) {
t.Parallel()
s, cleanup := setupTest(t)
defer cleanup()
vd := volumetestutils.NewFakeDriver("fake")
volumedrivers.Register(vd, "fake")
dir, err := ioutil.TempDir("", "test-same-deref")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
s, err := New(dir)
if err != nil {
t.Fatal(err)
}
s.drivers.Register(vd, "fake")
// Create a volume in the driver directly
if _, err := vd.Create("foo", nil); err != nil {
@ -273,6 +238,8 @@ func TestCreateKeepOptsLabelsWhenExistsRemotely(t *testing.T) {
}
func TestDefererencePluginOnCreateError(t *testing.T) {
t.Parallel()
var (
l net.Listener
err error
@ -286,6 +253,9 @@ func TestDefererencePluginOnCreateError(t *testing.T) {
}
defer l.Close()
s, cleanup := setupTest(t)
defer cleanup()
d := volumetestutils.NewFakeDriver("TestDefererencePluginOnCreateError")
p, err := volumetestutils.MakeFakePlugin(d, l)
if err != nil {
@ -293,19 +263,7 @@ func TestDefererencePluginOnCreateError(t *testing.T) {
}
pg := volumetestutils.NewFakePluginGetter(p)
volumedrivers.RegisterPluginGetter(pg)
defer volumedrivers.RegisterPluginGetter(nil)
dir, err := ioutil.TempDir("", "test-plugin-deref-err")
if err != nil {
t.Fatal(err)
}
defer os.RemoveAll(dir)
s, err := New(dir)
if err != nil {
t.Fatal(err)
}
s.drivers = volumedrivers.NewStore(pg)
// create a good volume so we have a plugin reference
_, err = s.Create("fake1", d.Name(), nil, nil)
@ -329,8 +287,9 @@ func TestRefDerefRemove(t *testing.T) {
t.Parallel()
driverName := "test-ref-deref-remove"
s, cleanup := setupTest(t, driverName)
defer cleanup(t)
s, cleanup := setupTest(t)
defer cleanup()
s.drivers.Register(volumetestutils.NewFakeDriver(driverName), driverName)
v, err := s.CreateWithRef("test", driverName, "test-ref", nil, nil)
assert.NilError(t, err)
@ -348,8 +307,9 @@ func TestGet(t *testing.T) {
t.Parallel()
driverName := "test-get"
s, cleanup := setupTest(t, driverName)
defer cleanup(t)
s, cleanup := setupTest(t)
defer cleanup()
s.drivers.Register(volumetestutils.NewFakeDriver(driverName), driverName)
_, err := s.Get("not-exist")
assert.Assert(t, is.ErrorContains(err, ""))
@ -373,8 +333,9 @@ func TestGetWithRef(t *testing.T) {
t.Parallel()
driverName := "test-get-with-ref"
s, cleanup := setupTest(t, driverName)
defer cleanup(t)
s, cleanup := setupTest(t)
defer cleanup()
s.drivers.Register(volumetestutils.NewFakeDriver(driverName), driverName)
_, err := s.GetWithRef("not-exist", driverName, "test-ref")
assert.Assert(t, is.ErrorContains(err, ""))
@ -397,32 +358,22 @@ func TestGetWithRef(t *testing.T) {
var cmpVolume = cmp.AllowUnexported(volumetestutils.FakeVolume{}, volumeWrapper{})
func setupTest(t *testing.T, name string) (*VolumeStore, func(*testing.T)) {
t.Helper()
s, cleanup := newTestStore(t)
volumedrivers.Register(volumetestutils.NewFakeDriver(name), name)
return s, func(t *testing.T) {
cleanup(t)
volumedrivers.Unregister(name)
}
}
func newTestStore(t *testing.T) (*VolumeStore, func(*testing.T)) {
func setupTest(t *testing.T) (*VolumeStore, func()) {
t.Helper()
dir, err := ioutil.TempDir("", "store-root")
dirName := strings.Replace(t.Name(), string(os.PathSeparator), "_", -1)
dir, err := ioutil.TempDir("", dirName)
assert.NilError(t, err)
cleanup := func(t *testing.T) {
cleanup := func() {
err := os.RemoveAll(dir)
assert.Check(t, err)
}
s, err := New(dir)
s, err := New(dir, volumedrivers.NewStore(nil))
assert.Check(t, err)
return s, func(t *testing.T) {
return s, func() {
s.Shutdown()
cleanup(t)
cleanup()
}
}