diff --git a/components/engine/daemon/daemon.go b/components/engine/daemon/daemon.go index 8c36f042ea..9812c0be6c 100644 --- a/components/engine/daemon/daemon.go +++ b/components/engine/daemon/daemon.go @@ -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 diff --git a/components/engine/daemon/daemon_test.go b/components/engine/daemon/daemon_test.go index 2fb4ff902a..2fe4276d7a 100644 --- a/components/engine/daemon/daemon_test.go +++ b/components/engine/daemon/daemon_test.go @@ -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 { diff --git a/components/engine/daemon/daemon_unix_test.go b/components/engine/daemon/daemon_unix_test.go index 84281c0b8c..36c6030988 100644 --- a/components/engine/daemon/daemon_unix_test.go +++ b/components/engine/daemon/daemon_unix_test.go @@ -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) - } - } -} diff --git a/components/engine/daemon/daemon_windows.go b/components/engine/daemon/daemon_windows.go index 50507e5fad..1f801032df 100644 --- a/components/engine/daemon/daemon_windows.go +++ b/components/engine/daemon/daemon_windows.go @@ -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 } diff --git a/components/engine/daemon/disk_usage.go b/components/engine/daemon/disk_usage.go index cad441f9cb..344627afa0 100644 --- a/components/engine/daemon/disk_usage.go +++ b/components/engine/daemon/disk_usage.go @@ -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 diff --git a/components/engine/daemon/info.go b/components/engine/daemon/info.go index 6f5ae69896..7b011fe324 100644 --- a/components/engine/daemon/info.go +++ b/components/engine/daemon/info.go @@ -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. diff --git a/components/engine/daemon/prune.go b/components/engine/daemon/prune.go index 286e1e4bd2..ba7e3485d0 100644 --- a/components/engine/daemon/prune.go +++ b/components/engine/daemon/prune.go @@ -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 diff --git a/components/engine/daemon/volumes.go b/components/engine/daemon/volumes.go index a63a90771a..948409a00a 100644 --- a/components/engine/daemon/volumes.go +++ b/components/engine/daemon/volumes.go @@ -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 -} diff --git a/components/engine/daemon/volumes_unix.go b/components/engine/daemon/volumes_unix.go index b752dbbb66..acf57436a6 100644 --- a/components/engine/daemon/volumes_unix.go +++ b/components/engine/daemon/volumes_unix.go @@ -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 { diff --git a/components/engine/integration-cli/docker_cli_daemon_test.go b/components/engine/integration-cli/docker_cli_daemon_test.go index b9ed41ca96..dea7a1ae60 100644 --- a/components/engine/integration-cli/docker_cli_daemon_test.go +++ b/components/engine/integration-cli/docker_cli_daemon_test.go @@ -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) diff --git a/components/engine/volume/drivers/adapter.go b/components/engine/volume/drivers/adapter.go index 8415c7f6c9..d5d9ea823e 100644 --- a/components/engine/volume/drivers/adapter.go +++ b/components/engine/volume/drivers/adapter.go @@ -1,4 +1,4 @@ -package volumedrivers // import "github.com/docker/docker/volume/drivers" +package drivers // import "github.com/docker/docker/volume/drivers" import ( "errors" diff --git a/components/engine/volume/drivers/extpoint.go b/components/engine/volume/drivers/extpoint.go index 15cf850d74..14e3b4f625 100644 --- a/components/engine/volume/drivers/extpoint.go +++ b/components/engine/volume/drivers/extpoint.go @@ -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) } diff --git a/components/engine/volume/drivers/extpoint_test.go b/components/engine/volume/drivers/extpoint_test.go index fa18322a5d..384742ea00 100644 --- a/components/engine/volume/drivers/extpoint_test.go +++ b/components/engine/volume/drivers/extpoint_test.go @@ -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) } diff --git a/components/engine/volume/drivers/proxy.go b/components/engine/volume/drivers/proxy.go index 6715e0ea9b..8a44faeddc 100644 --- a/components/engine/volume/drivers/proxy.go +++ b/components/engine/volume/drivers/proxy.go @@ -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" diff --git a/components/engine/volume/drivers/proxy_test.go b/components/engine/volume/drivers/proxy_test.go index 220186d298..79af956333 100644 --- a/components/engine/volume/drivers/proxy_test.go +++ b/components/engine/volume/drivers/proxy_test.go @@ -1,4 +1,4 @@ -package volumedrivers // import "github.com/docker/docker/volume/drivers" +package drivers // import "github.com/docker/docker/volume/drivers" import ( "fmt" diff --git a/components/engine/volume/store/restore.go b/components/engine/volume/store/restore.go index 790a5b65f7..2e072ec087 100644 --- a/components/engine/volume/store/restore.go +++ b/components/engine/volume/store/restore.go @@ -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() diff --git a/components/engine/volume/store/restore_test.go b/components/engine/volume/store/restore_test.go index 680735a384..5c3c6df72c 100644 --- a/components/engine/volume/store/restore_test.go +++ b/components/engine/volume/store/restore_test.go @@ -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") diff --git a/components/engine/volume/store/store.go b/components/engine/volume/store/store.go index 70dd8b2d89..4f4cffafd7 100644 --- a/components/engine/volume/store/store.go +++ b/components/engine/volume/store/store.go @@ -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() +} diff --git a/components/engine/volume/store/store_test.go b/components/engine/volume/store/store_test.go index faf4035e29..288a4ce824 100644 --- a/components/engine/volume/store/store_test.go +++ b/components/engine/volume/store/store_test.go @@ -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() } }