distribution/storagedriver/inmemory/inmemory.go

167 lines
3.7 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{})
}
// inMemoryDriverFacotry implements the factory.StorageDriverFactory interface
type inMemoryDriverFactory struct{}
func (factory *inMemoryDriverFactory) Create(parameters map[string]string) (storagedriver.StorageDriver, error) {
return New(), nil
}
// InMemoryDriver is a storagedriver.StorageDriver implementation backed by a local map
// Intended solely for example and testing purposes
type InMemoryDriver struct {
storage map[string][]byte
mutex sync.RWMutex
}
// New constructs a new InMemoryDriver
func New() *InMemoryDriver {
return &InMemoryDriver{storage: make(map[string][]byte)}
}
// Implement the storagedriver.StorageDriver interface
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
}