ca0084fad1
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
164 lines
3.5 KiB
Go
164 lines
3.5 KiB
Go
package inmemory
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"regexp"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/docker/docker-registry/storagedriver"
|
|
"github.com/docker/docker-registry/storagedriver/factory"
|
|
)
|
|
|
|
const DriverName = "inmemory"
|
|
|
|
func init() {
|
|
factory.Register(DriverName, &inMemoryDriverFactory{})
|
|
}
|
|
|
|
// Implements the factory.StorageDriverFactory interface
|
|
type inMemoryDriverFactory struct{}
|
|
|
|
func (factory *inMemoryDriverFactory) Create(parameters map[string]string) (storagedriver.StorageDriver, error) {
|
|
return New(), nil
|
|
}
|
|
|
|
// InMemory Storage Driver backed by a map
|
|
// Intended solely for example and testing purposes
|
|
type InMemoryDriver struct {
|
|
storage map[string][]byte
|
|
mutex sync.RWMutex
|
|
}
|
|
|
|
// Constructs a new InMemoryDriver
|
|
func New() *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
|
|
}
|