Adds storage driver interface, tests, and two basic implementations
							parent
							
								
									12e68998e1
								
							
						
					
					
						commit
						3f95694180
					
				| 
						 | 
					@ -0,0 +1,5 @@
 | 
				
			||||||
 | 
					language: go
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					go:
 | 
				
			||||||
 | 
					- 1.3
 | 
				
			||||||
 | 
					- tip
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,26 @@
 | 
				
			||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/docker-registry/storagedriver/filesystem"
 | 
				
			||||||
 | 
						"github.com/docker/docker-registry/storagedriver/ipc"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						parametersBytes := []byte(os.Args[1])
 | 
				
			||||||
 | 
						var parameters map[string]interface{}
 | 
				
			||||||
 | 
						err := json.Unmarshal(parametersBytes, ¶meters)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						rootDirectory := "/tmp/registry"
 | 
				
			||||||
 | 
						if parameters != nil {
 | 
				
			||||||
 | 
							rootDirParam, ok := parameters["RootDirectory"].(string)
 | 
				
			||||||
 | 
							if ok && rootDirParam != "" {
 | 
				
			||||||
 | 
								rootDirectory = rootDirParam
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ipc.Server(filesystem.NewDriver(rootDirectory))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,10 @@
 | 
				
			||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"github.com/docker/docker-registry/storagedriver/inmemory"
 | 
				
			||||||
 | 
						"github.com/docker/docker-registry/storagedriver/ipc"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						ipc.Server(inmemory.NewDriver())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,173 @@
 | 
				
			||||||
 | 
					package filesystem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"path"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/docker-registry/storagedriver"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type FilesystemDriver struct {
 | 
				
			||||||
 | 
						rootDirectory string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewDriver(rootDirectory string) *FilesystemDriver {
 | 
				
			||||||
 | 
						return &FilesystemDriver{rootDirectory}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *FilesystemDriver) subPath(subPath string) string {
 | 
				
			||||||
 | 
						return path.Join(d.rootDirectory, subPath)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *FilesystemDriver) GetContent(path string) ([]byte, error) {
 | 
				
			||||||
 | 
						contents, err := ioutil.ReadFile(d.subPath(path))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, storagedriver.PathNotFoundError{path}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return contents, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *FilesystemDriver) PutContent(subPath string, contents []byte) error {
 | 
				
			||||||
 | 
						fullPath := d.subPath(subPath)
 | 
				
			||||||
 | 
						parentDir := path.Dir(fullPath)
 | 
				
			||||||
 | 
						err := os.MkdirAll(parentDir, 0755)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = ioutil.WriteFile(fullPath, contents, 0644)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *FilesystemDriver) ReadStream(path string, offset uint64) (io.ReadCloser, error) {
 | 
				
			||||||
 | 
						file, err := os.OpenFile(d.subPath(path), os.O_RDONLY, 0644)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						seekPos, err := file.Seek(int64(offset), os.SEEK_SET)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							file.Close()
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						} else if seekPos < int64(offset) {
 | 
				
			||||||
 | 
							file.Close()
 | 
				
			||||||
 | 
							return nil, storagedriver.InvalidOffsetError{path, offset}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return file, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *FilesystemDriver) WriteStream(subPath string, offset, size uint64, reader io.ReadCloser) error {
 | 
				
			||||||
 | 
						defer reader.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resumableOffset, err := d.ResumeWritePosition(subPath)
 | 
				
			||||||
 | 
						if _, pathNotFound := err.(storagedriver.PathNotFoundError); err != nil && !pathNotFound {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if offset > resumableOffset {
 | 
				
			||||||
 | 
							return storagedriver.InvalidOffsetError{subPath, offset}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fullPath := d.subPath(subPath)
 | 
				
			||||||
 | 
						parentDir := path.Dir(fullPath)
 | 
				
			||||||
 | 
						err = os.MkdirAll(parentDir, 0755)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var file *os.File
 | 
				
			||||||
 | 
						if offset == 0 {
 | 
				
			||||||
 | 
							file, err = os.Create(fullPath)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							file, err = os.OpenFile(fullPath, os.O_WRONLY|os.O_APPEND, 0)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer file.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						buf := make([]byte, 32*1024)
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							bytesRead, er := reader.Read(buf)
 | 
				
			||||||
 | 
							if bytesRead > 0 {
 | 
				
			||||||
 | 
								bytesWritten, ew := file.WriteAt(buf[0:bytesRead], int64(offset))
 | 
				
			||||||
 | 
								if bytesWritten > 0 {
 | 
				
			||||||
 | 
									offset += uint64(bytesWritten)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if ew != nil {
 | 
				
			||||||
 | 
									err = ew
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								if bytesRead != bytesWritten {
 | 
				
			||||||
 | 
									err = io.ErrShortWrite
 | 
				
			||||||
 | 
									break
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if er == io.EOF {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if er != nil {
 | 
				
			||||||
 | 
								err = er
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *FilesystemDriver) ResumeWritePosition(subPath string) (uint64, error) {
 | 
				
			||||||
 | 
						fullPath := d.subPath(subPath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fileInfo, err := os.Stat(fullPath)
 | 
				
			||||||
 | 
						if err != nil && !os.IsNotExist(err) {
 | 
				
			||||||
 | 
							return 0, err
 | 
				
			||||||
 | 
						} else if err != nil {
 | 
				
			||||||
 | 
							return 0, storagedriver.PathNotFoundError{subPath}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return uint64(fileInfo.Size()), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *FilesystemDriver) List(prefix string) ([]string, error) {
 | 
				
			||||||
 | 
						prefix = strings.TrimRight(prefix, "/")
 | 
				
			||||||
 | 
						fullPath := d.subPath(prefix)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dir, err := os.Open(fullPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fileNames, err := dir.Readdirnames(0)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						keys := make([]string, 0, len(fileNames))
 | 
				
			||||||
 | 
						for _, fileName := range fileNames {
 | 
				
			||||||
 | 
							keys = append(keys, path.Join(prefix, fileName))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return keys, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *FilesystemDriver) Move(sourcePath string, destPath string) error {
 | 
				
			||||||
 | 
						err := os.Rename(d.subPath(sourcePath), d.subPath(destPath))
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *FilesystemDriver) Delete(subPath string) error {
 | 
				
			||||||
 | 
						fullPath := d.subPath(subPath)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err := os.Stat(fullPath)
 | 
				
			||||||
 | 
						if err != nil && !os.IsNotExist(err) {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						} else if err != nil {
 | 
				
			||||||
 | 
							return storagedriver.PathNotFoundError{subPath}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = os.RemoveAll(fullPath)
 | 
				
			||||||
 | 
						return err
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,24 @@
 | 
				
			||||||
 | 
					package filesystem
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/docker-registry/storagedriver"
 | 
				
			||||||
 | 
						"github.com/docker/docker-registry/storagedriver/testsuites"
 | 
				
			||||||
 | 
						. "gopkg.in/check.v1"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Hook up gocheck into the "go test" runner.
 | 
				
			||||||
 | 
					func Test(t *testing.T) { TestingT(t) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						rootDirectory := "/tmp/driver"
 | 
				
			||||||
 | 
						os.RemoveAll(rootDirectory)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						filesystemDriverConstructor := func() (storagedriver.StorageDriver, error) {
 | 
				
			||||||
 | 
							return NewDriver(rootDirectory), nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						testsuites.RegisterInProcessSuite(filesystemDriverConstructor)
 | 
				
			||||||
 | 
						testsuites.RegisterIPCSuite("filesystem", map[string]string{"RootDirectory": rootDirectory})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,147 @@
 | 
				
			||||||
 | 
					package inmemory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"bytes"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"io/ioutil"
 | 
				
			||||||
 | 
						"regexp"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/docker-registry/storagedriver"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InMemoryDriver struct {
 | 
				
			||||||
 | 
						storage map[string][]byte
 | 
				
			||||||
 | 
						mutex   sync.RWMutex
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewDriver() *InMemoryDriver {
 | 
				
			||||||
 | 
						return &InMemoryDriver{storage: make(map[string][]byte)}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *InMemoryDriver) GetContent(path string) ([]byte, error) {
 | 
				
			||||||
 | 
						d.mutex.RLock()
 | 
				
			||||||
 | 
						defer d.mutex.RUnlock()
 | 
				
			||||||
 | 
						contents, ok := d.storage[path]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return nil, storagedriver.PathNotFoundError{path}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return contents, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *InMemoryDriver) PutContent(path string, contents []byte) error {
 | 
				
			||||||
 | 
						d.mutex.Lock()
 | 
				
			||||||
 | 
						defer d.mutex.Unlock()
 | 
				
			||||||
 | 
						d.storage[path] = contents
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *InMemoryDriver) ReadStream(path string, offset uint64) (io.ReadCloser, error) {
 | 
				
			||||||
 | 
						d.mutex.RLock()
 | 
				
			||||||
 | 
						defer d.mutex.RUnlock()
 | 
				
			||||||
 | 
						contents, err := d.GetContent(path)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						} else if len(contents) < int(offset) {
 | 
				
			||||||
 | 
							return nil, storagedriver.InvalidOffsetError{path, offset}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						src := contents[offset:]
 | 
				
			||||||
 | 
						buf := make([]byte, len(src))
 | 
				
			||||||
 | 
						copy(buf, src)
 | 
				
			||||||
 | 
						return ioutil.NopCloser(bytes.NewReader(buf)), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *InMemoryDriver) WriteStream(path string, offset, size uint64, reader io.ReadCloser) error {
 | 
				
			||||||
 | 
						defer reader.Close()
 | 
				
			||||||
 | 
						d.mutex.RLock()
 | 
				
			||||||
 | 
						defer d.mutex.RUnlock()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						resumableOffset, err := d.ResumeWritePosition(path)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if offset > resumableOffset {
 | 
				
			||||||
 | 
							return storagedriver.InvalidOffsetError{path, offset}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						contents, err := ioutil.ReadAll(reader)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if offset > 0 {
 | 
				
			||||||
 | 
							contents = append(d.storage[path][0:offset], contents...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						d.storage[path] = contents
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *InMemoryDriver) ResumeWritePosition(path string) (uint64, error) {
 | 
				
			||||||
 | 
						d.mutex.RLock()
 | 
				
			||||||
 | 
						defer d.mutex.RUnlock()
 | 
				
			||||||
 | 
						contents, ok := d.storage[path]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return 0, nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return uint64(len(contents)), nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *InMemoryDriver) List(prefix string) ([]string, error) {
 | 
				
			||||||
 | 
						subPathMatcher, err := regexp.Compile(fmt.Sprintf("^%s/[^/]+", prefix))
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						d.mutex.RLock()
 | 
				
			||||||
 | 
						defer d.mutex.RUnlock()
 | 
				
			||||||
 | 
						// we use map to collect uniq keys
 | 
				
			||||||
 | 
						keySet := make(map[string]struct{})
 | 
				
			||||||
 | 
						for k := range d.storage {
 | 
				
			||||||
 | 
							if key := subPathMatcher.FindString(k); key != "" {
 | 
				
			||||||
 | 
								keySet[key] = struct{}{}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						keys := make([]string, 0, len(keySet))
 | 
				
			||||||
 | 
						for k := range keySet {
 | 
				
			||||||
 | 
							keys = append(keys, k)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return keys, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *InMemoryDriver) Move(sourcePath string, destPath string) error {
 | 
				
			||||||
 | 
						d.mutex.Lock()
 | 
				
			||||||
 | 
						defer d.mutex.Unlock()
 | 
				
			||||||
 | 
						contents, ok := d.storage[sourcePath]
 | 
				
			||||||
 | 
						if !ok {
 | 
				
			||||||
 | 
							return storagedriver.PathNotFoundError{sourcePath}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						d.storage[destPath] = contents
 | 
				
			||||||
 | 
						delete(d.storage, sourcePath)
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (d *InMemoryDriver) Delete(path string) error {
 | 
				
			||||||
 | 
						d.mutex.Lock()
 | 
				
			||||||
 | 
						defer d.mutex.Unlock()
 | 
				
			||||||
 | 
						subPaths := make([]string, 0)
 | 
				
			||||||
 | 
						for k := range d.storage {
 | 
				
			||||||
 | 
							if strings.HasPrefix(k, path) {
 | 
				
			||||||
 | 
								subPaths = append(subPaths, k)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if len(subPaths) == 0 {
 | 
				
			||||||
 | 
							return storagedriver.PathNotFoundError{path}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for _, subPath := range subPaths {
 | 
				
			||||||
 | 
							delete(d.storage, subPath)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,20 @@
 | 
				
			||||||
 | 
					package inmemory
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"testing"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/docker-registry/storagedriver"
 | 
				
			||||||
 | 
						"github.com/docker/docker-registry/storagedriver/testsuites"
 | 
				
			||||||
 | 
						. "gopkg.in/check.v1"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Hook up gocheck into the "go test" runner.
 | 
				
			||||||
 | 
					func Test(t *testing.T) { TestingT(t) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						inmemoryDriverConstructor := func() (storagedriver.StorageDriver, error) {
 | 
				
			||||||
 | 
							return NewDriver(), nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						testsuites.RegisterInProcessSuite(inmemoryDriverConstructor)
 | 
				
			||||||
 | 
						testsuites.RegisterIPCSuite("inmemory", nil)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,285 @@
 | 
				
			||||||
 | 
					package ipc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"encoding/json"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"path"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/libchan"
 | 
				
			||||||
 | 
						"github.com/docker/libchan/spdy"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type StorageDriverClient struct {
 | 
				
			||||||
 | 
						subprocess *exec.Cmd
 | 
				
			||||||
 | 
						socket     *os.File
 | 
				
			||||||
 | 
						transport  *spdy.Transport
 | 
				
			||||||
 | 
						sender     libchan.Sender
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func NewDriverClient(name string, parameters map[string]string) (*StorageDriverClient, error) {
 | 
				
			||||||
 | 
						paramsBytes, err := json.Marshal(parameters)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						driverPath := os.ExpandEnv(path.Join("$GOPATH", "bin", name))
 | 
				
			||||||
 | 
						if _, err := os.Stat(driverPath); os.IsNotExist(err) {
 | 
				
			||||||
 | 
							driverPath = path.Join(path.Dir(os.Args[0]), name)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if _, err := os.Stat(driverPath); os.IsNotExist(err) {
 | 
				
			||||||
 | 
							driverPath, err = exec.LookPath(name)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						command := exec.Command(driverPath, string(paramsBytes))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return &StorageDriverClient{
 | 
				
			||||||
 | 
							subprocess: command,
 | 
				
			||||||
 | 
						}, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (driver *StorageDriverClient) Start() error {
 | 
				
			||||||
 | 
						fileDescriptors, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						childSocket := os.NewFile(uintptr(fileDescriptors[0]), "childSocket")
 | 
				
			||||||
 | 
						parentSocket := os.NewFile(uintptr(fileDescriptors[1]), "parentSocket")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						driver.subprocess.Stdout = os.Stdout
 | 
				
			||||||
 | 
						driver.subprocess.Stderr = os.Stderr
 | 
				
			||||||
 | 
						driver.subprocess.ExtraFiles = []*os.File{childSocket}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err = driver.subprocess.Start(); err != nil {
 | 
				
			||||||
 | 
							parentSocket.Close()
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if err = childSocket.Close(); err != nil {
 | 
				
			||||||
 | 
							parentSocket.Close()
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						connection, err := net.FileConn(parentSocket)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							parentSocket.Close()
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						transport, err := spdy.NewClientTransport(connection)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							parentSocket.Close()
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						sender, err := transport.NewSendChannel()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							transport.Close()
 | 
				
			||||||
 | 
							parentSocket.Close()
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						driver.socket = parentSocket
 | 
				
			||||||
 | 
						driver.transport = transport
 | 
				
			||||||
 | 
						driver.sender = sender
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (driver *StorageDriverClient) Stop() error {
 | 
				
			||||||
 | 
						closeSenderErr := driver.sender.Close()
 | 
				
			||||||
 | 
						closeTransportErr := driver.transport.Close()
 | 
				
			||||||
 | 
						closeSocketErr := driver.socket.Close()
 | 
				
			||||||
 | 
						killErr := driver.subprocess.Process.Kill()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if closeSenderErr != nil {
 | 
				
			||||||
 | 
							return closeSenderErr
 | 
				
			||||||
 | 
						} else if closeTransportErr != nil {
 | 
				
			||||||
 | 
							return closeTransportErr
 | 
				
			||||||
 | 
						} else if closeSocketErr != nil {
 | 
				
			||||||
 | 
							return closeSocketErr
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return killErr
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (driver *StorageDriverClient) GetContent(path string) ([]byte, error) {
 | 
				
			||||||
 | 
						receiver, remoteSender := libchan.Pipe()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params := map[string]interface{}{"Path": path}
 | 
				
			||||||
 | 
						err := driver.sender.Send(&Request{Type: "GetContent", Parameters: params, ResponseChannel: remoteSender})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var response GetContentResponse
 | 
				
			||||||
 | 
						err = receiver.Receive(&response)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if response.Error != nil {
 | 
				
			||||||
 | 
							return nil, response.Error
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return response.Content, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (driver *StorageDriverClient) PutContent(path string, contents []byte) error {
 | 
				
			||||||
 | 
						receiver, remoteSender := libchan.Pipe()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params := map[string]interface{}{"Path": path, "Contents": contents}
 | 
				
			||||||
 | 
						err := driver.sender.Send(&Request{Type: "PutContent", Parameters: params, ResponseChannel: remoteSender})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var response PutContentResponse
 | 
				
			||||||
 | 
						err = receiver.Receive(&response)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if response.Error != nil {
 | 
				
			||||||
 | 
							return response.Error
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (driver *StorageDriverClient) ReadStream(path string, offset uint64) (io.ReadCloser, error) {
 | 
				
			||||||
 | 
						receiver, remoteSender := libchan.Pipe()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params := map[string]interface{}{"Path": path, "Offset": offset}
 | 
				
			||||||
 | 
						err := driver.sender.Send(&Request{Type: "ReadStream", Parameters: params, ResponseChannel: remoteSender})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var response ReadStreamResponse
 | 
				
			||||||
 | 
						err = receiver.Receive(&response)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if response.Error != nil {
 | 
				
			||||||
 | 
							return nil, response.Error
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return response.Reader, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (driver *StorageDriverClient) WriteStream(path string, offset, size uint64, reader io.ReadCloser) error {
 | 
				
			||||||
 | 
						receiver, remoteSender := libchan.Pipe()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params := map[string]interface{}{"Path": path, "Offset": offset, "Size": size, "Reader": WrapReadCloser(reader)}
 | 
				
			||||||
 | 
						err := driver.sender.Send(&Request{Type: "WriteStream", Parameters: params, ResponseChannel: remoteSender})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var response WriteStreamResponse
 | 
				
			||||||
 | 
						err = receiver.Receive(&response)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if response.Error != nil {
 | 
				
			||||||
 | 
							return response.Error
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (driver *StorageDriverClient) ResumeWritePosition(path string) (uint64, error) {
 | 
				
			||||||
 | 
						receiver, remoteSender := libchan.Pipe()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params := map[string]interface{}{"Path": path}
 | 
				
			||||||
 | 
						err := driver.sender.Send(&Request{Type: "ResumeWritePosition", Parameters: params, ResponseChannel: remoteSender})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var response ResumeWritePositionResponse
 | 
				
			||||||
 | 
						err = receiver.Receive(&response)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if response.Error != nil {
 | 
				
			||||||
 | 
							return 0, response.Error
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return response.Position, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (driver *StorageDriverClient) List(prefix string) ([]string, error) {
 | 
				
			||||||
 | 
						receiver, remoteSender := libchan.Pipe()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params := map[string]interface{}{"Prefix": prefix}
 | 
				
			||||||
 | 
						err := driver.sender.Send(&Request{Type: "List", Parameters: params, ResponseChannel: remoteSender})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var response ListResponse
 | 
				
			||||||
 | 
						err = receiver.Receive(&response)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if response.Error != nil {
 | 
				
			||||||
 | 
							return nil, response.Error
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return response.Keys, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (driver *StorageDriverClient) Move(sourcePath string, destPath string) error {
 | 
				
			||||||
 | 
						receiver, remoteSender := libchan.Pipe()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params := map[string]interface{}{"SourcePath": sourcePath, "DestPath": destPath}
 | 
				
			||||||
 | 
						err := driver.sender.Send(&Request{Type: "Move", Parameters: params, ResponseChannel: remoteSender})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var response MoveResponse
 | 
				
			||||||
 | 
						err = receiver.Receive(&response)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if response.Error != nil {
 | 
				
			||||||
 | 
							return response.Error
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (driver *StorageDriverClient) Delete(path string) error {
 | 
				
			||||||
 | 
						receiver, remoteSender := libchan.Pipe()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						params := map[string]interface{}{"Path": path}
 | 
				
			||||||
 | 
						err := driver.sender.Send(&Request{Type: "Delete", Parameters: params, ResponseChannel: remoteSender})
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var response DeleteResponse
 | 
				
			||||||
 | 
						err = receiver.Receive(&response)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if response.Error != nil {
 | 
				
			||||||
 | 
							return response.Error
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,83 @@
 | 
				
			||||||
 | 
					package ipc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/libchan"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type Request struct {
 | 
				
			||||||
 | 
						Type            string
 | 
				
			||||||
 | 
						Parameters      map[string]interface{}
 | 
				
			||||||
 | 
						ResponseChannel libchan.Sender
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type noWriteReadWriteCloser struct {
 | 
				
			||||||
 | 
						io.ReadCloser
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (r noWriteReadWriteCloser) Write(p []byte) (n int, err error) {
 | 
				
			||||||
 | 
						return 0, errors.New("Write unsupported")
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func WrapReadCloser(readCloser io.ReadCloser) io.ReadWriteCloser {
 | 
				
			||||||
 | 
						return noWriteReadWriteCloser{readCloser}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type responseError struct {
 | 
				
			||||||
 | 
						Type    string
 | 
				
			||||||
 | 
						Message string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func ResponseError(err error) *responseError {
 | 
				
			||||||
 | 
						if err == nil {
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return &responseError{
 | 
				
			||||||
 | 
							Type:    reflect.TypeOf(err).String(),
 | 
				
			||||||
 | 
							Message: err.Error(),
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (err *responseError) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("%s: %s", err.Type, err.Message)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type GetContentResponse struct {
 | 
				
			||||||
 | 
						Content []byte
 | 
				
			||||||
 | 
						Error   *responseError
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type PutContentResponse struct {
 | 
				
			||||||
 | 
						Error *responseError
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ReadStreamResponse struct {
 | 
				
			||||||
 | 
						Reader io.ReadWriteCloser
 | 
				
			||||||
 | 
						Error  *responseError
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type WriteStreamResponse struct {
 | 
				
			||||||
 | 
						Error *responseError
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ResumeWritePositionResponse struct {
 | 
				
			||||||
 | 
						Position uint64
 | 
				
			||||||
 | 
						Error    *responseError
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type ListResponse struct {
 | 
				
			||||||
 | 
						Keys  []string
 | 
				
			||||||
 | 
						Error *responseError
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type MoveResponse struct {
 | 
				
			||||||
 | 
						Error *responseError
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DeleteResponse struct {
 | 
				
			||||||
 | 
						Error *responseError
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,160 @@
 | 
				
			||||||
 | 
					package ipc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
						"net"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/docker/docker-registry/storagedriver"
 | 
				
			||||||
 | 
						"github.com/docker/libchan"
 | 
				
			||||||
 | 
						"github.com/docker/libchan/spdy"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func Server(driver storagedriver.StorageDriver) error {
 | 
				
			||||||
 | 
						childSocket := os.NewFile(3, "childSocket")
 | 
				
			||||||
 | 
						defer childSocket.Close()
 | 
				
			||||||
 | 
						conn, err := net.FileConn(childSocket)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						defer conn.Close()
 | 
				
			||||||
 | 
						if transport, err := spdy.NewServerTransport(conn); err != nil {
 | 
				
			||||||
 | 
							panic(err)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							for {
 | 
				
			||||||
 | 
								receiver, err := transport.WaitReceiveChannel()
 | 
				
			||||||
 | 
								if err != nil {
 | 
				
			||||||
 | 
									panic(err)
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								go receive(driver, receiver)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							return nil
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func receive(driver storagedriver.StorageDriver, receiver libchan.Receiver) {
 | 
				
			||||||
 | 
						for {
 | 
				
			||||||
 | 
							var request Request
 | 
				
			||||||
 | 
							err := receiver.Receive(&request)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							go handleRequest(driver, request)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func handleRequest(driver storagedriver.StorageDriver, request Request) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch request.Type {
 | 
				
			||||||
 | 
						case "GetContent":
 | 
				
			||||||
 | 
							path, _ := request.Parameters["Path"].(string)
 | 
				
			||||||
 | 
							content, err := driver.GetContent(path)
 | 
				
			||||||
 | 
							response := GetContentResponse{
 | 
				
			||||||
 | 
								Content: content,
 | 
				
			||||||
 | 
								Error:   ResponseError(err),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err = request.ResponseChannel.Send(&response)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case "PutContent":
 | 
				
			||||||
 | 
							path, _ := request.Parameters["Path"].(string)
 | 
				
			||||||
 | 
							contents, _ := request.Parameters["Contents"].([]byte)
 | 
				
			||||||
 | 
							err := driver.PutContent(path, contents)
 | 
				
			||||||
 | 
							response := PutContentResponse{
 | 
				
			||||||
 | 
								Error: ResponseError(err),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err = request.ResponseChannel.Send(&response)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case "ReadStream":
 | 
				
			||||||
 | 
							var offset uint64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							path, _ := request.Parameters["Path"].(string)
 | 
				
			||||||
 | 
							offset, ok := request.Parameters["Offset"].(uint64)
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								offsetSigned, _ := request.Parameters["Offset"].(int64)
 | 
				
			||||||
 | 
								offset = uint64(offsetSigned)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							reader, err := driver.ReadStream(path, offset)
 | 
				
			||||||
 | 
							var response ReadStreamResponse
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								response = ReadStreamResponse{Error: ResponseError(err)}
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								response = ReadStreamResponse{Reader: WrapReadCloser(reader)}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err = request.ResponseChannel.Send(&response)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case "WriteStream":
 | 
				
			||||||
 | 
							var offset uint64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							path, _ := request.Parameters["Path"].(string)
 | 
				
			||||||
 | 
							offset, ok := request.Parameters["Offset"].(uint64)
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								offsetSigned, _ := request.Parameters["Offset"].(int64)
 | 
				
			||||||
 | 
								offset = uint64(offsetSigned)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							size, ok := request.Parameters["Size"].(uint64)
 | 
				
			||||||
 | 
							if !ok {
 | 
				
			||||||
 | 
								sizeSigned, _ := request.Parameters["Size"].(int64)
 | 
				
			||||||
 | 
								size = uint64(sizeSigned)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							reader, _ := request.Parameters["Reader"].(io.ReadCloser)
 | 
				
			||||||
 | 
							err := driver.WriteStream(path, offset, size, reader)
 | 
				
			||||||
 | 
							response := WriteStreamResponse{
 | 
				
			||||||
 | 
								Error: ResponseError(err),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err = request.ResponseChannel.Send(&response)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case "ResumeWritePosition":
 | 
				
			||||||
 | 
							path, _ := request.Parameters["Path"].(string)
 | 
				
			||||||
 | 
							position, err := driver.ResumeWritePosition(path)
 | 
				
			||||||
 | 
							response := ResumeWritePositionResponse{
 | 
				
			||||||
 | 
								Position: position,
 | 
				
			||||||
 | 
								Error:    ResponseError(err),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err = request.ResponseChannel.Send(&response)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case "List":
 | 
				
			||||||
 | 
							prefix, _ := request.Parameters["Prefix"].(string)
 | 
				
			||||||
 | 
							keys, err := driver.List(prefix)
 | 
				
			||||||
 | 
							response := ListResponse{
 | 
				
			||||||
 | 
								Keys:  keys,
 | 
				
			||||||
 | 
								Error: ResponseError(err),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err = request.ResponseChannel.Send(&response)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case "Move":
 | 
				
			||||||
 | 
							sourcePath, _ := request.Parameters["SourcePath"].(string)
 | 
				
			||||||
 | 
							destPath, _ := request.Parameters["DestPath"].(string)
 | 
				
			||||||
 | 
							err := driver.Move(sourcePath, destPath)
 | 
				
			||||||
 | 
							response := MoveResponse{
 | 
				
			||||||
 | 
								Error: ResponseError(err),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err = request.ResponseChannel.Send(&response)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						case "Delete":
 | 
				
			||||||
 | 
							path, _ := request.Parameters["Path"].(string)
 | 
				
			||||||
 | 
							err := driver.Delete(path)
 | 
				
			||||||
 | 
							response := DeleteResponse{
 | 
				
			||||||
 | 
								Error: ResponseError(err),
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							err = request.ResponseChannel.Send(&response)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								panic(err)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							panic(request)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,34 @@
 | 
				
			||||||
 | 
					package storagedriver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type StorageDriver interface {
 | 
				
			||||||
 | 
						GetContent(path string) ([]byte, error)
 | 
				
			||||||
 | 
						PutContent(path string, content []byte) error
 | 
				
			||||||
 | 
						ReadStream(path string, offset uint64) (io.ReadCloser, error)
 | 
				
			||||||
 | 
						WriteStream(path string, offset, size uint64, readCloser io.ReadCloser) error
 | 
				
			||||||
 | 
						ResumeWritePosition(path string) (uint64, error)
 | 
				
			||||||
 | 
						List(prefix string) ([]string, error)
 | 
				
			||||||
 | 
						Move(sourcePath string, destPath string) error
 | 
				
			||||||
 | 
						Delete(path string) error
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type PathNotFoundError struct {
 | 
				
			||||||
 | 
						Path string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (err PathNotFoundError) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("Path not found: %s", err.Path)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type InvalidOffsetError struct {
 | 
				
			||||||
 | 
						Path   string
 | 
				
			||||||
 | 
						Offset uint64
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (err InvalidOffsetError) Error() string {
 | 
				
			||||||
 | 
						return fmt.Sprintf("Invalid offset: %d for path: %s", err.Offset, err.Path)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,353 @@
 | 
				
			||||||
 | 
					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) }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func RegisterInProcessSuite(driverConstructor DriverConstructor) {
 | 
				
			||||||
 | 
						Suite(&DriverSuite{
 | 
				
			||||||
 | 
							Constructor: driverConstructor,
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func RegisterIPCSuite(driverName string, ipcParams map[string]string) {
 | 
				
			||||||
 | 
						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
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						suite.Teardown = func() error {
 | 
				
			||||||
 | 
							driverClient := suite.StorageDriver.(*ipc.StorageDriverClient)
 | 
				
			||||||
 | 
							return driverClient.Stop()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						Suite(suite)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DriverConstructor func() (storagedriver.StorageDriver, error)
 | 
				
			||||||
 | 
					type DriverTeardown func() error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type DriverSuite struct {
 | 
				
			||||||
 | 
						Constructor DriverConstructor
 | 
				
			||||||
 | 
						Teardown    DriverTeardown
 | 
				
			||||||
 | 
						storagedriver.StorageDriver
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type TestDriverConfig struct {
 | 
				
			||||||
 | 
						name   string
 | 
				
			||||||
 | 
						params map[string]string
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (suite *DriverSuite) SetUpSuite(c *C) {
 | 
				
			||||||
 | 
						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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						chunkSize := uint64(32)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						offset, err = suite.StorageDriver.ResumeWritePosition(filename)
 | 
				
			||||||
 | 
						c.Assert(err, IsNil)
 | 
				
			||||||
 | 
						c.Assert(offset, Equals, uint64(3*chunkSize))
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (suite *DriverSuite) TestReadStreamWithOffset(c *C) {
 | 
				
			||||||
 | 
						filename := randomString(32)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reader, err = suite.StorageDriver.ReadStream(filename, chunkSize*3)
 | 
				
			||||||
 | 
						c.Assert(err, IsNil)
 | 
				
			||||||
 | 
						defer reader.Close()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						readContents, err = ioutil.ReadAll(reader)
 | 
				
			||||||
 | 
						c.Assert(err, IsNil)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						c.Assert(readContents, DeepEquals, []byte{})
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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)))
 | 
				
			||||||
 | 
						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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						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) {
 | 
				
			||||||
 | 
						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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = suite.StorageDriver.Delete(filename)
 | 
				
			||||||
 | 
						c.Assert(err, IsNil)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (suite *DriverSuite) writeReadCompareStreams(c *C, filename string, contents, expected []byte) {
 | 
				
			||||||
 | 
						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)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = suite.StorageDriver.Delete(filename)
 | 
				
			||||||
 | 
						c.Assert(err, IsNil)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Loading…
	
		Reference in New Issue