distribution/storagedriver/testsuites/testsuites.go
Brian Bland ca0084fad1 Adds StorageDriverFactory, unifying creation of StorageDrivers
Custom storage drivers can register a factory to create the driver by
name, similar to the database/sql package's Register and Open
factory.Create returns an in-process driver if registered or an IPC
driver if one can be found, erroring otherwise
This standardizes parameter passing for creation of storage drivers

Also adds documentation for storagedriver package and children
2014-10-28 18:21:06 -07:00

370 lines
10 KiB
Go

package testsuites
import (
"bytes"
"io/ioutil"
"math/rand"
"path"
"sort"
"testing"
"github.com/docker/docker-registry/storagedriver"
"github.com/docker/docker-registry/storagedriver/ipc"
. "gopkg.in/check.v1"
)
// Hook up gocheck into the "go test" runner
func Test(t *testing.T) { TestingT(t) }
// Registers an in-process storage driver test suite with the go test runner
//
// If skipCheck returns a non-empty skip reason, the suite is skipped with the given reason
func RegisterInProcessSuite(driverConstructor DriverConstructor, skipCheck SkipCheck) {
Suite(&DriverSuite{
Constructor: driverConstructor,
SkipCheck: skipCheck,
})
}
// Registers a storage driver test suite which runs the named driver as a child process with the given parameters
//
// If skipCheck returns a non-empty skip reason, the suite is skipped with the given reason
func RegisterIPCSuite(driverName string, ipcParams map[string]string, skipCheck SkipCheck) {
suite := &DriverSuite{
Constructor: func() (storagedriver.StorageDriver, error) {
d, err := ipc.NewDriverClient(driverName, ipcParams)
if err != nil {
return nil, err
}
err = d.Start()
if err != nil {
return nil, err
}
return d, nil
},
SkipCheck: skipCheck,
}
suite.Teardown = func() error {
if suite.StorageDriver == nil {
return nil
}
driverClient := suite.StorageDriver.(*ipc.StorageDriverClient)
return driverClient.Stop()
}
Suite(suite)
}
type SkipCheck func() (reason string)
var NeverSkip = func() string { return "" }
type DriverConstructor func() (storagedriver.StorageDriver, error)
type DriverTeardown func() error
type DriverSuite struct {
Constructor DriverConstructor
Teardown DriverTeardown
SkipCheck
storagedriver.StorageDriver
}
type TestDriverConfig struct {
name string
params map[string]string
}
func (suite *DriverSuite) SetUpSuite(c *C) {
if reason := suite.SkipCheck(); reason != "" {
c.Skip(reason)
}
d, err := suite.Constructor()
c.Assert(err, IsNil)
suite.StorageDriver = d
}
func (suite *DriverSuite) TearDownSuite(c *C) {
if suite.Teardown != nil {
err := suite.Teardown()
c.Assert(err, IsNil)
}
}
func (suite *DriverSuite) TestWriteRead1(c *C) {
filename := randomString(32)
contents := []byte("a")
suite.writeReadCompare(c, filename, contents, contents)
}
func (suite *DriverSuite) TestWriteRead2(c *C) {
filename := randomString(32)
contents := []byte("\xc3\x9f")
suite.writeReadCompare(c, filename, contents, contents)
}
func (suite *DriverSuite) TestWriteRead3(c *C) {
filename := randomString(32)
contents := []byte(randomString(32))
suite.writeReadCompare(c, filename, contents, contents)
}
func (suite *DriverSuite) TestWriteRead4(c *C) {
filename := randomString(32)
contents := []byte(randomString(1024 * 1024))
suite.writeReadCompare(c, filename, contents, contents)
}
func (suite *DriverSuite) TestReadNonexistent(c *C) {
filename := randomString(32)
_, err := suite.StorageDriver.GetContent(filename)
c.Assert(err, NotNil)
}
func (suite *DriverSuite) TestWriteReadStreams1(c *C) {
filename := randomString(32)
contents := []byte("a")
suite.writeReadCompareStreams(c, filename, contents, contents)
}
func (suite *DriverSuite) TestWriteReadStreams2(c *C) {
filename := randomString(32)
contents := []byte("\xc3\x9f")
suite.writeReadCompareStreams(c, filename, contents, contents)
}
func (suite *DriverSuite) TestWriteReadStreams3(c *C) {
filename := randomString(32)
contents := []byte(randomString(32))
suite.writeReadCompareStreams(c, filename, contents, contents)
}
func (suite *DriverSuite) TestWriteReadStreams4(c *C) {
filename := randomString(32)
contents := []byte(randomString(1024 * 1024))
suite.writeReadCompareStreams(c, filename, contents, contents)
}
func (suite *DriverSuite) TestContinueStreamAppend(c *C) {
filename := randomString(32)
defer suite.StorageDriver.Delete(filename)
chunkSize := uint64(10 * 1024 * 1024)
contentsChunk1 := []byte(randomString(chunkSize))
contentsChunk2 := []byte(randomString(chunkSize))
contentsChunk3 := []byte(randomString(chunkSize))
fullContents := append(append(contentsChunk1, contentsChunk2...), contentsChunk3...)
err := suite.StorageDriver.WriteStream(filename, 0, 3*chunkSize, ioutil.NopCloser(bytes.NewReader(contentsChunk1)))
c.Assert(err, IsNil)
offset, err := suite.StorageDriver.ResumeWritePosition(filename)
c.Assert(err, IsNil)
if offset > chunkSize {
c.Fatalf("Offset too large, %d > %d", offset, chunkSize)
}
err = suite.StorageDriver.WriteStream(filename, offset, 3*chunkSize, ioutil.NopCloser(bytes.NewReader(fullContents[offset:2*chunkSize])))
c.Assert(err, IsNil)
offset, err = suite.StorageDriver.ResumeWritePosition(filename)
c.Assert(err, IsNil)
if offset > 2*chunkSize {
c.Fatalf("Offset too large, %d > %d", offset, 2*chunkSize)
}
err = suite.StorageDriver.WriteStream(filename, offset, 3*chunkSize, ioutil.NopCloser(bytes.NewReader(fullContents[offset:])))
c.Assert(err, IsNil)
received, err := suite.StorageDriver.GetContent(filename)
c.Assert(err, IsNil)
c.Assert(received, DeepEquals, fullContents)
}
func (suite *DriverSuite) TestReadStreamWithOffset(c *C) {
filename := randomString(32)
defer suite.StorageDriver.Delete(filename)
chunkSize := uint64(32)
contentsChunk1 := []byte(randomString(chunkSize))
contentsChunk2 := []byte(randomString(chunkSize))
contentsChunk3 := []byte(randomString(chunkSize))
err := suite.StorageDriver.PutContent(filename, append(append(contentsChunk1, contentsChunk2...), contentsChunk3...))
c.Assert(err, IsNil)
reader, err := suite.StorageDriver.ReadStream(filename, 0)
c.Assert(err, IsNil)
defer reader.Close()
readContents, err := ioutil.ReadAll(reader)
c.Assert(err, IsNil)
c.Assert(readContents, DeepEquals, append(append(contentsChunk1, contentsChunk2...), contentsChunk3...))
reader, err = suite.StorageDriver.ReadStream(filename, chunkSize)
c.Assert(err, IsNil)
defer reader.Close()
readContents, err = ioutil.ReadAll(reader)
c.Assert(err, IsNil)
c.Assert(readContents, DeepEquals, append(contentsChunk2, contentsChunk3...))
reader, err = suite.StorageDriver.ReadStream(filename, chunkSize*2)
c.Assert(err, IsNil)
defer reader.Close()
readContents, err = ioutil.ReadAll(reader)
c.Assert(err, IsNil)
c.Assert(readContents, DeepEquals, contentsChunk3)
}
func (suite *DriverSuite) TestReadNonexistentStream(c *C) {
filename := randomString(32)
_, err := suite.StorageDriver.ReadStream(filename, 0)
c.Assert(err, NotNil)
}
func (suite *DriverSuite) TestList(c *C) {
rootDirectory := randomString(uint64(8 + rand.Intn(8)))
defer suite.StorageDriver.Delete(rootDirectory)
parentDirectory := rootDirectory + "/" + randomString(uint64(8+rand.Intn(8)))
childFiles := make([]string, 50)
for i := 0; i < len(childFiles); i++ {
childFile := parentDirectory + "/" + randomString(uint64(8+rand.Intn(8)))
childFiles[i] = childFile
err := suite.StorageDriver.PutContent(childFile, []byte(randomString(32)))
c.Assert(err, IsNil)
}
sort.Strings(childFiles)
keys, err := suite.StorageDriver.List(rootDirectory)
c.Assert(err, IsNil)
c.Assert(keys, DeepEquals, []string{parentDirectory})
keys, err = suite.StorageDriver.List(parentDirectory)
c.Assert(err, IsNil)
sort.Strings(keys)
c.Assert(keys, DeepEquals, childFiles)
}
func (suite *DriverSuite) TestMove(c *C) {
contents := []byte(randomString(32))
sourcePath := randomString(32)
destPath := randomString(32)
defer suite.StorageDriver.Delete(sourcePath)
defer suite.StorageDriver.Delete(destPath)
err := suite.StorageDriver.PutContent(sourcePath, contents)
c.Assert(err, IsNil)
err = suite.StorageDriver.Move(sourcePath, destPath)
c.Assert(err, IsNil)
received, err := suite.StorageDriver.GetContent(destPath)
c.Assert(err, IsNil)
c.Assert(received, DeepEquals, contents)
_, err = suite.StorageDriver.GetContent(sourcePath)
c.Assert(err, NotNil)
}
func (suite *DriverSuite) TestMoveNonexistent(c *C) {
sourcePath := randomString(32)
destPath := randomString(32)
err := suite.StorageDriver.Move(sourcePath, destPath)
c.Assert(err, NotNil)
}
func (suite *DriverSuite) TestRemove(c *C) {
filename := randomString(32)
contents := []byte(randomString(32))
defer suite.StorageDriver.Delete(filename)
err := suite.StorageDriver.PutContent(filename, contents)
c.Assert(err, IsNil)
err = suite.StorageDriver.Delete(filename)
c.Assert(err, IsNil)
_, err = suite.StorageDriver.GetContent(filename)
c.Assert(err, NotNil)
}
func (suite *DriverSuite) TestRemoveNonexistent(c *C) {
filename := randomString(32)
err := suite.StorageDriver.Delete(filename)
c.Assert(err, NotNil)
}
func (suite *DriverSuite) TestRemoveFolder(c *C) {
dirname := randomString(32)
filename1 := randomString(32)
filename2 := randomString(32)
contents := []byte(randomString(32))
defer suite.StorageDriver.Delete(path.Join(dirname, filename1))
defer suite.StorageDriver.Delete(path.Join(dirname, filename2))
err := suite.StorageDriver.PutContent(path.Join(dirname, filename1), contents)
c.Assert(err, IsNil)
err = suite.StorageDriver.PutContent(path.Join(dirname, filename2), contents)
c.Assert(err, IsNil)
err = suite.StorageDriver.Delete(dirname)
c.Assert(err, IsNil)
_, err = suite.StorageDriver.GetContent(path.Join(dirname, filename1))
c.Assert(err, NotNil)
_, err = suite.StorageDriver.GetContent(path.Join(dirname, filename2))
c.Assert(err, NotNil)
}
func (suite *DriverSuite) writeReadCompare(c *C, filename string, contents, expected []byte) {
defer suite.StorageDriver.Delete(filename)
err := suite.StorageDriver.PutContent(filename, contents)
c.Assert(err, IsNil)
readContents, err := suite.StorageDriver.GetContent(filename)
c.Assert(err, IsNil)
c.Assert(readContents, DeepEquals, contents)
}
func (suite *DriverSuite) writeReadCompareStreams(c *C, filename string, contents, expected []byte) {
defer suite.StorageDriver.Delete(filename)
err := suite.StorageDriver.WriteStream(filename, 0, uint64(len(contents)), ioutil.NopCloser(bytes.NewReader(contents)))
c.Assert(err, IsNil)
reader, err := suite.StorageDriver.ReadStream(filename, 0)
c.Assert(err, IsNil)
defer reader.Close()
readContents, err := ioutil.ReadAll(reader)
c.Assert(err, IsNil)
c.Assert(readContents, DeepEquals, contents)
}
var pathChars = []byte("abcdefghijklmnopqrstuvwxyz")
func randomString(length uint64) string {
b := make([]byte, length)
for i := range b {
b[i] = pathChars[rand.Intn(len(pathChars))]
}
return string(b)
}