dotcloud/docker

View on GitHub
volume/testutils/testutils.go

Summary

Maintainability
A
0 mins
Test Coverage
package testutils // import "github.com/docker/docker/volume/testutils"

import (
    "encoding/json"
    "errors"
    "fmt"
    "net"
    "net/http"
    "time"

    "github.com/docker/docker/pkg/plugingetter"
    "github.com/docker/docker/pkg/plugins"
    "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
func (NoopVolume) Mount(_ string) (string, error) { return "noop", nil }

// Unmount unmounts the volume from the container
func (NoopVolume) Unmount(_ string) error { return nil }

// Status provides low-level details about the volume
func (NoopVolume) Status() map[string]interface{} { return nil }

// CreatedAt provides the time the volume (directory) was created at
func (NoopVolume) CreatedAt() (time.Time, error) { return time.Now(), nil }

// FakeVolume is a fake volume with a random name
type FakeVolume struct {
    name       string
    driverName string
    createdAt  time.Time
}

// NewFakeVolume creates a new fake volume for testing
func NewFakeVolume(name string, driverName string) volume.Volume {
    return FakeVolume{name: name, driverName: driverName, createdAt: time.Now()}
}

// Name is the name of the volume
func (f FakeVolume) Name() string { return f.name }

// DriverName is the name of the driver
func (f FakeVolume) DriverName() string { return f.driverName }

// Path is the filesystem path to the volume
func (FakeVolume) Path() string { return "fake" }

// Mount mounts the volume in the container
func (FakeVolume) Mount(_ string) (string, error) { return "fake", nil }

// Unmount unmounts the volume from the container
func (FakeVolume) Unmount(_ string) error { return nil }

// Status provides low-level details about the volume
func (FakeVolume) Status() map[string]interface{} {
    return map[string]interface{}{"datakey": "datavalue"}
}

// CreatedAt provides the time the volume (directory) was created at
func (f FakeVolume) CreatedAt() (time.Time, error) {
    return f.createdAt, nil
}

// FakeDriver is a driver that generates fake volumes
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),
    }
}

// Name is the name of the driver
func (d *FakeDriver) Name() string { return d.name }

// Create initializes a fake volume.
// It returns an error if the options include an "error" key with a message
func (d *FakeDriver) Create(name string, opts map[string]string) (volume.Volume, error) {
    if opts != nil && opts["error"] != "" {
        return nil, errors.New(opts["error"])
    }
    v := NewFakeVolume(name, d.name)
    d.vols[name] = v
    return v, nil
}

// Remove deletes a volume.
func (d *FakeDriver) Remove(v volume.Volume) error {
    if _, exists := d.vols[v.Name()]; !exists {
        return errors.New("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, errors.New("no such volume")
}

// Scope returns the local scope
func (*FakeDriver) Scope() string {
    return "local"
}

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) // #nosec G114 -- Ignoring for test-code: G114: Use of net/http serve function that has no support for setting timeouts (gosec)
    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
}

func (p *fakePlugin) ScopedPath(s string) string {
    return s
}

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
}