Skip to content
This repository was archived by the owner on Sep 12, 2018. It is now read-only.

Commit 3f95694

Browse files
committed
Adds storage driver interface, tests, and two basic implementations
1 parent 12e6899 commit 3f95694

File tree

12 files changed

+1320
-0
lines changed

12 files changed

+1320
-0
lines changed

.travis.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
language: go
2+
3+
go:
4+
- 1.3
5+
- tip
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package main
2+
3+
import (
4+
"encoding/json"
5+
"os"
6+
7+
"github.com/docker/docker-registry/storagedriver/filesystem"
8+
"github.com/docker/docker-registry/storagedriver/ipc"
9+
)
10+
11+
func main() {
12+
parametersBytes := []byte(os.Args[1])
13+
var parameters map[string]interface{}
14+
err := json.Unmarshal(parametersBytes, &parameters)
15+
if err != nil {
16+
panic(err)
17+
}
18+
rootDirectory := "/tmp/registry"
19+
if parameters != nil {
20+
rootDirParam, ok := parameters["RootDirectory"].(string)
21+
if ok && rootDirParam != "" {
22+
rootDirectory = rootDirParam
23+
}
24+
}
25+
ipc.Server(filesystem.NewDriver(rootDirectory))
26+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package main
2+
3+
import (
4+
"github.com/docker/docker-registry/storagedriver/inmemory"
5+
"github.com/docker/docker-registry/storagedriver/ipc"
6+
)
7+
8+
func main() {
9+
ipc.Server(inmemory.NewDriver())
10+
}
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
package filesystem
2+
3+
import (
4+
"io"
5+
"io/ioutil"
6+
"os"
7+
"path"
8+
"strings"
9+
10+
"github.com/docker/docker-registry/storagedriver"
11+
)
12+
13+
type FilesystemDriver struct {
14+
rootDirectory string
15+
}
16+
17+
func NewDriver(rootDirectory string) *FilesystemDriver {
18+
return &FilesystemDriver{rootDirectory}
19+
}
20+
21+
func (d *FilesystemDriver) subPath(subPath string) string {
22+
return path.Join(d.rootDirectory, subPath)
23+
}
24+
25+
func (d *FilesystemDriver) GetContent(path string) ([]byte, error) {
26+
contents, err := ioutil.ReadFile(d.subPath(path))
27+
if err != nil {
28+
return nil, storagedriver.PathNotFoundError{path}
29+
}
30+
return contents, nil
31+
}
32+
33+
func (d *FilesystemDriver) PutContent(subPath string, contents []byte) error {
34+
fullPath := d.subPath(subPath)
35+
parentDir := path.Dir(fullPath)
36+
err := os.MkdirAll(parentDir, 0755)
37+
if err != nil {
38+
return err
39+
}
40+
41+
err = ioutil.WriteFile(fullPath, contents, 0644)
42+
return err
43+
}
44+
45+
func (d *FilesystemDriver) ReadStream(path string, offset uint64) (io.ReadCloser, error) {
46+
file, err := os.OpenFile(d.subPath(path), os.O_RDONLY, 0644)
47+
if err != nil {
48+
return nil, err
49+
}
50+
51+
seekPos, err := file.Seek(int64(offset), os.SEEK_SET)
52+
if err != nil {
53+
file.Close()
54+
return nil, err
55+
} else if seekPos < int64(offset) {
56+
file.Close()
57+
return nil, storagedriver.InvalidOffsetError{path, offset}
58+
}
59+
60+
return file, nil
61+
}
62+
63+
func (d *FilesystemDriver) WriteStream(subPath string, offset, size uint64, reader io.ReadCloser) error {
64+
defer reader.Close()
65+
66+
resumableOffset, err := d.ResumeWritePosition(subPath)
67+
if _, pathNotFound := err.(storagedriver.PathNotFoundError); err != nil && !pathNotFound {
68+
return err
69+
}
70+
71+
if offset > resumableOffset {
72+
return storagedriver.InvalidOffsetError{subPath, offset}
73+
}
74+
75+
fullPath := d.subPath(subPath)
76+
parentDir := path.Dir(fullPath)
77+
err = os.MkdirAll(parentDir, 0755)
78+
if err != nil {
79+
return err
80+
}
81+
82+
var file *os.File
83+
if offset == 0 {
84+
file, err = os.Create(fullPath)
85+
} else {
86+
file, err = os.OpenFile(fullPath, os.O_WRONLY|os.O_APPEND, 0)
87+
}
88+
89+
if err != nil {
90+
return err
91+
}
92+
defer file.Close()
93+
94+
buf := make([]byte, 32*1024)
95+
for {
96+
bytesRead, er := reader.Read(buf)
97+
if bytesRead > 0 {
98+
bytesWritten, ew := file.WriteAt(buf[0:bytesRead], int64(offset))
99+
if bytesWritten > 0 {
100+
offset += uint64(bytesWritten)
101+
}
102+
if ew != nil {
103+
err = ew
104+
break
105+
}
106+
if bytesRead != bytesWritten {
107+
err = io.ErrShortWrite
108+
break
109+
}
110+
}
111+
if er == io.EOF {
112+
break
113+
}
114+
if er != nil {
115+
err = er
116+
break
117+
}
118+
}
119+
return err
120+
}
121+
122+
func (d *FilesystemDriver) ResumeWritePosition(subPath string) (uint64, error) {
123+
fullPath := d.subPath(subPath)
124+
125+
fileInfo, err := os.Stat(fullPath)
126+
if err != nil && !os.IsNotExist(err) {
127+
return 0, err
128+
} else if err != nil {
129+
return 0, storagedriver.PathNotFoundError{subPath}
130+
}
131+
return uint64(fileInfo.Size()), nil
132+
}
133+
134+
func (d *FilesystemDriver) List(prefix string) ([]string, error) {
135+
prefix = strings.TrimRight(prefix, "/")
136+
fullPath := d.subPath(prefix)
137+
138+
dir, err := os.Open(fullPath)
139+
if err != nil {
140+
return nil, err
141+
}
142+
143+
fileNames, err := dir.Readdirnames(0)
144+
if err != nil {
145+
return nil, err
146+
}
147+
148+
keys := make([]string, 0, len(fileNames))
149+
for _, fileName := range fileNames {
150+
keys = append(keys, path.Join(prefix, fileName))
151+
}
152+
153+
return keys, nil
154+
}
155+
156+
func (d *FilesystemDriver) Move(sourcePath string, destPath string) error {
157+
err := os.Rename(d.subPath(sourcePath), d.subPath(destPath))
158+
return err
159+
}
160+
161+
func (d *FilesystemDriver) Delete(subPath string) error {
162+
fullPath := d.subPath(subPath)
163+
164+
_, err := os.Stat(fullPath)
165+
if err != nil && !os.IsNotExist(err) {
166+
return err
167+
} else if err != nil {
168+
return storagedriver.PathNotFoundError{subPath}
169+
}
170+
171+
err = os.RemoveAll(fullPath)
172+
return err
173+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package filesystem
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
"github.com/docker/docker-registry/storagedriver"
8+
"github.com/docker/docker-registry/storagedriver/testsuites"
9+
. "gopkg.in/check.v1"
10+
)
11+
12+
// Hook up gocheck into the "go test" runner.
13+
func Test(t *testing.T) { TestingT(t) }
14+
15+
func init() {
16+
rootDirectory := "/tmp/driver"
17+
os.RemoveAll(rootDirectory)
18+
19+
filesystemDriverConstructor := func() (storagedriver.StorageDriver, error) {
20+
return NewDriver(rootDirectory), nil
21+
}
22+
testsuites.RegisterInProcessSuite(filesystemDriverConstructor)
23+
testsuites.RegisterIPCSuite("filesystem", map[string]string{"RootDirectory": rootDirectory})
24+
}

storagedriver/inmemory/inmemory.go

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package inmemory
2+
3+
import (
4+
"bytes"
5+
"fmt"
6+
"io"
7+
"io/ioutil"
8+
"regexp"
9+
"strings"
10+
"sync"
11+
12+
"github.com/docker/docker-registry/storagedriver"
13+
)
14+
15+
type InMemoryDriver struct {
16+
storage map[string][]byte
17+
mutex sync.RWMutex
18+
}
19+
20+
func NewDriver() *InMemoryDriver {
21+
return &InMemoryDriver{storage: make(map[string][]byte)}
22+
}
23+
24+
func (d *InMemoryDriver) GetContent(path string) ([]byte, error) {
25+
d.mutex.RLock()
26+
defer d.mutex.RUnlock()
27+
contents, ok := d.storage[path]
28+
if !ok {
29+
return nil, storagedriver.PathNotFoundError{path}
30+
}
31+
return contents, nil
32+
}
33+
34+
func (d *InMemoryDriver) PutContent(path string, contents []byte) error {
35+
d.mutex.Lock()
36+
defer d.mutex.Unlock()
37+
d.storage[path] = contents
38+
return nil
39+
}
40+
41+
func (d *InMemoryDriver) ReadStream(path string, offset uint64) (io.ReadCloser, error) {
42+
d.mutex.RLock()
43+
defer d.mutex.RUnlock()
44+
contents, err := d.GetContent(path)
45+
if err != nil {
46+
return nil, err
47+
} else if len(contents) < int(offset) {
48+
return nil, storagedriver.InvalidOffsetError{path, offset}
49+
}
50+
51+
src := contents[offset:]
52+
buf := make([]byte, len(src))
53+
copy(buf, src)
54+
return ioutil.NopCloser(bytes.NewReader(buf)), nil
55+
}
56+
57+
func (d *InMemoryDriver) WriteStream(path string, offset, size uint64, reader io.ReadCloser) error {
58+
defer reader.Close()
59+
d.mutex.RLock()
60+
defer d.mutex.RUnlock()
61+
62+
resumableOffset, err := d.ResumeWritePosition(path)
63+
if err != nil {
64+
return err
65+
}
66+
67+
if offset > resumableOffset {
68+
return storagedriver.InvalidOffsetError{path, offset}
69+
}
70+
71+
contents, err := ioutil.ReadAll(reader)
72+
if err != nil {
73+
return err
74+
}
75+
76+
if offset > 0 {
77+
contents = append(d.storage[path][0:offset], contents...)
78+
}
79+
80+
d.storage[path] = contents
81+
return nil
82+
}
83+
84+
func (d *InMemoryDriver) ResumeWritePosition(path string) (uint64, error) {
85+
d.mutex.RLock()
86+
defer d.mutex.RUnlock()
87+
contents, ok := d.storage[path]
88+
if !ok {
89+
return 0, nil
90+
}
91+
return uint64(len(contents)), nil
92+
}
93+
94+
func (d *InMemoryDriver) List(prefix string) ([]string, error) {
95+
subPathMatcher, err := regexp.Compile(fmt.Sprintf("^%s/[^/]+", prefix))
96+
if err != nil {
97+
return nil, err
98+
}
99+
100+
d.mutex.RLock()
101+
defer d.mutex.RUnlock()
102+
// we use map to collect uniq keys
103+
keySet := make(map[string]struct{})
104+
for k := range d.storage {
105+
if key := subPathMatcher.FindString(k); key != "" {
106+
keySet[key] = struct{}{}
107+
}
108+
}
109+
110+
keys := make([]string, 0, len(keySet))
111+
for k := range keySet {
112+
keys = append(keys, k)
113+
}
114+
return keys, nil
115+
}
116+
117+
func (d *InMemoryDriver) Move(sourcePath string, destPath string) error {
118+
d.mutex.Lock()
119+
defer d.mutex.Unlock()
120+
contents, ok := d.storage[sourcePath]
121+
if !ok {
122+
return storagedriver.PathNotFoundError{sourcePath}
123+
}
124+
d.storage[destPath] = contents
125+
delete(d.storage, sourcePath)
126+
return nil
127+
}
128+
129+
func (d *InMemoryDriver) Delete(path string) error {
130+
d.mutex.Lock()
131+
defer d.mutex.Unlock()
132+
subPaths := make([]string, 0)
133+
for k := range d.storage {
134+
if strings.HasPrefix(k, path) {
135+
subPaths = append(subPaths, k)
136+
}
137+
}
138+
139+
if len(subPaths) == 0 {
140+
return storagedriver.PathNotFoundError{path}
141+
}
142+
143+
for _, subPath := range subPaths {
144+
delete(d.storage, subPath)
145+
}
146+
return nil
147+
}

0 commit comments

Comments
 (0)