2018-02-05 16:05:59 -05:00
|
|
|
package testutils // import "github.com/docker/docker/volume/testutils"
|
2015-09-18 19:58:05 -04:00
|
|
|
|
|
|
|
import (
|
2017-11-15 13:13:22 -05:00
|
|
|
"encoding/json"
|
|
|
|
"errors"
|
2015-09-18 19:58:05 -04:00
|
|
|
"fmt"
|
2017-11-15 13:13:22 -05:00
|
|
|
"net"
|
|
|
|
"net/http"
|
2017-05-17 17:19:13 -04:00
|
|
|
"time"
|
2015-09-18 19:58:05 -04:00
|
|
|
|
2017-11-15 13:13:22 -05:00
|
|
|
"github.com/docker/docker/pkg/plugingetter"
|
|
|
|
"github.com/docker/docker/pkg/plugins"
|
2015-09-18 19:58:05 -04:00
|
|
|
"github.com/docker/docker/volume"
|
|
|
|
)
|
|
|
|
|
|
|
|
// NoopVolume is a volume that doesn't perform any operation
|
|
|
|
type NoopVolume struct{}
|
|
|
|
|
|
|
|
// Name is the name of the volume
|
|
|
|
func (NoopVolume) Name() string { return "noop" }
|
|
|
|
|
|
|
|
// DriverName is the name of the driver
|
|
|
|
func (NoopVolume) DriverName() string { return "noop" }
|
|
|
|
|
|
|
|
// Path is the filesystem path to the volume
|
|
|
|
func (NoopVolume) Path() string { return "noop" }
|
|
|
|
|
|
|
|
// Mount mounts the volume in the container
|
2016-03-07 21:41:44 -05:00
|
|
|
func (NoopVolume) Mount(_ string) (string, error) { return "noop", nil }
|
2015-09-18 19:58:05 -04:00
|
|
|
|
|
|
|
// Unmount unmounts the volume from the container
|
2016-03-07 21:41:44 -05:00
|
|
|
func (NoopVolume) Unmount(_ string) error { return nil }
|
2015-09-18 19:58:05 -04:00
|
|
|
|
2017-05-21 19:24:07 -04:00
|
|
|
// Status provides low-level details about the volume
|
2016-03-07 15:44:43 -05:00
|
|
|
func (NoopVolume) Status() map[string]interface{} { return nil }
|
|
|
|
|
2017-05-17 17:19:13 -04:00
|
|
|
// CreatedAt provides the time the volume (directory) was created at
|
|
|
|
func (NoopVolume) CreatedAt() (time.Time, error) { return time.Now(), nil }
|
|
|
|
|
2015-09-18 19:58:05 -04:00
|
|
|
// FakeVolume is a fake volume with a random name
|
|
|
|
type FakeVolume struct {
|
2016-03-22 16:24:09 -04:00
|
|
|
name string
|
|
|
|
driverName string
|
2015-09-18 19:58:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// NewFakeVolume creates a new fake volume for testing
|
2016-03-22 16:24:09 -04:00
|
|
|
func NewFakeVolume(name string, driverName string) volume.Volume {
|
|
|
|
return FakeVolume{name: name, driverName: driverName}
|
2015-09-18 19:58:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Name is the name of the volume
|
|
|
|
func (f FakeVolume) Name() string { return f.name }
|
|
|
|
|
|
|
|
// DriverName is the name of the driver
|
2016-03-22 16:24:09 -04:00
|
|
|
func (f FakeVolume) DriverName() string { return f.driverName }
|
2015-09-18 19:58:05 -04:00
|
|
|
|
|
|
|
// Path is the filesystem path to the volume
|
|
|
|
func (FakeVolume) Path() string { return "fake" }
|
|
|
|
|
|
|
|
// Mount mounts the volume in the container
|
2016-03-07 21:41:44 -05:00
|
|
|
func (FakeVolume) Mount(_ string) (string, error) { return "fake", nil }
|
2015-09-18 19:58:05 -04:00
|
|
|
|
|
|
|
// Unmount unmounts the volume from the container
|
2016-03-07 21:41:44 -05:00
|
|
|
func (FakeVolume) Unmount(_ string) error { return nil }
|
2015-09-18 19:58:05 -04:00
|
|
|
|
2017-05-21 19:24:07 -04:00
|
|
|
// Status provides low-level details about the volume
|
2016-03-07 15:44:43 -05:00
|
|
|
func (FakeVolume) Status() map[string]interface{} { return nil }
|
|
|
|
|
2017-05-17 17:19:13 -04:00
|
|
|
// CreatedAt provides the time the volume (directory) was created at
|
|
|
|
func (FakeVolume) CreatedAt() (time.Time, error) { return time.Now(), nil }
|
|
|
|
|
2015-09-18 19:58:05 -04:00
|
|
|
// FakeDriver is a driver that generates fake volumes
|
2015-09-23 16:29:14 -04:00
|
|
|
type FakeDriver struct {
|
|
|
|
name string
|
|
|
|
vols map[string]volume.Volume
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewFakeDriver creates a new FakeDriver with the specified name
|
|
|
|
func NewFakeDriver(name string) volume.Driver {
|
|
|
|
return &FakeDriver{
|
|
|
|
name: name,
|
|
|
|
vols: make(map[string]volume.Volume),
|
|
|
|
}
|
|
|
|
}
|
2015-09-18 19:58:05 -04:00
|
|
|
|
|
|
|
// Name is the name of the driver
|
2015-09-23 16:29:14 -04:00
|
|
|
func (d *FakeDriver) Name() string { return d.name }
|
2015-09-18 19:58:05 -04:00
|
|
|
|
|
|
|
// Create initializes a fake volume.
|
|
|
|
// It returns an error if the options include an "error" key with a message
|
2015-09-23 16:29:14 -04:00
|
|
|
func (d *FakeDriver) Create(name string, opts map[string]string) (volume.Volume, error) {
|
2015-09-18 19:58:05 -04:00
|
|
|
if opts != nil && opts["error"] != "" {
|
|
|
|
return nil, fmt.Errorf(opts["error"])
|
|
|
|
}
|
2016-03-22 16:24:09 -04:00
|
|
|
v := NewFakeVolume(name, d.name)
|
2015-09-23 16:29:14 -04:00
|
|
|
d.vols[name] = v
|
|
|
|
return v, nil
|
2015-09-18 19:58:05 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
// Remove deletes a volume.
|
2015-09-23 16:29:14 -04:00
|
|
|
func (d *FakeDriver) Remove(v volume.Volume) error {
|
|
|
|
if _, exists := d.vols[v.Name()]; !exists {
|
|
|
|
return fmt.Errorf("no such volume")
|
|
|
|
}
|
|
|
|
delete(d.vols, v.Name())
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// List lists the volumes
|
|
|
|
func (d *FakeDriver) List() ([]volume.Volume, error) {
|
|
|
|
var vols []volume.Volume
|
|
|
|
for _, v := range d.vols {
|
|
|
|
vols = append(vols, v)
|
|
|
|
}
|
|
|
|
return vols, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get gets the volume
|
|
|
|
func (d *FakeDriver) Get(name string) (volume.Volume, error) {
|
|
|
|
if v, exists := d.vols[name]; exists {
|
|
|
|
return v, nil
|
|
|
|
}
|
|
|
|
return nil, fmt.Errorf("no such volume")
|
|
|
|
}
|
2016-04-11 11:17:52 -04:00
|
|
|
|
|
|
|
// Scope returns the local scope
|
|
|
|
func (*FakeDriver) Scope() string {
|
|
|
|
return "local"
|
|
|
|
}
|
2017-11-15 13:13:22 -05:00
|
|
|
|
|
|
|
type fakePlugin struct {
|
|
|
|
client *plugins.Client
|
|
|
|
name string
|
|
|
|
refs int
|
|
|
|
}
|
|
|
|
|
|
|
|
// MakeFakePlugin creates a fake plugin from the passed in driver
|
|
|
|
// Note: currently only "Create" is implemented because that's all that's needed
|
|
|
|
// so far. If you need it to test something else, add it here, but probably you
|
|
|
|
// shouldn't need to use this except for very specific cases with v2 plugin handling.
|
|
|
|
func MakeFakePlugin(d volume.Driver, l net.Listener) (plugingetter.CompatPlugin, error) {
|
|
|
|
c, err := plugins.NewClient(l.Addr().Network()+"://"+l.Addr().String(), nil)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
mux := http.NewServeMux()
|
|
|
|
|
|
|
|
mux.HandleFunc("/VolumeDriver.Create", func(w http.ResponseWriter, r *http.Request) {
|
|
|
|
createReq := struct {
|
|
|
|
Name string
|
|
|
|
Opts map[string]string
|
|
|
|
}{}
|
|
|
|
if err := json.NewDecoder(r.Body).Decode(&createReq); err != nil {
|
|
|
|
fmt.Fprintf(w, `{"Err": "%s"}`, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
_, err := d.Create(createReq.Name, createReq.Opts)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Fprintf(w, `{"Err": "%s"}`, err.Error())
|
|
|
|
return
|
|
|
|
}
|
|
|
|
w.Write([]byte("{}"))
|
|
|
|
})
|
|
|
|
|
|
|
|
go http.Serve(l, mux)
|
|
|
|
return &fakePlugin{client: c, name: d.Name()}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *fakePlugin) Client() *plugins.Client {
|
|
|
|
return p.client
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *fakePlugin) Name() string {
|
|
|
|
return p.name
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *fakePlugin) IsV1() bool {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2017-12-14 10:27:10 -05:00
|
|
|
func (p *fakePlugin) ScopedPath(s string) string {
|
|
|
|
return s
|
2017-11-15 13:13:22 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
type fakePluginGetter struct {
|
|
|
|
plugins map[string]plugingetter.CompatPlugin
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewFakePluginGetter returns a plugin getter for fake plugins
|
|
|
|
func NewFakePluginGetter(pls ...plugingetter.CompatPlugin) plugingetter.PluginGetter {
|
|
|
|
idx := make(map[string]plugingetter.CompatPlugin, len(pls))
|
|
|
|
for _, p := range pls {
|
|
|
|
idx[p.Name()] = p
|
|
|
|
}
|
|
|
|
return &fakePluginGetter{plugins: idx}
|
|
|
|
}
|
|
|
|
|
|
|
|
// This ignores the second argument since we only care about volume drivers here,
|
|
|
|
// there shouldn't be any other kind of plugin in here
|
|
|
|
func (g *fakePluginGetter) Get(name, _ string, mode int) (plugingetter.CompatPlugin, error) {
|
|
|
|
p, ok := g.plugins[name]
|
|
|
|
if !ok {
|
|
|
|
return nil, errors.New("not found")
|
|
|
|
}
|
|
|
|
p.(*fakePlugin).refs += mode
|
|
|
|
return p, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *fakePluginGetter) GetAllByCap(capability string) ([]plugingetter.CompatPlugin, error) {
|
|
|
|
panic("GetAllByCap shouldn't be called")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *fakePluginGetter) GetAllManagedPluginsByCap(capability string) []plugingetter.CompatPlugin {
|
|
|
|
panic("GetAllManagedPluginsByCap should not be called")
|
|
|
|
}
|
|
|
|
|
|
|
|
func (g *fakePluginGetter) Handle(capability string, callback func(string, *plugins.Client)) {
|
|
|
|
panic("Handle should not be called")
|
|
|
|
}
|
|
|
|
|
|
|
|
// FakeRefs checks ref count on a fake plugin.
|
|
|
|
func FakeRefs(p plugingetter.CompatPlugin) int {
|
|
|
|
// this should panic if something other than a `*fakePlugin` is passed in
|
|
|
|
return p.(*fakePlugin).refs
|
|
|
|
}
|