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

Commit ca0084f

Browse files
committed
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
1 parent ff81f3a commit ca0084f

File tree

15 files changed

+290
-79
lines changed

15 files changed

+290
-79
lines changed

main/storagedriver/filesystem/filesystem.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,19 +8,14 @@ import (
88
"github.com/docker/docker-registry/storagedriver/ipc"
99
)
1010

11+
// An out-of-process filesystem driver, intended to be run by ipc.NewDriverClient
1112
func main() {
1213
parametersBytes := []byte(os.Args[1])
13-
var parameters map[string]interface{}
14+
var parameters map[string]string
1415
err := json.Unmarshal(parametersBytes, &parameters)
1516
if err != nil {
1617
panic(err)
1718
}
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))
19+
20+
ipc.StorageDriverServer(filesystem.FromParameters(parameters))
2621
}

main/storagedriver/inmemory/inmemory.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import (
55
"github.com/docker/docker-registry/storagedriver/ipc"
66
)
77

8+
// An out-of-process inmemory driver, intended to be run by ipc.NewDriverClient
9+
// This exists primarily for example and testing purposes
810
func main() {
9-
ipc.Server(inmemory.NewDriver())
11+
ipc.StorageDriverServer(inmemory.New())
1012
}

main/storagedriver/s3/s3.go

Lines changed: 4 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,24 @@ package main
33
import (
44
"encoding/json"
55
"os"
6-
"strconv"
76

8-
"github.com/crowdmob/goamz/aws"
97
"github.com/docker/docker-registry/storagedriver/ipc"
108
"github.com/docker/docker-registry/storagedriver/s3"
119
)
1210

11+
// An out-of-process S3 driver, intended to be run by ipc.NewDriverClient
1312
func main() {
1413
parametersBytes := []byte(os.Args[1])
15-
var parameters map[string]interface{}
14+
var parameters map[string]string
1615
err := json.Unmarshal(parametersBytes, &parameters)
1716
if err != nil {
1817
panic(err)
1918
}
2019

21-
accessKey, ok := parameters["accessKey"].(string)
22-
if !ok || accessKey == "" {
23-
panic("No accessKey parameter")
24-
}
25-
26-
secretKey, ok := parameters["secretKey"].(string)
27-
if !ok || secretKey == "" {
28-
panic("No secretKey parameter")
29-
}
30-
31-
region, ok := parameters["region"].(string)
32-
if !ok || region == "" {
33-
panic("No region parameter")
34-
}
35-
36-
bucket, ok := parameters["bucket"].(string)
37-
if !ok || bucket == "" {
38-
panic("No bucket parameter")
39-
}
40-
41-
encrypt, ok := parameters["encrypt"].(string)
42-
if !ok {
43-
panic("No encrypt parameter")
44-
}
45-
46-
encryptBool, err := strconv.ParseBool(encrypt)
47-
if err != nil {
48-
panic(err)
49-
}
50-
51-
driver, err := s3.NewDriver(accessKey, secretKey, aws.GetRegion(region), encryptBool, bucket)
20+
driver, err := s3.FromParameters(parameters)
5221
if err != nil {
5322
panic(err)
5423
}
5524

56-
ipc.Server(driver)
25+
ipc.StorageDriverServer(driver)
5726
}

storagedriver/factory/factory.go

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
package factory
2+
3+
import (
4+
"fmt"
5+
6+
"github.com/docker/docker-registry/storagedriver"
7+
"github.com/docker/docker-registry/storagedriver/ipc"
8+
)
9+
10+
// Internal mapping between storage driver names and their respective factories
11+
var driverFactories = make(map[string]StorageDriverFactory)
12+
13+
// Factory interface for the storagedriver.StorageDriver interface
14+
// Storage drivers should call Register() with a factory to make the driver available by name
15+
type StorageDriverFactory interface {
16+
// Creates and returns a new storagedriver.StorageDriver with the given parameters
17+
// Parameters will vary by driver and may be ignored
18+
// Each parameter key must only consist of lowercase letters and numbers
19+
Create(parameters map[string]string) (storagedriver.StorageDriver, error)
20+
}
21+
22+
// Register makes a storage driver available by the provided name.
23+
// If Register is called twice with the same name or if driver factory is nil, it panics.
24+
func Register(name string, factory StorageDriverFactory) {
25+
if factory == nil {
26+
panic("Must not provide nil StorageDriverFactory")
27+
}
28+
_, registered := driverFactories[name]
29+
if registered {
30+
panic(fmt.Sprintf("StorageDriverFactory named %s already registered", name))
31+
}
32+
33+
driverFactories[name] = factory
34+
}
35+
36+
// Create a new storagedriver.StorageDriver with the given name and parameters
37+
// To run in-process, the StorageDriverFactory must first be registered with the given name
38+
// If no in-process drivers are found with the given name, this attempts to create an IPC driver
39+
// If no in-process or external drivers are found, an InvalidStorageDriverError is returned
40+
func Create(name string, parameters map[string]string) (storagedriver.StorageDriver, error) {
41+
driverFactory, ok := driverFactories[name]
42+
if !ok {
43+
// No registered StorageDriverFactory found, try ipc
44+
driverClient, err := ipc.NewDriverClient(name, parameters)
45+
if err != nil {
46+
return nil, InvalidStorageDriverError{name}
47+
}
48+
err = driverClient.Start()
49+
if err != nil {
50+
return nil, err
51+
}
52+
return driverClient, nil
53+
}
54+
return driverFactory.Create(parameters)
55+
}
56+
57+
// Error returned when attempting to construct an unregistered storage driver
58+
type InvalidStorageDriverError struct {
59+
Name string
60+
}
61+
62+
func (err InvalidStorageDriverError) Error() string {
63+
return fmt.Sprintf("StorageDriver not registered: %s", err.Name)
64+
}

storagedriver/filesystem/filesystem.go

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,20 +8,54 @@ import (
88
"strings"
99

1010
"github.com/docker/docker-registry/storagedriver"
11+
"github.com/docker/docker-registry/storagedriver/factory"
1112
)
1213

14+
const DriverName = "filesystem"
15+
const DefaultRootDirectory = "/tmp/registry/storage"
16+
17+
func init() {
18+
factory.Register(DriverName, &filesystemDriverFactory{})
19+
}
20+
21+
// Implements the factory.StorageDriverFactory interface
22+
type filesystemDriverFactory struct{}
23+
24+
func (factory *filesystemDriverFactory) Create(parameters map[string]string) (storagedriver.StorageDriver, error) {
25+
return FromParameters(parameters), nil
26+
}
27+
28+
// Storage Driver backed by a local filesystem
29+
// All provided paths will be subpaths of the RootDirectory
1330
type FilesystemDriver struct {
1431
rootDirectory string
1532
}
1633

17-
func NewDriver(rootDirectory string) *FilesystemDriver {
34+
// Constructs a new FilesystemDriver with a given parameters map
35+
// Optional Parameters:
36+
// - rootdirectory
37+
func FromParameters(parameters map[string]string) *FilesystemDriver {
38+
var rootDirectory = DefaultRootDirectory
39+
if parameters != nil {
40+
rootDir, ok := parameters["rootdirectory"]
41+
if ok {
42+
rootDirectory = rootDir
43+
}
44+
}
45+
return New(rootDirectory)
46+
}
47+
48+
// Constructs a new FilesystemDriver with a given rootDirectory
49+
func New(rootDirectory string) *FilesystemDriver {
1850
return &FilesystemDriver{rootDirectory}
1951
}
2052

2153
func (d *FilesystemDriver) subPath(subPath string) string {
2254
return path.Join(d.rootDirectory, subPath)
2355
}
2456

57+
// Implement the storagedriver.StorageDriver interface
58+
2559
func (d *FilesystemDriver) GetContent(path string) ([]byte, error) {
2660
contents, err := ioutil.ReadFile(d.subPath(path))
2761
if err != nil {

storagedriver/filesystem/filesystem_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ func init() {
1717
os.RemoveAll(rootDirectory)
1818

1919
filesystemDriverConstructor := func() (storagedriver.StorageDriver, error) {
20-
return NewDriver(rootDirectory), nil
20+
return New(rootDirectory), nil
2121
}
2222
testsuites.RegisterInProcessSuite(filesystemDriverConstructor, testsuites.NeverSkip)
23-
testsuites.RegisterIPCSuite("filesystem", map[string]string{"RootDirectory": rootDirectory}, testsuites.NeverSkip)
23+
testsuites.RegisterIPCSuite(DriverName, map[string]string{"rootdirectory": rootDirectory}, testsuites.NeverSkip)
2424
}

storagedriver/inmemory/inmemory.go

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,14 +10,31 @@ import (
1010
"sync"
1111

1212
"github.com/docker/docker-registry/storagedriver"
13+
"github.com/docker/docker-registry/storagedriver/factory"
1314
)
1415

16+
const DriverName = "inmemory"
17+
18+
func init() {
19+
factory.Register(DriverName, &inMemoryDriverFactory{})
20+
}
21+
22+
// Implements the factory.StorageDriverFactory interface
23+
type inMemoryDriverFactory struct{}
24+
25+
func (factory *inMemoryDriverFactory) Create(parameters map[string]string) (storagedriver.StorageDriver, error) {
26+
return New(), nil
27+
}
28+
29+
// InMemory Storage Driver backed by a map
30+
// Intended solely for example and testing purposes
1531
type InMemoryDriver struct {
1632
storage map[string][]byte
1733
mutex sync.RWMutex
1834
}
1935

20-
func NewDriver() *InMemoryDriver {
36+
// Constructs a new InMemoryDriver
37+
func New() *InMemoryDriver {
2138
return &InMemoryDriver{storage: make(map[string][]byte)}
2239
}
2340

storagedriver/inmemory/inmemory_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@ func Test(t *testing.T) { TestingT(t) }
1313

1414
func init() {
1515
inmemoryDriverConstructor := func() (storagedriver.StorageDriver, error) {
16-
return NewDriver(), nil
16+
return New(), nil
1717
}
1818
testsuites.RegisterInProcessSuite(inmemoryDriverConstructor, testsuites.NeverSkip)
19-
testsuites.RegisterIPCSuite("inmemory", nil, testsuites.NeverSkip)
19+
testsuites.RegisterIPCSuite(DriverName, nil, testsuites.NeverSkip)
2020
}

storagedriver/ipc/client.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,13 +15,21 @@ import (
1515
"github.com/docker/libchan/spdy"
1616
)
1717

18+
// Storage Driver implementation using a managed child process communicating over IPC
1819
type StorageDriverClient struct {
1920
subprocess *exec.Cmd
2021
socket *os.File
2122
transport *spdy.Transport
2223
sender libchan.Sender
2324
}
2425

26+
// Constructs a new out-of-process storage driver using the driver name and configuration parameters
27+
// Must call Start() on this driver client before remote method calls can be made
28+
//
29+
// Looks for drivers in the following locations in order:
30+
// - Storage drivers directory (to be determined, yet not implemented)
31+
// - $GOPATH/bin
32+
// - $PATH
2533
func NewDriverClient(name string, parameters map[string]string) (*StorageDriverClient, error) {
2634
paramsBytes, err := json.Marshal(parameters)
2735
if err != nil {
@@ -46,6 +54,7 @@ func NewDriverClient(name string, parameters map[string]string) (*StorageDriverC
4654
}, nil
4755
}
4856

57+
// Starts the designated child process storage driver and binds a socket to this process for IPC
4958
func (driver *StorageDriverClient) Start() error {
5059
fileDescriptors, err := syscall.Socketpair(syscall.AF_LOCAL, syscall.SOCK_STREAM, 0)
5160
if err != nil {
@@ -93,6 +102,8 @@ func (driver *StorageDriverClient) Start() error {
93102
return nil
94103
}
95104

105+
// Stops the child process storage driver
106+
// storagedriver.StorageDriver methods called after Stop() will fail
96107
func (driver *StorageDriverClient) Stop() error {
97108
closeSenderErr := driver.sender.Close()
98109
closeTransportErr := driver.transport.Close()
@@ -109,6 +120,8 @@ func (driver *StorageDriverClient) Stop() error {
109120
return killErr
110121
}
111122

123+
// Implement the storagedriver.StorageDriver interface over IPC
124+
112125
func (driver *StorageDriverClient) GetContent(path string) ([]byte, error) {
113126
receiver, remoteSender := libchan.Pipe()
114127

storagedriver/ipc/ipc.go

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,16 @@ import (
1010
"github.com/docker/libchan"
1111
)
1212

13+
// Defines a remote method call request
14+
// A return value struct is to be sent over the ResponseChannel
1315
type Request struct {
1416
Type string
1517
Parameters map[string]interface{}
1618
ResponseChannel libchan.Sender
1719
}
1820

21+
// A simple wrapper around an io.ReadCloser that implements the io.ReadWriteCloser interface
22+
// Writes are disallowed and will return an error if ever called
1923
type noWriteReadWriteCloser struct {
2024
io.ReadCloser
2125
}
@@ -24,6 +28,8 @@ func (r noWriteReadWriteCloser) Write(p []byte) (n int, err error) {
2428
return 0, errors.New("Write unsupported")
2529
}
2630

31+
// Wraps an io.Reader as an io.ReadWriteCloser with a nop Close and unsupported Write method
32+
// Has no effect when an io.ReadWriteCloser is passed in
2733
func WrapReader(reader io.Reader) io.ReadWriteCloser {
2834
if readWriteCloser, ok := reader.(io.ReadWriteCloser); ok {
2935
return readWriteCloser
@@ -39,6 +45,7 @@ type responseError struct {
3945
Message string
4046
}
4147

48+
// Wraps an error in a serializable struct containing the error's type and message
4249
func ResponseError(err error) *responseError {
4350
if err == nil {
4451
return nil
@@ -53,29 +60,37 @@ func (err *responseError) Error() string {
5360
return fmt.Sprintf("%s: %s", err.Type, err.Message)
5461
}
5562

63+
// IPC method call response object definitions
64+
65+
// Response for a ReadStream request
5666
type ReadStreamResponse struct {
5767
Reader io.ReadWriteCloser
5868
Error *responseError
5969
}
6070

71+
// Response for a WriteStream request
6172
type WriteStreamResponse struct {
6273
Error *responseError
6374
}
6475

76+
// Response for a ResumeWritePosition request
6577
type ResumeWritePositionResponse struct {
6678
Position uint64
6779
Error *responseError
6880
}
6981

82+
// Response for a List request
7083
type ListResponse struct {
7184
Keys []string
7285
Error *responseError
7386
}
7487

88+
// Response for a Move request
7589
type MoveResponse struct {
7690
Error *responseError
7791
}
7892

93+
// Response for a Delete request
7994
type DeleteResponse struct {
8095
Error *responseError
8196
}

0 commit comments

Comments
 (0)